[gnome-shell] [ShellTrayIcon] add ShellTrayIcon, make ShellTrayManager use it



commit ae9360659db29eb2850ccd2db8f1ec69cdfa29ce
Author: Dan Winship <danw gnome org>
Date:   Tue Sep 7 22:27:08 2010 -0400

    [ShellTrayIcon] add ShellTrayIcon, make ShellTrayManager use it
    
    The actor emitted by ShellTrayManager is now ShellTrayIcon, a subclass
    of ShellGtkEmbed which has several properties on it which are (or will
    soon be) useful to the shell.
    
    Part of the rearranging to use ShellTrayIcon means that we now show
    the ShellEmbeddedWindow before creating its ShellGtkEmbed, which
    requires a few modifications to ShellEmbeddedWindow (notably, telling
    it at construct time what stage it will be drawn on, since it needs to
    know that before it has a ShellGtkEmbed now).
    
    https://bugzilla.gnome.org/show_bug.cgi?id=608869

 js/ui/panel.js              |    4 +-
 src/Makefile.am             |    2 +
 src/shell-embedded-window.c |   60 +++++++++++-----
 src/shell-embedded-window.h |    3 +-
 src/shell-tray-icon.c       |  168 +++++++++++++++++++++++++++++++++++++++++++
 src/shell-tray-icon.h       |   34 +++++++++
 src/shell-tray-manager.c    |   42 +++---------
 7 files changed, 261 insertions(+), 52 deletions(-)
---
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 1abcad0..4c3206c 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -885,7 +885,9 @@ Panel.prototype = {
                          });
     },
 
-    _onTrayIconAdded: function(o, icon, wmClass) {
+    _onTrayIconAdded: function(o, icon) {
+        let wmClass = icon.wm_class.toLowerCase();
+
         icon.height = PANEL_ICON_SIZE;
 
         let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
diff --git a/src/Makefile.am b/src/Makefile.am
index c47d834..db97ef2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -74,6 +74,7 @@ shell_public_headers_h =		\
 	shell-perf-log.h		\
 	shell-slicer.h			\
 	shell-stack.h			\
+	shell-tray-icon.h		\
 	shell-tray-manager.h		\
 	shell-uri-util.h		\
 	shell-window-tracker.h		\
@@ -104,6 +105,7 @@ libgnome_shell_la_SOURCES =		\
 	shell-perf-log.c		\
 	shell-slicer.c			\
 	shell-stack.c			\
+	shell-tray-icon.c		\
 	shell-tray-manager.c		\
 	shell-uri-util.c		\
 	shell-window-tracker.c		\
diff --git a/src/shell-embedded-window.c b/src/shell-embedded-window.c
index 8da3f14..a3b9153 100644
--- a/src/shell-embedded-window.c
+++ b/src/shell-embedded-window.c
@@ -38,10 +38,17 @@
 
 G_DEFINE_TYPE (ShellEmbeddedWindow, shell_embedded_window, GTK_TYPE_WINDOW);
 
+enum {
+   PROP_0,
+
+   PROP_STAGE
+};
+
 struct _ShellEmbeddedWindowPrivate {
   ShellGtkEmbed *actor;
 
   GdkRectangle position;
+  Window stage_xwindow;
 };
 
 /*
@@ -87,22 +94,9 @@ static void
 shell_embedded_window_realize (GtkWidget *widget)
 {
   ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
-  ClutterActor *stage;
-  Window stage_xwindow;
 
   GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->realize (widget);
 
-  stage = clutter_actor_get_stage (CLUTTER_ACTOR (window->priv->actor));
-
-  /* Clutter is buggy and will realize an actor when it has a parent
-   * but no grandparent; this is a workaround until
-   * http://bugzilla.openedhand.com/show_bug.cgi?id=1138 is fixed - we
-   * only have one stage in the shell in any case.
-   */
-  if (!stage)
-    stage = clutter_stage_get_default ();
-
-  stage_xwindow = clutter_x11_get_stage_window (CLUTTER_STAGE (stage));
 
   /* Using XReparentWindow() is simpler than using gdk_window_reparent(),
    * since it avoids maybe having to create a new foreign GDK window for
@@ -113,7 +107,7 @@ shell_embedded_window_realize (GtkWidget *widget)
    */
   XReparentWindow (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)),
                    GDK_WINDOW_XWINDOW (gtk_widget_get_window (widget)),
-                   stage_xwindow,
+                   window->priv->stage_xwindow,
                    window->priv->position.x, window->priv->position.y);
 }
 
@@ -143,6 +137,27 @@ shell_embedded_window_check_resize (GtkContainer *container)
     clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
 }
 
+static void
+shell_embedded_window_set_property (GObject         *object,
+                                    guint            prop_id,
+                                    const GValue    *value,
+                                    GParamSpec      *pspec)
+{
+  ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (object);
+
+  switch (prop_id)
+    {
+    case PROP_STAGE:
+      window->priv->stage_xwindow =
+        clutter_x11_get_stage_window (g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
 static GObject *
 shell_embedded_window_constructor (GType                  gtype,
                                    guint                  n_properties,
@@ -177,6 +192,7 @@ shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
 
   g_type_class_add_private (klass, sizeof (ShellEmbeddedWindowPrivate));
 
+  object_class->set_property    = shell_embedded_window_set_property;
   object_class->constructor     = shell_embedded_window_constructor;
 
   widget_class->show            = shell_embedded_window_show;
@@ -185,6 +201,14 @@ shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
   widget_class->configure_event = shell_embedded_window_configure_event;
 
   container_class->check_resize    = shell_embedded_window_check_resize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_STAGE,
+                                   g_param_spec_object ("stage",
+                                                        "Stage",
+                                                        "ClutterStage to embed on",
+                                                        CLUTTER_TYPE_STAGE,
+                                                        G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
@@ -207,8 +231,9 @@ _shell_embedded_window_set_actor (ShellEmbeddedWindow  *window,
 
   window->priv->actor = actor;
 
-  if (gtk_widget_get_visible (GTK_WIDGET (window))
-      && CLUTTER_ACTOR_IS_REALIZED (actor))
+  if (actor &&
+      CLUTTER_ACTOR_IS_REALIZED (actor) &&
+      gtk_widget_get_visible (GTK_WIDGET (window)))
     gtk_widget_map (GTK_WIDGET (window));
 }
 
@@ -267,8 +292,9 @@ _shell_embedded_window_unrealize (ShellEmbeddedWindow *window)
  * Public API
  */
 GtkWidget *
-shell_embedded_window_new (void)
+shell_embedded_window_new (ClutterStage *stage)
 {
   return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW,
+                       "stage", stage,
                        NULL);
 }
diff --git a/src/shell-embedded-window.h b/src/shell-embedded-window.h
index 91923a6..145bac3 100644
--- a/src/shell-embedded-window.h
+++ b/src/shell-embedded-window.h
@@ -3,6 +3,7 @@
 #define __SHELL_EMBEDDED_WINDOW_H__
 
 #include <gtk/gtk.h>
+#include <clutter/clutter.h>
 
 #define SHELL_TYPE_EMBEDDED_WINDOW                 (shell_embedded_window_get_type ())
 #define SHELL_EMBEDDED_WINDOW(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_EMBEDDED_WINDOW, ShellEmbeddedWindow))
@@ -29,6 +30,6 @@ struct _ShellEmbeddedWindowClass
 };
 
 GType shell_embedded_window_get_type (void) G_GNUC_CONST;
-GtkWidget *shell_embedded_window_new (void);
+GtkWidget *shell_embedded_window_new (ClutterStage *stage);
 
 #endif /* __SHELL_EMBEDDED_WINDOW_H__ */
diff --git a/src/shell-tray-icon.c b/src/shell-tray-icon.c
new file mode 100644
index 0000000..dce8630
--- /dev/null
+++ b/src/shell-tray-icon.c
@@ -0,0 +1,168 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
+#include "shell-tray-icon.h"
+#include "shell-gtk-embed.h"
+#include "shell-window-tracker.h"
+#include "gtk-compat.h"
+#include "tray/na-tray-child.h"
+#include <gdk/gdkx.h>
+#include "st.h"
+
+enum {
+   PROP_0,
+
+   PROP_PID,
+   PROP_TITLE,
+   PROP_WM_CLASS
+};
+
+struct _ShellTrayIconPrivate
+{
+  NaTrayChild *socket;
+
+  pid_t pid;
+  char *title, *wm_class;
+};
+
+G_DEFINE_TYPE (ShellTrayIcon, shell_tray_icon, SHELL_TYPE_GTK_EMBED);
+
+static void
+shell_tray_icon_finalize (GObject *object)
+{
+  ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
+
+  g_free (icon->priv->title);
+  g_free (icon->priv->wm_class);
+
+  G_OBJECT_CLASS (shell_tray_icon_parent_class)->finalize (object);
+}
+
+static void
+shell_tray_icon_constructed (GObject *object)
+{
+  GdkWindow *icon_app_window;
+  ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
+  ShellEmbeddedWindow *window;
+  GdkDisplay *display;
+  Window plug_xid;
+  Atom _NET_WM_PID, type;
+  int result, format;
+  gulong nitems, bytes_after, *val = NULL;
+
+  /* We do all this now rather than computing it on the fly later,
+   * because the shell may want to see their values from a
+   * tray-icon-removed signal handler, at which point the plug has
+   * already been removed from the socket.
+   */
+
+  g_object_get (object, "window", &window, NULL);
+  g_return_if_fail (window != NULL);
+  icon->priv->socket = NA_TRAY_CHILD (gtk_bin_get_child (GTK_BIN (window)));
+  g_object_unref (window);
+
+  icon->priv->title = na_tray_child_get_title (icon->priv->socket);
+  na_tray_child_get_wm_class (icon->priv->socket, NULL, &icon->priv->wm_class);
+
+  icon_app_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket));
+  plug_xid = GDK_WINDOW_XID (icon_app_window);
+
+  display = gtk_widget_get_display (GTK_WIDGET (icon->priv->socket));
+  gdk_error_trap_push ();
+  _NET_WM_PID = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PID");
+  result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), plug_xid,
+                               _NET_WM_PID, 0, G_MAXLONG, False, XA_CARDINAL,
+                               &type, &format, &nitems,
+                               &bytes_after, (guchar **)&val);
+  if (!gdk_error_trap_pop () &&
+      result == Success &&
+      type == XA_CARDINAL &&
+      nitems == 1)
+    icon->priv->pid = *val;
+
+  if (val)
+    XFree (val);
+}
+
+static void
+shell_tray_icon_get_property (GObject         *object,
+                              guint            prop_id,
+                              GValue          *value,
+                              GParamSpec      *pspec)
+{
+  ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
+
+  switch (prop_id)
+    {
+    case PROP_PID:
+      g_value_set_uint (value, icon->priv->pid);
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, icon->priv->title);
+      break;
+
+    case PROP_WM_CLASS:
+      g_value_set_string (value, icon->priv->wm_class);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+shell_tray_icon_class_init (ShellTrayIconClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ShellTrayIconPrivate));
+
+  object_class->get_property = shell_tray_icon_get_property;
+  object_class->constructed  = shell_tray_icon_constructed;
+  object_class->finalize     = shell_tray_icon_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_PID,
+                                   g_param_spec_uint ("pid",
+                                                      "PID",
+                                                      "The PID of the icon's application",
+                                                      0, G_MAXUINT, 0,
+                                                      G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+                                   PROP_TITLE,
+                                   g_param_spec_string ("title",
+                                                        "Title",
+                                                        "The icon's window title",
+                                                        NULL,
+                                                        G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+                                   PROP_WM_CLASS,
+                                   g_param_spec_string ("wm-class",
+                                                        "WM Class",
+                                                        "The icon's window WM_CLASS",
+                                                        NULL,
+                                                        G_PARAM_READABLE));
+}
+
+static void
+shell_tray_icon_init (ShellTrayIcon *icon)
+{
+  icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, SHELL_TYPE_TRAY_ICON,
+                                            ShellTrayIconPrivate);
+}
+
+/*
+ * Public API
+ */
+ClutterActor *
+shell_tray_icon_new (ShellEmbeddedWindow *window)
+{
+  g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL);
+
+  return g_object_new (SHELL_TYPE_TRAY_ICON,
+                       "window", window,
+                       NULL);
+}
diff --git a/src/shell-tray-icon.h b/src/shell-tray-icon.h
new file mode 100644
index 0000000..88e2a14
--- /dev/null
+++ b/src/shell-tray-icon.h
@@ -0,0 +1,34 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_TRAY_ICON_H__
+#define __SHELL_TRAY_ICON_H__
+
+#include "shell-gtk-embed.h"
+
+#define SHELL_TYPE_TRAY_ICON                 (shell_tray_icon_get_type ())
+#define SHELL_TRAY_ICON(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_TRAY_ICON, ShellTrayIcon))
+#define SHELL_TRAY_ICON_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_TRAY_ICON, ShellTrayIconClass))
+#define SHELL_IS_TRAY_ICON(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_TRAY_ICON))
+#define SHELL_IS_TRAY_ICON_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_TRAY_ICON))
+#define SHELL_TRAY_ICON_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_TRAY_ICON, ShellTrayIconClass))
+
+typedef struct _ShellTrayIcon        ShellTrayIcon;
+typedef struct _ShellTrayIconClass   ShellTrayIconClass;
+typedef struct _ShellTrayIconPrivate ShellTrayIconPrivate;
+
+struct _ShellTrayIcon
+{
+    ShellGtkEmbed parent;
+
+    ShellTrayIconPrivate *priv;
+};
+
+struct _ShellTrayIconClass
+{
+    ShellGtkEmbedClass parent_class;
+};
+
+
+GType         shell_tray_icon_get_type (void) G_GNUC_CONST;
+ClutterActor *shell_tray_icon_new      (ShellEmbeddedWindow *window);
+
+#endif /* __SHELL_TRAY_ICON_H__ */
diff --git a/src/shell-tray-manager.c b/src/shell-tray-manager.c
index f7c172a..330e811 100644
--- a/src/shell-tray-manager.c
+++ b/src/shell-tray-manager.c
@@ -13,7 +13,7 @@
 #include "shell-tray-manager.h"
 #include "na-tray-manager.h"
 
-#include "shell-gtk-embed.h"
+#include "shell-tray-icon.h"
 #include "shell-embedded-window.h"
 #include "shell-global.h"
 
@@ -30,7 +30,6 @@ typedef struct {
   GtkWidget *socket;
   GtkWidget *window;
   ClutterActor *actor;
-  gboolean emitted_plugged;
 } ShellTrayManagerChild;
 
 enum {
@@ -159,11 +158,9 @@ shell_tray_manager_class_init (ShellTrayManagerClass *klass)
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_added),
                   NULL, NULL,
-                  gi_cclosure_marshal_generic,
-                  G_TYPE_NONE, 3,
-                  CLUTTER_TYPE_ACTOR,
-                  G_TYPE_STRING,
-                  G_TYPE_STRING);
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1,
+                  CLUTTER_TYPE_ACTOR);
   shell_tray_manager_signals[TRAY_ICON_REMOVED] =
     g_signal_new ("tray-icon-removed",
                   G_TYPE_FROM_CLASS (klass),
@@ -280,31 +277,14 @@ on_plug_added (GtkSocket        *socket,
                ShellTrayManager *manager)
 {
   ShellTrayManagerChild *child;
-  char *wm_class, *lower_wm_class;
-  char *title;
 
-  child = g_hash_table_lookup (manager->priv->icons, socket);
-  /* Only emit this signal once; the point of waiting until we
-   * get the first plugged notification is to be able to get the WM_CLASS
-   * from the child window.  But we don't want to emit this signal twice
-   * if for some reason the socket gets replugged.
-   */
-  if (child->emitted_plugged)
-    return;
-  child->emitted_plugged = TRUE;
+  g_signal_handlers_disconnect_by_func (socket, on_plug_added, manager);
 
-  na_tray_child_get_wm_class (NA_TRAY_CHILD (socket), NULL, &wm_class);
-  if (!wm_class)
-    return;
-
-  title = na_tray_child_get_title (NA_TRAY_CHILD (socket));
+  child = g_hash_table_lookup (manager->priv->icons, socket);
 
-  lower_wm_class = g_ascii_strdown (wm_class, -1);
+  child->actor = shell_tray_icon_new (SHELL_EMBEDDED_WINDOW (child->window));
   g_signal_emit (manager, shell_tray_manager_signals[TRAY_ICON_ADDED], 0,
-                 child->actor, lower_wm_class, title);
-  g_free (lower_wm_class);
-  g_free (wm_class);
-  g_free (title);
+                 child->actor);
 }
 
 static void
@@ -313,7 +293,6 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
 {
   ShellTrayManager *manager = user_data;
   GtkWidget *win;
-  ClutterActor *icon;
   ShellTrayManagerChild *child;
 
   /* We don't need the NaTrayIcon to be composited on the window we
@@ -324,7 +303,7 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
    */
   na_tray_child_set_composited (NA_TRAY_CHILD (socket), FALSE);
 
-  win = shell_embedded_window_new ();
+  win = shell_embedded_window_new (manager->priv->stage);
   gtk_container_add (GTK_CONTAINER (win), socket);
 
   /* The colormap of the socket matches that of its contents; make
@@ -341,9 +320,6 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
 
   gtk_widget_show_all (win);
 
-  icon = shell_gtk_embed_new (SHELL_EMBEDDED_WINDOW (win));
-
-  child->actor = g_object_ref (icon);
   g_hash_table_insert (manager->priv->icons, socket, child);
 
   g_signal_connect (socket, "plug-added", G_CALLBACK (on_plug_added), manager);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]