From 3327a198e1d457d0a1a951eec4eaa67a44124e25 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Sun, 3 May 2009 13:05:15 -0400 Subject: [PATCH] Add size negotiation to the tray icons * Rename ShellGtkWindowActor to ShellGtkEmbed, and make it require a ShellEmbeddedWindow rather than a general GtkWindow. * Add ShellEmbeddedWindow subclass of GtkWindow that bypasses most of the GtkWindow complexity for size negotiation, and calls out to a clutter actor instead; also automatically handle reparenting the GtkWindow into the stage window. * Use the reworked ShellGtkEmbed in ShellTrayManager, this simplifies the code a bit, and more importantly results in the tray icons having the correct size negotiation, rather than having a fixed 24x24 size. http://bugzilla.gnome.org/show_bug.cgi?id=580047 --- src/Makefile.am | 11 +- src/shell-embedded-window-private.h | 19 ++ src/shell-embedded-window.c | 243 +++++++++++++++++++++++ src/shell-embedded-window.h | 33 ++++ src/shell-gtk-embed.c | 297 ++++++++++++++++++++++++++++ src/shell-gtk-embed.h | 21 ++ src/shell-gtkwindow-actor.c | 174 ---------------- src/shell-gtkwindow-actor.h | 35 ---- src/shell-tray-manager.c | 98 +++++---- 9 files changed, 677 insertions(+), 254 deletions(-) create mode 100644 src/shell-embedded-window-private.h create mode 100644 src/shell-embedded-window.c create mode 100644 src/shell-embedded-window.h create mode 100644 src/shell-gtk-embed.c create mode 100644 src/shell-gtk-embed.h delete mode 100644 src/shell-gtkwindow-actor.c delete mode 100644 src/shell-gtkwindow-actor.h diff --git a/src/Makefile.am b/src/Makefile.am index ac49049a8..f15622852 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -57,8 +57,11 @@ libgnome_shell_la_SOURCES = \ shell-app-system.h \ shell-arrow.c \ shell-arrow.h \ - shell-gtkwindow-actor.c \ - shell-gtkwindow-actor.h \ + shell-embedded-window.c \ + shell-embedded-window.h \ + shell-embedded-window-private.h \ + shell-gtk-embed.c \ + shell-gtk-embed.h \ shell-process.c \ shell-process.h \ shell-global.c \ @@ -70,8 +73,8 @@ libgnome_shell_la_SOURCES = \ shell-wm.c \ shell-wm.h -# ClutterGLXTexturePixmap is currently not wrapped -non_gir_sources = shell-gtkwindow-actor.h +non_gir_sources = \ + shell-embedded-window-private.h shell_recorder_sources = \ shell-recorder.c \ diff --git a/src/shell-embedded-window-private.h b/src/shell-embedded-window-private.h new file mode 100644 index 000000000..44b00c206 --- /dev/null +++ b/src/shell-embedded-window-private.h @@ -0,0 +1,19 @@ +#ifndef __SHELL_EMBEDDED_WINDOW_PRIVATE_H__ +#define __SHELL_EMBEDDED_WINDOW_PRIVATE_H__ + +#include "shell-embedded-window.h" +#include "shell-gtk-embed.h" + +void _shell_embedded_window_set_actor (ShellEmbeddedWindow *window, + ShellGtkEmbed *embed); + +void _shell_embedded_window_allocate (ShellEmbeddedWindow *window, + int x, + int y, + int width, + int height); + +void _shell_embedded_window_realize (ShellEmbeddedWindow *window); +void _shell_embedded_window_unrealize (ShellEmbeddedWindow *window); + +#endif /* __SHELL_EMBEDDED_WINDOW_PRIVATE_H__ */ diff --git a/src/shell-embedded-window.c b/src/shell-embedded-window.c new file mode 100644 index 000000000..80c64fff9 --- /dev/null +++ b/src/shell-embedded-window.c @@ -0,0 +1,243 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#include +#include + +#include "shell-embedded-window-private.h" + +/* This type is a subclass of GtkWindow that ties the window to a + * ShellGtkEmbed; the window is reparented into the stage + * window for the actor and the resizing logic is bound to the clutter + * logic. + * + * The typical usage we might expect is + * + * - ShellEmbeddedWindow is created and filled with content + * - ShellEmbeddedWindow is shown with gtk_widget_show_all() + * - ShellGtkEmbed is created for the ShellEmbeddedWindow + * - actor is added to a stage + * + * Ideally, the way it would work is that the GtkWindow is mapped + * if and only if both: + * + * - GTK_WIDGET_VISIBLE (window) [widget has been shown] + * - Actor is mapped [actor and all parents visible, actor in stage] + * + * Implementing this perfectly is not currently possible, due to problems + * in Clutter, see: + * + * http://bugzilla.openedhand.com/show_bug.cgi?id=1138 + * + * So until that is fixed we use the "realized" state of the ClutterActor + * as a stand-in for the ideal mapped state, this will work as long + * as the ClutterActor and all its parents are in fact visible. + */ + +G_DEFINE_TYPE (ShellEmbeddedWindow, shell_embedded_window, GTK_TYPE_WINDOW); + +struct _ShellEmbeddedWindowPrivate { + ShellGtkEmbed *actor; + + GdkRectangle position; +}; + +/* + * The normal gtk_window_show() starts all of the complicated asynchronous + * window resizing code running; we don't want or need any of that. + * Bypassing the normal code does mean that the extra geometry management + * available on GtkWindow: gridding, maximum sizes, etc, is ignored; we + * don't really want that anyways - we just want a way of embedding a + * GtkWidget into a Clutter stage. + */ +static void +shell_embedded_window_show (GtkWidget *widget) +{ + ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget); + + GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE); + + if (window->priv->actor) + { + /* Size is 0x0 if the GtkWindow is not shown */ + clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor)); + + if (CLUTTER_ACTOR_IS_REALIZED (window->priv->actor)) + gtk_widget_map (widget); + } +} + +static void +shell_embedded_window_hide (GtkWidget *widget) +{ + ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget); + + clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor)); + + GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->hide (widget); +} + +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 + * the stage. However, GDK will be left thinking that the parent of + * window->window is the root window - it's not immediately clear + * to me whether that is more or less likely to cause problems than + * modifying the GDK hierarchy. + */ + XReparentWindow (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)), + GDK_WINDOW_XWINDOW (widget->window), + stage_xwindow, + window->priv->position.x, window->priv->position.y); +} + +static gboolean +shell_embedded_window_configure_event (GtkWidget *widget, + GdkEventConfigure *event) +{ + /* Normally a configure event coming back from X triggers the + * resizing logic inside GtkWindow; we just ignore them + * since we are handling the resizing logic separately. + */ + return FALSE; +} + +static void +shell_embedded_window_check_resize (GtkContainer *container) +{ + ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (container); + + /* Check resize is called when a resize is queued on something + * inside the GtkWindow; we need to make sure that in response + * to this gtk_widget_size_request() and then + * gtk_widget_size_allocate() are called; we defer to the Clutter + * logic and assume it will do the right thing. + */ + if (window->priv->actor) + clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor)); +} + +static void +shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ShellEmbeddedWindowPrivate)); + + widget_class->show = shell_embedded_window_show; + widget_class->hide = shell_embedded_window_hide; + widget_class->realize = shell_embedded_window_realize; + widget_class->configure_event = shell_embedded_window_configure_event; + + container_class->check_resize = shell_embedded_window_check_resize; +} + +static void +shell_embedded_window_init (ShellEmbeddedWindow *window) +{ + window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, SHELL_TYPE_EMBEDDED_WINDOW, + ShellEmbeddedWindowPrivate); + + /* Setting the resize mode to immediate means that calling queue_resize() + * on a widget within the window will immmediately call check_resize() + * to be called, instead of having it queued to an idle. From our perspective, + * this is ideal since we just are going to queue a resize to Clutter's + * idle resize anyways. + */ + g_object_set (G_OBJECT (window), + "resize-mode", GTK_RESIZE_IMMEDIATE, + "type", GTK_WINDOW_POPUP, + NULL); +} + +/* + * Private routines called by ShellGtkEmbed + */ + +void +_shell_embedded_window_set_actor (ShellEmbeddedWindow *window, + ShellGtkEmbed *actor) + +{ + g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window)); + + window->priv->actor = actor; + + if (GTK_WIDGET_VISIBLE (window) && CLUTTER_ACTOR_IS_REALIZED (actor)) + gtk_widget_map (GTK_WIDGET (window)); +} + +void +_shell_embedded_window_allocate (ShellEmbeddedWindow *window, + int x, + int y, + int width, + int height) +{ + GtkAllocation allocation; + + g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window)); + + window->priv->position.x = x; + window->priv->position.y = y; + window->priv->position.width = width; + window->priv->position.height = height; + + if (GTK_WIDGET_REALIZED (window)) + gdk_window_move_resize (GTK_WIDGET (window)->window, + x, y, width, height); + + allocation.x = 0; + allocation.y = 0; + allocation.width = width; + allocation.height = height; + + gtk_widget_size_allocate (GTK_WIDGET (window), &allocation); +} + +void +_shell_embedded_window_realize (ShellEmbeddedWindow *window) +{ + g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window)); + + if (GTK_WIDGET_VISIBLE (window)) + gtk_widget_map (GTK_WIDGET (window)); +} + +void +_shell_embedded_window_unrealize (ShellEmbeddedWindow *window) +{ + g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window)); + + gtk_widget_unmap (GTK_WIDGET (window)); +} + +/* + * Public API + */ +GtkWidget * +shell_embedded_window_new (void) +{ + return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW, + NULL); +} diff --git a/src/shell-embedded-window.h b/src/shell-embedded-window.h new file mode 100644 index 000000000..98572ac86 --- /dev/null +++ b/src/shell-embedded-window.h @@ -0,0 +1,33 @@ +#ifndef __SHELL_EMBEDDED_WINDOW_H__ +#define __SHELL_EMBEDDED_WINDOW_H__ + +#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)) +#define SHELL_EMBEDDED_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_EMBEDDED_WINDOW, ShellEmbeddedWindowClass)) +#define SHELL_IS_EMBEDDED_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_EMBEDDED_WINDOW)) +#define SHELL_IS_EMBEDDED_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_EMBEDDED_WINDOW)) +#define SHELL_EMBEDDED_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_EMBEDDED_WINDOW, ShellEmbeddedWindowClass)) + +typedef struct _ShellEmbeddedWindow ShellEmbeddedWindow; +typedef struct _ShellEmbeddedWindowClass ShellEmbeddedWindowClass; + +typedef struct _ShellEmbeddedWindowPrivate ShellEmbeddedWindowPrivate; + +struct _ShellEmbeddedWindow +{ + GtkWindow parent; + + ShellEmbeddedWindowPrivate *priv; +}; + +struct _ShellEmbeddedWindowClass +{ + GtkWindowClass parent_class; +}; + +GType shell_embedded_window_get_type (void) G_GNUC_CONST; +GtkWidget *shell_embedded_window_new (void); + +#endif /* __SHELL_EMBEDDED_WINDOW_H__ */ diff --git a/src/shell-gtk-embed.c b/src/shell-gtk-embed.c new file mode 100644 index 000000000..998651c09 --- /dev/null +++ b/src/shell-gtk-embed.c @@ -0,0 +1,297 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#include "shell-embedded-window-private.h" + +#include +#include +#include + +enum { + PROP_0, + + PROP_WINDOW +}; + +typedef struct _ShellGtkEmbedPrivate ShellGtkEmbedPrivate; + +/* The reason that the instance/class structures are here is to avoid + * problems with g-ir-scanner chocking on ClutterGLXTexturePixmap. We + * stick with having a separate private structure so that we can move + * the instance/class structures back to the public header if this + * code is reused in another context where inheritance from C is useful. + */ +struct _ShellGtkEmbed +{ + ClutterGLXTexturePixmap parent; + + ShellGtkEmbedPrivate *priv; +}; + +struct _ShellGtkEmbedClass +{ + ClutterGLXTexturePixmapClass parent_class; +}; + +struct _ShellGtkEmbedPrivate +{ + ShellEmbeddedWindow *window; +}; + +G_DEFINE_TYPE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_GLX_TYPE_TEXTURE_PIXMAP); + +static void shell_gtk_embed_set_window (ShellGtkEmbed *embed, + ShellEmbeddedWindow *window); + +static void +shell_gtk_embed_on_window_destroy (GtkObject *object, + ShellGtkEmbed *embed) +{ + shell_gtk_embed_set_window (embed, NULL); +} + +static void +shell_gtk_embed_on_window_realize (GtkWidget *widget, + ShellGtkEmbed *embed) +{ + /* Here automatic=FALSE means to use CompositeRedirectManual. + * That is, the X server shouldn't draw the window onto the + * screen. + */ + clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed), + GDK_WINDOW_XWINDOW (widget->window), + FALSE); +} + +static void +shell_gtk_embed_set_window (ShellGtkEmbed *embed, + ShellEmbeddedWindow *window) +{ + + if (embed->priv->window) + { + _shell_embedded_window_set_actor (embed->priv->window, NULL); + + g_object_unref (embed->priv->window); + + clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed), + None, + FALSE); + + g_signal_handlers_disconnect_by_func (embed->priv->window, + (gpointer)shell_gtk_embed_on_window_destroy, + embed); + g_signal_handlers_disconnect_by_func (embed->priv->window, + (gpointer)shell_gtk_embed_on_window_realize, + embed); + } + + embed->priv->window = window; + + if (embed->priv->window) + { + g_object_ref (embed->priv->window); + + _shell_embedded_window_set_actor (embed->priv->window, embed); + + g_signal_connect (embed->priv->window, "destroy", + G_CALLBACK (shell_gtk_embed_on_window_destroy), embed); + g_signal_connect (embed->priv->window, "realize", + G_CALLBACK (shell_gtk_embed_on_window_realize), embed); + + if (GTK_WIDGET_REALIZED (window)) + shell_gtk_embed_on_window_realize (GTK_WIDGET (embed->priv->window), embed); + } + + clutter_actor_queue_relayout (CLUTTER_ACTOR (embed)); +} + +static void +shell_gtk_embed_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ShellGtkEmbed *embed = SHELL_GTK_EMBED (object); + + switch (prop_id) + { + case PROP_WINDOW: + shell_gtk_embed_set_window (embed, (ShellEmbeddedWindow *)g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +shell_gtk_embed_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ShellGtkEmbed *embed = SHELL_GTK_EMBED (object); + + switch (prop_id) + { + case PROP_WINDOW: + g_value_set_object (value, embed->priv->window); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +shell_gtk_embed_get_preferred_width (ClutterActor *actor, + ClutterUnit for_height, + ClutterUnit *min_width_p, + ClutterUnit *natural_width_p) +{ + ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); + + if (embed->priv->window && GTK_WIDGET_VISIBLE (embed->priv->window)) + { + GtkRequisition requisition; + gtk_widget_size_request (GTK_WIDGET (embed->priv->window), &requisition); + + *min_width_p = *natural_width_p = requisition.width; + } + else + *min_width_p = *natural_width_p = 0; +} + +static void +shell_gtk_embed_get_preferred_height (ClutterActor *actor, + ClutterUnit for_width, + ClutterUnit *min_height_p, + ClutterUnit *natural_height_p) +{ + ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); + + if (embed->priv->window && GTK_WIDGET_VISIBLE (embed->priv->window)) + { + GtkRequisition requisition; + gtk_widget_size_request (GTK_WIDGET (embed->priv->window), &requisition); + + *min_height_p = *natural_height_p = requisition.height; + } + else + *min_height_p = *natural_height_p = 0; +} + +static void +shell_gtk_embed_allocate (ClutterActor *actor, + const ClutterActorBox *box, + gboolean absolute_origin_changed) +{ + ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); + int wx = 0, wy = 0, x, y, ax, ay; + + CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)-> + allocate (actor, box, absolute_origin_changed); + + /* Find the actor's new coordinates in terms of the stage (which is + * priv->window's parent window. + */ + while (actor) + { + clutter_actor_get_position (actor, &x, &y); + clutter_actor_get_anchor_point (actor, &ax, &ay); + + wx += x - ax; + wy += y - ay; + + actor = clutter_actor_get_parent (actor); + } + + _shell_embedded_window_allocate (embed->priv->window, + wx, wy, + box->x2 - box->x1, + box->y2 - box->y1); +} + +static void +shell_gtk_embed_realize (ClutterActor *actor) +{ + ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); + + _shell_embedded_window_realize (embed->priv->window); + + CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->realize (actor); +} + +static void +shell_gtk_embed_unrealize (ClutterActor *actor) +{ + ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor); + + _shell_embedded_window_unrealize (embed->priv->window); + + CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->unrealize (actor); +} + +static void +shell_gtk_embed_dispose (GObject *object) +{ + ShellGtkEmbed *embed = SHELL_GTK_EMBED (object); + + shell_gtk_embed_set_window (embed, NULL); + + G_OBJECT_CLASS (shell_gtk_embed_parent_class)->dispose (object); +} + +static void +shell_gtk_embed_class_init (ShellGtkEmbedClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ShellGtkEmbedPrivate)); + + object_class->get_property = shell_gtk_embed_get_property; + object_class->set_property = shell_gtk_embed_set_property; + object_class->dispose = shell_gtk_embed_dispose; + + actor_class->get_preferred_width = shell_gtk_embed_get_preferred_width; + actor_class->get_preferred_height = shell_gtk_embed_get_preferred_height; + actor_class->allocate = shell_gtk_embed_allocate; + actor_class->realize = shell_gtk_embed_realize; + actor_class->unrealize = shell_gtk_embed_unrealize; + + g_object_class_install_property (object_class, + PROP_WINDOW, + g_param_spec_object ("window", + "Window", + "ShellEmbeddedWindow to embed", + SHELL_TYPE_EMBEDDED_WINDOW, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +shell_gtk_embed_init (ShellGtkEmbed *embed) +{ + embed->priv = G_TYPE_INSTANCE_GET_PRIVATE (embed, SHELL_TYPE_GTK_EMBED, + ShellGtkEmbedPrivate); + + /* automatic here means whether ClutterX11TexturePixmap should + * process damage update and refresh the pixmap itself. + */ + clutter_x11_texture_pixmap_set_automatic (CLUTTER_X11_TEXTURE_PIXMAP (embed), TRUE); +} + +/* + * Public API + */ +ClutterActor * +shell_gtk_embed_new (ShellEmbeddedWindow *window) +{ + g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL); + + return g_object_new (SHELL_TYPE_GTK_EMBED, + "window", window, + NULL); +} diff --git a/src/shell-gtk-embed.h b/src/shell-gtk-embed.h new file mode 100644 index 000000000..a6b014f6c --- /dev/null +++ b/src/shell-gtk-embed.h @@ -0,0 +1,21 @@ +#ifndef __SHELL_GTK_EMBED_H__ +#define __SHELL_GTK_EMBED_H__ + +#include + +#include "shell-embedded-window.h" + +#define SHELL_TYPE_GTK_EMBED (shell_gtk_embed_get_type ()) +#define SHELL_GTK_EMBED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_GTK_EMBED, ShellGtkEmbed)) +#define SHELL_GTK_EMBED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_GTK_EMBED, ShellGtkEmbedClass)) +#define SHELL_IS_GTK_EMBED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_GTK_EMBED)) +#define SHELL_IS_GTK_EMBED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_GTK_EMBED)) +#define SHELL_GTK_EMBED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_GTK_EMBED, ShellGtkEmbedClass)) + +typedef struct _ShellGtkEmbed ShellGtkEmbed; +typedef struct _ShellGtkEmbedClass ShellGtkEmbedClass; + +GType shell_gtk_embed_get_type (void) G_GNUC_CONST; +ClutterActor *shell_gtk_embed_new (ShellEmbeddedWindow *window); + +#endif /* __SHELL_GTK_EMBED_H__ */ diff --git a/src/shell-gtkwindow-actor.c b/src/shell-gtkwindow-actor.c deleted file mode 100644 index bb10a6431..000000000 --- a/src/shell-gtkwindow-actor.c +++ /dev/null @@ -1,174 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -#include "shell-gtkwindow-actor.h" - -#include -#include -#include - -enum { - PROP_0, - - PROP_WINDOW -}; - -G_DEFINE_TYPE (ShellGtkWindowActor, shell_gtk_window_actor, CLUTTER_GLX_TYPE_TEXTURE_PIXMAP); - -struct _ShellGtkWindowActorPrivate { - GtkWidget *window; -}; - -static void -shell_gtk_window_actor_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ShellGtkWindowActor *wactor = SHELL_GTK_WINDOW_ACTOR (object); - - switch (prop_id) - { - case PROP_WINDOW: - wactor->priv->window = g_value_dup_object (value); - - /* Here automatic=FALSE means to use CompositeRedirectManual. - * That is, the X server shouldn't draw the window onto the - * screen. - */ - clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (wactor), - GDK_WINDOW_XWINDOW (wactor->priv->window->window), - FALSE); - /* Here automatic has a different meaning--whether - * ClutterX11TexturePixmap should process damage update and - * refresh the pixmap itself. - */ - clutter_x11_texture_pixmap_set_automatic (CLUTTER_X11_TEXTURE_PIXMAP (wactor), TRUE); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -shell_gtk_window_actor_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ShellGtkWindowActor *wactor = SHELL_GTK_WINDOW_ACTOR (object); - - switch (prop_id) - { - case PROP_WINDOW: - g_value_set_object (value, wactor->priv->window); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -shell_gtk_window_actor_allocate (ClutterActor *actor, - const ClutterActorBox *box, - gboolean absolute_origin_changed) -{ - ShellGtkWindowActor *wactor = SHELL_GTK_WINDOW_ACTOR (actor); - int wx = 0, wy = 0, x, y, ax, ay; - - CLUTTER_ACTOR_CLASS (shell_gtk_window_actor_parent_class)-> - allocate (actor, box, absolute_origin_changed); - - /* Find the actor's new coordinates in terms of the stage (which is - * priv->window's parent window. - */ - while (actor) - { - clutter_actor_get_position (actor, &x, &y); - clutter_actor_get_anchor_point (actor, &ax, &ay); - - wx += x - ax; - wy += y - ay; - - actor = clutter_actor_get_parent (actor); - } - - gtk_window_move (GTK_WINDOW (wactor->priv->window), wx, wy); -} - -static void -shell_gtk_window_actor_show (ClutterActor *actor) -{ - ShellGtkWindowActor *wactor = SHELL_GTK_WINDOW_ACTOR (actor); - - gtk_widget_show (wactor->priv->window); - - CLUTTER_ACTOR_CLASS (shell_gtk_window_actor_parent_class)->show (actor); -} - -static void -shell_gtk_window_actor_hide (ClutterActor *actor) -{ - ShellGtkWindowActor *wactor = SHELL_GTK_WINDOW_ACTOR (actor); - - gtk_widget_hide (wactor->priv->window); - - CLUTTER_ACTOR_CLASS (shell_gtk_window_actor_parent_class)->hide (actor); -} - -static void -shell_gtk_window_actor_dispose (GObject *object) -{ - ShellGtkWindowActor *wactor = SHELL_GTK_WINDOW_ACTOR (object); - - if (wactor->priv->window) - { - gtk_widget_destroy (wactor->priv->window); - wactor->priv->window = NULL; - } - - G_OBJECT_CLASS (shell_gtk_window_actor_parent_class)->dispose (object); -} - -static void -shell_gtk_window_actor_class_init (ShellGtkWindowActorClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - - g_type_class_add_private (klass, sizeof (ShellGtkWindowActorPrivate)); - - object_class->get_property = shell_gtk_window_actor_get_property; - object_class->set_property = shell_gtk_window_actor_set_property; - object_class->dispose = shell_gtk_window_actor_dispose; - - actor_class->allocate = shell_gtk_window_actor_allocate; - actor_class->show = shell_gtk_window_actor_show; - actor_class->hide = shell_gtk_window_actor_hide; - - g_object_class_install_property (object_class, - PROP_WINDOW, - g_param_spec_object ("window", - "Window", - "GtkWindow to wrap", - GTK_TYPE_WINDOW, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -} - -static void -shell_gtk_window_actor_init (ShellGtkWindowActor *actor) -{ - actor->priv = G_TYPE_INSTANCE_GET_PRIVATE (actor, SHELL_TYPE_GTK_WINDOW_ACTOR, - ShellGtkWindowActorPrivate); -} - -ClutterActor * -shell_gtk_window_actor_new (GtkWidget *window) -{ - return g_object_new (SHELL_TYPE_GTK_WINDOW_ACTOR, - "window", window, - NULL); -} diff --git a/src/shell-gtkwindow-actor.h b/src/shell-gtkwindow-actor.h deleted file mode 100644 index d97a01bfb..000000000 --- a/src/shell-gtkwindow-actor.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __SHELL_GTK_WINDOW_ACTOR_H__ -#define __SHELL_GTK_WINDOW_ACTOR_H__ - -#include -#include - -#define SHELL_TYPE_GTK_WINDOW_ACTOR (shell_gtk_window_actor_get_type ()) -#define SHELL_GTK_WINDOW_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_GTK_WINDOW_ACTOR, ShellGtkWindowActor)) -#define SHELL_GTK_WINDOW_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_GTK_WINDOW_ACTOR, ShellGtkWindowActorClass)) -#define SHELL_IS_GTK_WINDOW_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_GTK_WINDOW_ACTOR)) -#define SHELL_IS_GTK_WINDOW_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_GTK_WINDOW_ACTOR)) -#define SHELL_GTK_WINDOW_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_GTK_WINDOW_ACTOR, ShellGtkWindowActorClass)) - -typedef struct _ShellGtkWindowActor ShellGtkWindowActor; -typedef struct _ShellGtkWindowActorClass ShellGtkWindowActorClass; - -typedef struct _ShellGtkWindowActorPrivate ShellGtkWindowActorPrivate; - -struct _ShellGtkWindowActor -{ - ClutterGLXTexturePixmap parent; - - ShellGtkWindowActorPrivate *priv; -}; - -struct _ShellGtkWindowActorClass -{ - ClutterGLXTexturePixmapClass parent_class; - -}; - -GType shell_gtk_window_actor_get_type (void) G_GNUC_CONST; -ClutterActor *shell_gtk_window_actor_new (GtkWidget *window); - -#endif /* __SHELL_GTK_WINDOW_ACTOR_H__ */ diff --git a/src/shell-tray-manager.c b/src/shell-tray-manager.c index 45654c941..de8b861a2 100644 --- a/src/shell-tray-manager.c +++ b/src/shell-tray-manager.c @@ -7,12 +7,12 @@ #include "shell-tray-manager.h" #include "na-tray-manager.h" -#include "shell-gtkwindow-actor.h" +#include "shell-gtk-embed.h" +#include "shell-embedded-window.h" struct _ShellTrayManagerPrivate { NaTrayManager *na_manager; ClutterStage *stage; - GdkWindow *stage_window; ClutterColor bg_color; GHashTable *icons; @@ -130,7 +130,6 @@ shell_tray_manager_finalize (GObject *object) g_object_unref (manager->priv->na_manager); g_object_unref (manager->priv->stage); - g_object_unref (manager->priv->stage_window); g_hash_table_destroy (manager->priv->icons); G_OBJECT_CLASS (shell_tray_manager_parent_class)->finalize (object); @@ -189,20 +188,35 @@ void shell_tray_manager_manage_stage (ShellTrayManager *manager, ClutterStage *stage) { - Window stage_xwin; + Window stage_xwindow; + GdkWindow *stage_window; + GdkScreen *screen; g_return_if_fail (manager->priv->stage == NULL); manager->priv->stage = g_object_ref (stage); - stage_xwin = clutter_x11_get_stage_window (stage); - manager->priv->stage_window = gdk_window_lookup (stage_xwin); - if (manager->priv->stage_window) - g_object_ref (manager->priv->stage_window); - else - manager->priv->stage_window = gdk_window_foreign_new (stage_xwin); - na_tray_manager_manage_screen (manager->priv->na_manager, - gdk_drawable_get_screen (GDK_DRAWABLE (manager->priv->stage_window))); + stage_xwindow = clutter_x11_get_stage_window (stage); + + /* This is a pretty ugly way to get the GdkScreen for the stage; it + * will normally go through the foreign_new() case with a + * round-trip to the X server, it might be nicer to pass the screen + * in in some way. (The Clutter/Mutter combo is currently incapable + * of multi-screen operation, so alternatively we could just assume + * that clutter_x11_get_default_screen() gives us the right + * screen.) + */ + stage_window = gdk_window_lookup (stage_xwindow); + if (stage_window) + g_object_ref (stage_window); + else + stage_window = gdk_window_foreign_new (stage_xwindow); + + screen = gdk_drawable_get_screen (stage_window); + + g_object_unref (stage_window); + + na_tray_manager_manage_screen (manager->priv->na_manager, screen); } static GdkPixmap * @@ -229,6 +243,28 @@ create_bg_pixmap (GdkColormap *colormap, return pixmap; } +static void +shell_tray_manager_child_on_realize (GtkWidget *widget, + ShellTrayManagerChild *child) +{ + GdkPixmap *bg_pixmap; + + /* If the tray child is using an RGBA colormap (and so we have real + * transparency), we don't need to worry about the background. If + * not, we obey the bg-color property by creating a 1x1 pixmap of + * that color and setting it as our background. Then "parent-relative" + * background on the socket and the plug within that will cause + * the icons contents to appear on top of our background color. + */ + if (!na_tray_child_has_alpha (NA_TRAY_CHILD (child->socket))) + { + bg_pixmap = create_bg_pixmap (gtk_widget_get_colormap (widget), + &child->manager->priv->bg_color); + gdk_window_set_back_pixmap (widget->window, bg_pixmap, FALSE); + g_object_unref (bg_pixmap); + } +} + static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket, gpointer user_data) @@ -237,7 +273,6 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket, GtkWidget *win; ClutterActor *icon; ShellTrayManagerChild *child; - GdkPixmap *bg_pixmap; /* We don't need the NaTrayIcon to be composited on the window we * put it in: the window is the same size as the tray icon @@ -247,43 +282,24 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket, */ na_tray_child_set_composited (NA_TRAY_CHILD (socket), FALSE); - win = gtk_window_new (GTK_WINDOW_POPUP); + win = shell_embedded_window_new (); gtk_container_add (GTK_CONTAINER (win), socket); /* The colormap of the socket matches that of its contents; make * the window we put it in match that as well */ gtk_widget_set_colormap (win, gtk_widget_get_colormap (socket)); - gtk_widget_set_size_request (win, 24, 24); - gtk_widget_realize (win); - - /* If the tray child is using an RGBA colormap (and so we have real - * transparency), we don't need to worry about the background. If - * not, we obey the bg-color property by creating a 1x1 pixmap of - * that color and setting it as our background. Then "parent-relative" - * background on the socket and the plug within that will cause - * the icons contents to appear on top of our background color. - */ - if (!na_tray_child_has_alpha (NA_TRAY_CHILD (socket))) - { - bg_pixmap = create_bg_pixmap (gtk_widget_get_colormap (win), - &manager->priv->bg_color); - gdk_window_set_back_pixmap (win->window, bg_pixmap, FALSE); - g_object_unref (bg_pixmap); - } - - gtk_widget_set_parent_window (win, manager->priv->stage_window); - gdk_window_reparent (win->window, manager->priv->stage_window, 0, 0); - gtk_widget_show_all (win); - - icon = shell_gtk_window_actor_new (win); - - /* Move to ShellGtkWindowActor? FIXME */ - clutter_actor_set_size (icon, 24, 24); - child = g_slice_new (ShellTrayManagerChild); child->window = win; child->socket = socket; + + g_signal_connect (win, "realize", + G_CALLBACK (shell_tray_manager_child_on_realize), child); + + 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);