[gnome-shell] Add size negotiation to the tray icons



commit 3327a198e1d457d0a1a951eec4eaa67a44124e25
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Sun May 3 13:05:15 2009 -0400

    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            |   92 +++++++-----
 9 files changed, 674 insertions(+), 251 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index ac49049..f156228 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 0000000..44b00c2
--- /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 0000000..80c64ff
--- /dev/null
+++ b/src/shell-embedded-window.c
@@ -0,0 +1,243 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include <gdk/gdkx.h>
+#include <clutter/x11/clutter-x11.h>
+
+#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 0000000..98572ac
--- /dev/null
+++ b/src/shell-embedded-window.h
@@ -0,0 +1,33 @@
+#ifndef __SHELL_EMBEDDED_WINDOW_H__
+#define __SHELL_EMBEDDED_WINDOW_H__
+
+#include <gtk/gtk.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))
+#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 0000000..998651c
--- /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 <clutter/glx/clutter-glx.h>
+#include <clutter/x11/clutter-x11.h>
+#include <gdk/gdkx.h>
+
+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 0000000..a6b014f
--- /dev/null
+++ b/src/shell-gtk-embed.h
@@ -0,0 +1,21 @@
+#ifndef __SHELL_GTK_EMBED_H__
+#define __SHELL_GTK_EMBED_H__
+
+#include <clutter/glx/clutter-glx.h>
+
+#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 bb10a64..0000000
--- 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 <clutter/glx/clutter-glx.h>
-#include <clutter/x11/clutter-x11.h>
-#include <gdk/gdkx.h>
-
-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 d97a01b..0000000
--- 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 <clutter/glx/clutter-glx.h>
-#include <gtk/gtk.h>
-
-#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 45654c9..de8b861 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);
+
+  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
-    manager->priv->stage_window = gdk_window_foreign_new (stage_xwin);
+    stage_window = gdk_window_foreign_new (stage_xwindow);
+
+  screen = gdk_drawable_get_screen (stage_window);
 
-  na_tray_manager_manage_screen (manager->priv->na_manager,
-                                 gdk_drawable_get_screen (GDK_DRAWABLE (manager->priv->stage_window)));
+  g_object_unref (stage_window);
+
+  na_tray_manager_manage_screen (manager->priv->na_manager, screen);
 }
 
 static GdkPixmap *
@@ -230,6 +244,28 @@ create_bg_pixmap (GdkColormap  *colormap,
 }
 
 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);
+  child = g_slice_new (ShellTrayManagerChild);
+  child->window = win;
+  child->socket = socket;
 
-  /* 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);
-    }
+  g_signal_connect (win, "realize",
+                    G_CALLBACK (shell_tray_manager_child_on_realize), child);
 
-  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);
+  icon = shell_gtk_embed_new (SHELL_EMBEDDED_WINDOW (win));
 
-  child = g_slice_new (ShellTrayManagerChild);
-  child->window = win;
-  child->socket = socket;
   child->actor = g_object_ref (icon);
   g_hash_table_insert (manager->priv->icons, socket, child);
 



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