From ae9360659db29eb2850ccd2db8f1ec69cdfa29ce Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 7 Sep 2010 22:27:08 -0400 Subject: [PATCH] [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(-) create mode 100644 src/shell-tray-icon.c create mode 100644 src/shell-tray-icon.h diff --git a/js/ui/panel.js b/js/ui/panel.js index 1abcad0ff..4c3206c9b 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 c47d834fe..db97ef2e6 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 8da3f1486..a3b9153ae 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 91923a657..145bac3a9 100644 --- a/src/shell-embedded-window.h +++ b/src/shell-embedded-window.h @@ -3,6 +3,7 @@ #define __SHELL_EMBEDDED_WINDOW_H__ #include +#include #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 000000000..dce8630f7 --- /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 +#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 000000000..88e2a1406 --- /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 f7c172a38..330e81169 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; + + g_signal_handlers_disconnect_by_func (socket, on_plug_added, manager); 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; - 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)); - - 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);