From 8de8bfe83e90e29eb97da164b52b16da2a4a452a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Guido=20G=C3=BCnther?= <agx@sigxcpu.org>
Date: Fri, 9 Feb 2018 13:37:28 +0100
Subject: [PATCH] Add support to launch favorites

(based on gnome-shell/maynard)

We switch to a private shell protocol from here on.  This might get
folded back into weston's desktop shell protocol at a later point when
we see what else is needed.
---
 README.md                                     |   7 +
 data/meson.build                              |   9 +
 data/sm.puri.phosh.gschema.xml                |  13 ++
 meson.build                                   |   8 +
 ...sktop-shell.xml => phosh-mobile-shell.xml} |  48 +++--
 run.in                                        |   7 +
 src/favorites.c                               | 186 ++++++++++++++++++
 src/favorites.h                               |  39 ++++
 src/lockscreen.h                              |   3 -
 src/meson.build                               |  12 +-
 src/panel.c                                   |  35 ++--
 src/phosh.c                                   | 132 +++++++++----
 src/style.css                                 |   4 +
 src/ui/top-panel.ui                           |  27 ++-
 14 files changed, 435 insertions(+), 95 deletions(-)
 create mode 100644 data/meson.build
 create mode 100644 data/sm.puri.phosh.gschema.xml
 rename protocol/{weston-desktop-shell.xml => phosh-mobile-shell.xml} (79%)
 create mode 100755 run.in
 create mode 100644 src/favorites.c
 create mode 100644 src/favorites.h

diff --git a/README.md b/README.md
index b206157..5b9b5ef 100644
--- a/README.md
+++ b/README.md
@@ -19,3 +19,10 @@ way to get going is to do the following:
 	ninja -C _build
 	ninja -C _build install
 
+
+## Running
+When running from the source tree start *rootston*. Then start *phosh*
+using:
+
+    _build/run
+
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 0000000..98c5857
--- /dev/null
+++ b/data/meson.build
@@ -0,0 +1,9 @@
+schemas = ['sm.puri.phosh.gschema.xml']
+compiled = gnome.compile_schemas(
+  build_by_default: true
+)
+
+install_data(
+  schemas,
+  install_dir: 'share/glib-2.0/schemas'
+)
diff --git a/data/sm.puri.phosh.gschema.xml b/data/sm.puri.phosh.gschema.xml
new file mode 100644
index 0000000..9c1bb93
--- /dev/null
+++ b/data/sm.puri.phosh.gschema.xml
@@ -0,0 +1,13 @@
+<schemalist>
+  <schema id="sm.puri.phosh"
+          path="/sm/puri/phosh/">
+    <key name="favorites" type="as">
+      <default>[ 'evince.desktop', 'gnome-chess.desktop' ]</default>
+      <summary>List of desktop file IDs for favorite applications</summary>
+      <description>
+        The applications corresponding to these identifiers will be
+        displayed in the favorites panel along with running applications.
+      </description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/meson.build b/meson.build
index 197fd67..e9414a6 100644
--- a/meson.build
+++ b/meson.build
@@ -59,7 +59,15 @@ add_project_arguments(
   language: 'c'
 )
 
+run_data = configuration_data()
+run_data.set('ABS_BUILDDIR', meson.current_build_dir())
+configure_file(
+  input: 'run.in',
+  output: 'run',
+  configuration: run_data)
+
 gnome = import('gnome')
 
 subdir('src')
+subdir('data')
 
diff --git a/protocol/weston-desktop-shell.xml b/protocol/phosh-mobile-shell.xml
similarity index 79%
rename from protocol/weston-desktop-shell.xml
rename to protocol/phosh-mobile-shell.xml
index 91c5eb4..259d2f7 100644
--- a/protocol/weston-desktop-shell.xml
+++ b/protocol/phosh-mobile-shell.xml
@@ -1,10 +1,8 @@
-<protocol name="weston_desktop">
-
-  <interface name="weston_desktop_shell" version="1">
-    <description summary="create desktop widgets and helpers">
-      Traditional user interfaces can rely on this interface to define the
-      foundations of typical desktops. Currently it's possible to set up
-      background, panels and locking surfaces.
+<protocol name="phosh_desktop">
+  <interface name="phosh_mobile_shell" version="1">
+    <description summary="panel menus and helpers">
+      Private protocol between phosh and the compositor. Similar to weston's desktop-shell
+      protocol.
     </description>
 
     <request name="set_background">
@@ -114,22 +112,32 @@
       <arg name="position" type="uint"/>
     </request>
 
-  </interface>
-
-  <interface name="weston_screensaver" version="1">
-    <description summary="interface for implementing screensavers">
-      Only one client can bind this interface at a time.
-    </description>
-
-    <request name="set_surface">
-      <description summary="set the surface type as a screensaver">
-	A screensaver surface is normally hidden, and only visible after an
-        idle timeout.
+    <!-- On top of weston's protocol -->
+    <request name="set_panel_menu">
+      <description summary="set panel menu">
+	Add a menu to the panel. Multiple menus can be added.
       </description>
+      <arg name="menu" type="object" interface="wl_surface"/>
+    </request>
 
-      <arg name="surface" type="object" interface="wl_surface"/>
-      <arg name="output" type="object" interface="wl_output"/>
+    <request name="show_panel_menu">
+      <arg name="menu" type="object" interface="wl_surface"/>
     </request>
 
+    <request name="hide_panel_menu">
+      <arg name="menu" type="object" interface="wl_surface"/>
+    </request>
   </interface>
+  
+  <!--
+    <enum name="menu_position">
+      <!- only supported for top panels ->
+      <entry name="left" value="0"/>
+      <entry name="right" value="1"/>
+    </enum>
+
+    <request name="set_menu_position">
+      <arg name="position" type="uint"/>
+    </request>
+  -->
 </protocol>
diff --git a/run.in b/run.in
new file mode 100755
index 0000000..6f7f2f0
--- /dev/null
+++ b/run.in
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+ABS_BUILDDIR='@ABS_BUILDDIR@'
+
+export GSETTINGS_SCHEMA_DIR="${ABS_BUILDDIR}/data"
+
+exec "${ABS_BUILDDIR}/src/phosh" $@
diff --git a/src/favorites.c b/src/favorites.c
new file mode 100644
index 0000000..30c881b
--- /dev/null
+++ b/src/favorites.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ * SPDX-License-Identifier: GPL-3.0+
+ * Author: Guido Günther <agx@sigxcpu.org>
+ *
+ * Based on maynard whish is
+ * Copyright (C) 2013 Collabora Ltd.
+ * Author: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
+
+ */
+
+#include "config.h"
+
+#include "favorites.h"
+
+#include <gio/gdesktopappinfo.h>
+
+enum {
+  APP_LAUNCHED,
+  N_SIGNALS
+};
+static guint signals[N_SIGNALS] = { 0 };
+
+struct PhoshFavoritesPrivate {
+  GtkWidget *grid;
+  GSettings *settings;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(PhoshFavorites, phosh_favorites, GTK_TYPE_WINDOW)
+
+
+static void
+term_btn_clicked (PhoshFavorites *self, GtkButton *btn)
+{
+  GError *error = NULL;
+  g_spawn_command_line_async ("weston-terminal", &error);
+  if (error)
+    g_warning ("Could not launch terminal");
+
+  g_signal_emit(self, signals[APP_LAUNCHED], 0);
+}
+
+
+static void
+favorite_clicked_cb (GtkWidget *widget,
+    GDesktopAppInfo *info)
+{
+  PhoshFavorites *self;
+
+  g_app_info_launch (G_APP_INFO (info), NULL, NULL, NULL);
+
+  self = g_object_get_data (G_OBJECT (widget), "favorites");
+  g_assert (self);
+  g_signal_emit (self, signals[APP_LAUNCHED], 0);
+}
+
+
+static GtkWidget*
+add_favorite (PhoshFavorites *self,
+	      const gchar *favorite)
+{
+  GIcon *icon;
+  GDesktopAppInfo *info;
+  GtkWidget *image;
+  GtkWidget *btn;
+
+  info = g_desktop_app_info_new (favorite);
+
+  icon = g_app_info_get_icon (G_APP_INFO (info));
+  image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
+  btn = gtk_button_new ();
+  gtk_style_context_remove_class (gtk_widget_get_style_context (btn),
+				  "button");
+  gtk_style_context_remove_class (gtk_widget_get_style_context (btn),
+				  "image-button");
+  gtk_style_context_add_class (gtk_widget_get_style_context (btn),
+			       "phosh-favorite");
+
+  gtk_button_set_image (GTK_BUTTON (btn), image);
+  g_object_set (image, "margin", 20, NULL);
+
+  g_object_set_data (G_OBJECT (btn), "favorites", self);
+  g_signal_connect (btn, "clicked", G_CALLBACK (favorite_clicked_cb), info);
+
+  return btn;
+}
+
+
+static void
+favorites_changed (GSettings *settings,
+		   const gchar *key,
+		   PhoshFavorites *self)
+{
+  PhoshFavoritesPrivate *priv = phosh_favorites_get_instance_private (self);
+  GtkWidget *btn;
+  gchar **favorites = g_settings_get_strv (settings, key);
+  guint top = 1;
+
+  /* Remove all favorites first */
+  gtk_container_foreach (GTK_CONTAINER (priv->grid),
+			 (GtkCallback) gtk_widget_destroy, NULL);
+
+  /* Add weston terminal as band aid in case all else fails for now */
+  btn = gtk_button_new_from_icon_name ("terminal", GTK_ICON_SIZE_DIALOG);
+  gtk_grid_attach (GTK_GRID (priv->grid), btn, 1, 1, 1, 1);
+  g_signal_connect_swapped (btn, "clicked", G_CALLBACK (term_btn_clicked), self);
+  top++;
+
+  for (gint i = 0; i < g_strv_length (favorites); i++) {
+    gchar *fav = favorites[i];
+    btn = add_favorite (self, fav);
+    gtk_grid_attach (GTK_GRID (self->priv->grid), btn, 1, top++, 1, 1);
+  }
+  g_strfreev (favorites);
+}
+
+
+static void
+phosh_favorites_constructed (GObject *object)
+{
+  PhoshFavorites *self = PHOSH_FAVORITES (object);
+  PhoshFavoritesPrivate *priv = phosh_favorites_get_instance_private (self);
+
+  G_OBJECT_CLASS (phosh_favorites_parent_class)->constructed (object);
+
+  /* window properties */
+  gtk_window_set_title (GTK_WINDOW (self), "phosh favorites");
+  gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
+  gtk_window_resize (GTK_WINDOW (self), 100, 100);
+  gtk_widget_realize(GTK_WIDGET (self));
+
+  gtk_style_context_add_class (
+      gtk_widget_get_style_context (GTK_WIDGET (self)),
+      "phosh-favorites");
+
+  priv->grid = gtk_widget_new (GTK_TYPE_GRID, "halign",
+			       GTK_ALIGN_CENTER, "valign",
+			       GTK_ALIGN_CENTER, NULL);
+  gtk_container_add (GTK_CONTAINER (self), priv->grid);
+
+  self->priv->settings = g_settings_new ("sm.puri.phosh");
+  g_signal_connect (self->priv->settings, "changed::favorites",
+		    G_CALLBACK (favorites_changed), self);
+  favorites_changed (self->priv->settings, "favorites", self);
+}
+
+
+static void
+phosh_favorites_dispose (GObject *object)
+{
+  PhoshFavorites *self = PHOSH_FAVORITES (object);
+
+  g_clear_object (&self->priv->settings);
+
+  G_OBJECT_CLASS (phosh_favorites_parent_class)->dispose (object);
+}
+
+
+static void
+phosh_favorites_class_init (PhoshFavoritesClass *klass)
+{
+  GObjectClass *object_class = (GObjectClass *)klass;
+
+  object_class->dispose = phosh_favorites_dispose;
+  object_class->constructed = phosh_favorites_constructed;
+
+  signals[APP_LAUNCHED] = g_signal_new ("app-launched",
+      G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+      NULL, G_TYPE_NONE, 0);
+}
+
+
+static void
+phosh_favorites_init (PhoshFavorites *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      PHOSH_FAVORITES_TYPE,
+      PhoshFavoritesPrivate);
+}
+
+
+GtkWidget *
+phosh_favorites_new (void)
+{
+  return g_object_new (PHOSH_FAVORITES_TYPE, NULL);
+}
diff --git a/src/favorites.h b/src/favorites.h
new file mode 100644
index 0000000..8584aeb
--- /dev/null
+++ b/src/favorites.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ *
+ * SPDX-License-Identifier: GPL-3.0+
+ */
+
+#ifndef __PHOSH_FAVORITES_H__
+#define __PHOSH_FAVORITES_H__
+
+#include <gtk/gtk.h>
+
+#define PHOSH_FAVORITES_TYPE                 (phosh_favorites_get_type ())
+#define PHOSH_FAVORITES(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), PHOSH_FAVORITES_TYPE, PhoshFavorites))
+#define PHOSH_FAVORITES_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), PHOSH_FAVORITES_TYPE, PhoshFavoritesClass))
+#define PHOSH_IS_FAVORITES(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PHOSH_FAVORITES_TYPE))
+#define PHOSH_IS_FAVORITES_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), PHOSH_FAVORITES_TYPE))
+#define PHOSH_FAVORITES_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), PHOSH_FAVORITES_TYPE, PhoshFavoritesClass))
+
+typedef struct PhoshFavorites PhoshFavorites;
+typedef struct PhoshFavoritesClass PhoshFavoritesClass;
+typedef struct PhoshFavoritesPrivate PhoshFavoritesPrivate;
+
+struct PhoshFavorites
+{
+  GtkWindow parent;
+
+  PhoshFavoritesPrivate *priv;
+};
+
+struct PhoshFavoritesClass
+{
+  GtkWindowClass parent_class;
+};
+
+GType phosh_favorites_get_type (void) G_GNUC_CONST;
+
+GtkWidget * phosh_favorites_new (void);
+
+#endif /* __PHOSH_FAVORITES_H__ */
diff --git a/src/lockscreen.h b/src/lockscreen.h
index 7eef980..16413ec 100644
--- a/src/lockscreen.h
+++ b/src/lockscreen.h
@@ -32,9 +32,6 @@ struct PhoshLockscreenClass
   GtkWindowClass parent_class;
 };
 
-#define PHOSH_LOCKSCREEN_WIDTH 56
-#define PHOSH_LOCKSCREEN_HEIGHT_RATIO 0.73
-
 GType phosh_lockscreen_get_type (void) G_GNUC_CONST;
 
 GtkWidget * phosh_lockscreen_new (void);
diff --git a/src/meson.build b/src/meson.build
index 9c7d6df..9d7a49d 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -14,15 +14,17 @@ phosh_resources = gnome.compile_resources(
 )
 
 
-shell_proto_header = gen_scanner_client_header.process('../protocol/weston-desktop-shell.xml')
-shell_proto_code = gen_scanner_client_code.process('../protocol/weston-desktop-shell.xml')
+shell_proto_header = gen_scanner_client_header.process('../protocol/phosh-mobile-shell.xml')
+shell_proto_code = gen_scanner_client_code.process('../protocol/phosh-mobile-shell.xml')
 
 phosh_sources = [
-  'phosh.c',
-  'panel.c',
-  'panel.h',
+  'favorites.c',
+  'favorites.h',
   'lockscreen.c',
   'lockscreen.h',
+  'panel.c',
+  'panel.h',
+  'phosh.c',
   shell_proto_header,
   shell_proto_code,
   phosh_resources,
diff --git a/src/panel.c b/src/panel.c
index fe79baf..9bc4a9d 100644
--- a/src/panel.c
+++ b/src/panel.c
@@ -18,15 +18,14 @@
 #define TOPLEFT_LABEL_TEXT "Librem5 dev board"
 
 enum {
-  FAVORITE_LAUNCHED,
+  FAVORITES_ACTIVATED,
   N_SIGNALS
 };
 static guint signals[N_SIGNALS] = { 0 };
 
 struct PhoshPanelPrivate {
-  GtkWidget *label_topleft;
+  GtkWidget *btn_topleft;
   GtkWidget *label_clock;
-  GtkWidget *btn_terminal;
 
   GnomeWallClock *wall_clock;
 };
@@ -34,19 +33,13 @@ struct PhoshPanelPrivate {
 G_DEFINE_TYPE_WITH_PRIVATE (PhoshPanel, phosh_panel, GTK_TYPE_WINDOW)
 
 
-
 /* FIXME: Temporarily add a term button until we have the menu system */
 static void
-term_clicked_cb (PhoshPanel *self, GtkButton *btn)
+topleft_clicked_cb (PhoshPanel *self, GtkButton *btn)
 {
-  GError *error = NULL;
-
   g_return_if_fail (PHOSH_IS_PANEL (self));
   g_return_if_fail (GTK_IS_BUTTON (btn));
-
-  g_spawn_command_line_async ("weston-terminal", &error);
-  if (error)
-    g_warning ("Could not launch terminal");
+  g_signal_emit(self, signals[FAVORITES_ACTIVATED], 0);
 }
 
 
@@ -76,16 +69,16 @@ phosh_panel_constructed (GObject *object)
 
   G_OBJECT_CLASS (phosh_panel_parent_class)->constructed (object);
 
-  gtk_label_set_text (GTK_LABEL (priv->label_topleft), TOPLEFT_LABEL_TEXT);
+  gtk_button_set_label (GTK_BUTTON (priv->btn_topleft), TOPLEFT_LABEL_TEXT);
   priv->wall_clock = g_object_new (GNOME_TYPE_WALL_CLOCK, NULL);
   g_signal_connect (priv->wall_clock,
 		    "notify::clock",
 		    G_CALLBACK (wall_clock_notify_cb),
 		    self);
 
-  g_signal_connect_object (priv->btn_terminal,
+  g_signal_connect_object (priv->btn_topleft,
                            "clicked",
-                           G_CALLBACK (term_clicked_cb),
+                           G_CALLBACK (topleft_clicked_cb),
                            self,
                            G_CONNECT_SWAPPED);
 
@@ -98,6 +91,14 @@ phosh_panel_constructed (GObject *object)
       gtk_widget_get_style_context (GTK_WIDGET (self)),
       "phosh-panel");
 
+  /* Button properites */
+  gtk_style_context_remove_class (gtk_widget_get_style_context (priv->btn_topleft),
+				  "button");
+  gtk_style_context_remove_class (gtk_widget_get_style_context (priv->btn_topleft),
+				  "image-button");
+  gtk_style_context_add_class (gtk_widget_get_style_context (priv->btn_topleft),
+			       "phosh-panel-btn-top-left");
+
   wall_clock_notify_cb (priv->wall_clock, NULL, self);
 }
 
@@ -114,7 +115,6 @@ phosh_panel_dispose (GObject *object)
 }
 
 
-
 static void
 phosh_panel_class_init (PhoshPanelClass *klass)
 {
@@ -124,15 +124,14 @@ phosh_panel_class_init (PhoshPanelClass *klass)
   object_class->constructed = phosh_panel_constructed;
   object_class->dispose = phosh_panel_dispose;
 
-  signals[FAVORITE_LAUNCHED] = g_signal_new ("favorite-launched",
+  signals[FAVORITES_ACTIVATED] = g_signal_new ("favorites-activated",
       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL,
       NULL, G_TYPE_NONE, 0);
 
   gtk_widget_class_set_template_from_resource (widget_class,
 					       "/sm/puri/phosh/ui/top-panel.ui");
-  gtk_widget_class_bind_template_child_private (widget_class, PhoshPanel, label_topleft);
+  gtk_widget_class_bind_template_child_private (widget_class, PhoshPanel, btn_topleft);
   gtk_widget_class_bind_template_child_private (widget_class, PhoshPanel, label_clock);
-  gtk_widget_class_bind_template_child_private (widget_class, PhoshPanel, btn_terminal);
 }
 
 
diff --git a/src/phosh.c b/src/phosh.c
index a1f7be5..26c71d1 100644
--- a/src/phosh.c
+++ b/src/phosh.c
@@ -14,10 +14,11 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkwayland.h>
 
-#include "weston-desktop-shell-client-protocol.h"
+#include "phosh-mobile-shell-client-protocol.h"
 
-#include "panel.h"
+#include "favorites.h"
 #include "lockscreen.h"
+#include "panel.h"
 
 struct elem {
   GtkWidget *window;
@@ -28,7 +29,7 @@ struct elem {
 struct desktop {
   struct wl_display *display;
   struct wl_registry *registry;
-  struct weston_desktop_shell *wshell;
+  struct phosh_mobile_shell *mshell;
   struct wl_output *output;
 
   struct wl_seat *seat;
@@ -36,23 +37,56 @@ struct desktop {
 
   GdkDisplay *gdk_display;
 
+  /* Top panel */
   struct elem *panel;
+
+  /* Background */
   struct elem *background;
+
+  /* Lockscreen */
   struct elem *lockscreen;
   gulong unlock_handler_id;
+
+  /* Favorites menu */
+  struct elem *favorites;
+  gboolean favorites_shown;
 };
 
 
 static void
 lockscreen_unlock_cb (struct desktop *desktop, PhoshLockscreen *window)
 {
-  weston_desktop_shell_unlock (desktop->wshell);
+  phosh_mobile_shell_unlock (desktop->mshell);
 
   g_signal_handler_disconnect (window, desktop->unlock_handler_id);
   gtk_widget_destroy (GTK_WIDGET (window));
   g_free (desktop->lockscreen);
   desktop->lockscreen = NULL;
-  weston_desktop_shell_unlock(desktop->wshell);
+  phosh_mobile_shell_unlock(desktop->mshell);
+}
+
+
+static void
+favorites_activated_cb (struct desktop *desktop, PhoshLockscreen *window)
+{
+  if (desktop->favorites_shown) {
+    phosh_mobile_shell_hide_panel_menu(desktop->mshell,
+					 desktop->favorites->surface);
+
+  } else {
+    phosh_mobile_shell_show_panel_menu(desktop->mshell,
+					 desktop->favorites->surface);
+  }
+  desktop->favorites_shown = !desktop->favorites_shown;
+}
+
+
+static void
+app_launched_cb (struct desktop *desktop, PhoshFavorites *favorites)
+{
+  phosh_mobile_shell_hide_panel_menu(desktop->mshell,
+				       desktop->favorites->surface);
+  desktop->favorites_shown = FALSE;
 }
 
 
@@ -70,7 +104,7 @@ lockscreen_create (struct desktop *desktop)
 
   lockscreen->surface = gdk_wayland_window_get_wl_surface (gdk_window);
 
-  weston_desktop_shell_set_lock_surface(desktop->wshell,
+  phosh_mobile_shell_set_lock_surface(desktop->mshell,
 					lockscreen->surface);
   gtk_widget_show_all (lockscreen->window);
   desktop->lockscreen = lockscreen;
@@ -83,6 +117,32 @@ lockscreen_create (struct desktop *desktop)
 }
 
 
+static void
+favorites_create(struct desktop *desktop)
+{
+  struct elem *favorites;
+  GdkWindow *gdk_window;
+
+  favorites = calloc (1, sizeof *favorites);
+  favorites->window = phosh_favorites_new ();
+
+  gdk_window = gtk_widget_get_window (favorites->window);
+  gdk_wayland_window_set_use_custom_surface (gdk_window);
+  favorites->surface = gdk_wayland_window_get_wl_surface (gdk_window);
+
+  phosh_mobile_shell_set_panel_menu (desktop->mshell,
+				       favorites->surface);
+  gtk_widget_show_all (favorites->window);
+  desktop->favorites = favorites;
+  desktop->favorites_shown = FALSE;
+
+  g_signal_connect_swapped (favorites->window,
+			    "app-launched",
+			    G_CALLBACK(app_launched_cb),
+			    desktop);
+}
+
+
 static void
 panel_create (struct desktop *desktop)
 {
@@ -95,17 +155,22 @@ panel_create (struct desktop *desktop)
   /* set it up as the panel */
   gdk_window = gtk_widget_get_window (panel->window);
   gdk_wayland_window_set_use_custom_surface (gdk_window);
-
   panel->surface = gdk_wayland_window_get_wl_surface (gdk_window);
 
-  weston_desktop_shell_set_user_data (desktop->wshell, desktop);
-  weston_desktop_shell_set_panel (desktop->wshell, desktop->output,
+  phosh_mobile_shell_set_user_data (desktop->mshell, desktop);
+  phosh_mobile_shell_set_panel (desktop->mshell, desktop->output,
       panel->surface);
-  weston_desktop_shell_set_panel_position (desktop->wshell,
-     WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP);
+  phosh_mobile_shell_set_panel_position (desktop->mshell,
+     PHOSH_MOBILE_SHELL_PANEL_POSITION_TOP);
 
   gtk_widget_show_all (panel->window);
   desktop->panel = panel;
+
+  g_signal_connect_swapped (
+    panel->window,
+    "favorites-activated",
+    G_CALLBACK(favorites_activated_cb),
+    desktop);
 }
 
 
@@ -191,8 +256,8 @@ background_create (struct desktop *desktop)
 
   background->surface = gdk_wayland_window_get_wl_surface (gdk_window);
 
-  weston_desktop_shell_set_user_data (desktop->wshell, desktop);
-  weston_desktop_shell_set_background (desktop->wshell, desktop->output,
+  phosh_mobile_shell_set_user_data (desktop->mshell, desktop);
+  phosh_mobile_shell_set_background (desktop->mshell, desktop->output,
       background->surface);
 
   desktop->background = background;
@@ -234,13 +299,13 @@ shell_configure (struct desktop *desktop,
   gtk_window_resize (GTK_WINDOW (desktop->panel->window),
       width, PHOSH_PANEL_HEIGHT);
 
-  weston_desktop_shell_desktop_ready (desktop->wshell);
+  phosh_mobile_shell_desktop_ready (desktop->mshell);
 }
 
 
 static void
-weston_desktop_shell_configure (void *data,
-    struct weston_desktop_shell *weston_desktop_shell,
+phosh_mobile_shell_configure (void *data,
+    struct phosh_mobile_shell *phosh_mobile_shell,
     uint32_t edges,
     struct wl_surface *surface,
     int32_t width, int32_t height)
@@ -250,8 +315,8 @@ weston_desktop_shell_configure (void *data,
 
 
 static void
-weston_desktop_shell_prepare_lock_surface (void *data,
-    struct weston_desktop_shell *weston_desktop_shell)
+phosh_mobile_shell_prepare_lock_surface (void *data,
+    struct phosh_mobile_shell *phosh_mobile_shell)
 {
   struct desktop *desktop = data;
 
@@ -261,18 +326,18 @@ weston_desktop_shell_prepare_lock_surface (void *data,
 
 
 static void
-weston_desktop_shell_grab_cursor (void *data,
-    struct weston_desktop_shell *weston_desktop_shell,
+phosh_mobile_shell_grab_cursor (void *data,
+    struct phosh_mobile_shell *phosh_mobile_shell,
     uint32_t cursor)
 {
   g_warning("%s not implmented", __func__);
 }
 
 
-static const struct weston_desktop_shell_listener wshell_listener = {
-  weston_desktop_shell_configure,
-  weston_desktop_shell_prepare_lock_surface,
-  weston_desktop_shell_grab_cursor
+static const struct phosh_mobile_shell_listener mshell_listener = {
+  phosh_mobile_shell_configure,
+  phosh_mobile_shell_prepare_lock_surface,
+  phosh_mobile_shell_grab_cursor
 };
 
 
@@ -285,11 +350,11 @@ registry_handle_global (void *data,
 {
   struct desktop *d = data;
 
-  if (!strcmp (interface, "weston_desktop_shell")) {
-      d->wshell = wl_registry_bind (registry, name,
-          &weston_desktop_shell_interface, MIN(version, 1));
-      weston_desktop_shell_add_listener (d->wshell, &wshell_listener, d);
-      weston_desktop_shell_set_user_data (d->wshell, d);
+  if (!strcmp (interface, "phosh_mobile_shell")) {
+      d->mshell = wl_registry_bind (registry, name,
+          &phosh_mobile_shell_interface, MIN(version, 1));
+      phosh_mobile_shell_add_listener (d->mshell, &mshell_listener, d);
+      phosh_mobile_shell_set_user_data (d->mshell, d);
     }
   else if (!strcmp (interface, "wl_output")) {
       /* TODO: create multiple outputs */
@@ -337,18 +402,19 @@ int main(int argc, char *argv[])
 
   /* Wait until we have been notified about the compositor,
    * shell, and shell helper objects */
-  if (!desktop->output || !desktop->wshell)
+  if (!desktop->output || !desktop->mshell)
     wl_display_roundtrip (desktop->display);
-  if (!desktop->output || !desktop->wshell) {
+  if (!desktop->output || !desktop->mshell) {
       g_error ("Could not find output, shell or helper modules\n"
-               "output: %p, wshell: %p\n",
-		 desktop->output, desktop->wshell);
+               "output: %p, mshell: %p\n",
+		 desktop->output, desktop->mshell);
       return -1;
   }
 
   css_setup (desktop);
   background_create (desktop);
   panel_create (desktop);
+  favorites_create (desktop);
 
   gtk_main ();
 
diff --git a/src/style.css b/src/style.css
index e69de29..0190459 100644
--- a/src/style.css
+++ b/src/style.css
@@ -0,0 +1,4 @@
+.phosh-panel-btn-top-left {
+    background-image: none;
+    border-width: 0;
+}
diff --git a/src/ui/top-panel.ui b/src/ui/top-panel.ui
index 92802c0..f4c470e 100644
--- a/src/ui/top-panel.ui
+++ b/src/ui/top-panel.ui
@@ -2,7 +2,7 @@
 <!-- Generated with glade 3.20.2 -->
 <interface>
   <requires lib="gtk+" version="3.20"/>
-  <template class="PhoshPanel">
+  <template class="PhoshPanel" parent="GtkWindow">
     <property name="can_focus">False</property>
     <child>
       <object class="GtkBox" id="top_panel_box">
@@ -10,11 +10,14 @@
         <property name="can_focus">False</property>
         <property name="homogeneous">True</property>
         <child>
-          <object class="GtkLabel" id="label_topleft">
+          <object class="GtkButton" id="btn_topleft">
+            <property name="label"></property>
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="label" translatable="yes"></property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
             <property name="xalign">0.1</property>
+            <property name="yalign">0.5</property>
+            <property name="always_show_image">True</property>
           </object>
           <packing>
             <property name="expand">False</property>
@@ -23,18 +26,7 @@
           </packing>
         </child>
         <child>
-          <object class="GtkButton" id="btn_terminal">
-            <property name="label" translatable="yes">Terminal</property>
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <property name="always_show_image">True</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">1</property>
-          </packing>
+          <placeholder/>
         </child>
         <child>
           <object class="GtkBox">
@@ -68,5 +60,8 @@
         </child>
       </object>
     </child>
+    <child type="titlebar">
+      <placeholder/>
+    </child>
   </template>
 </interface>
-- 
GitLab