[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
This commit is contained in:
Dan Winship 2010-09-07 22:27:08 -04:00
parent 63f2066135
commit ae9360659d
7 changed files with 261 additions and 52 deletions

View File

@ -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];

View File

@ -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 \

View File

@ -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);
}

View File

@ -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__ */

168
src/shell-tray-icon.c Normal file
View File

@ -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);
}

34
src/shell-tray-icon.h Normal file
View File

@ -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__ */

View File

@ -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);