gnome-shell-3.6.3.1/src/shell-embedded-window.c

No issues found

  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 
  3 #include "config.h"
  4 
  5 #include <gdk/gdkx.h>
  6 #include <clutter/x11/clutter-x11.h>
  7 
  8 #include "shell-embedded-window-private.h"
  9 
 10 /* This type is a subclass of GtkWindow that ties the window to a
 11  * ShellGtkEmbed; the window is reparented into the stage
 12  * window for the actor and the resizing logic is bound to the clutter
 13  * logic.
 14  *
 15  * The typical usage we might expect is
 16  *
 17  *  - ShellEmbeddedWindow is created and filled with content
 18  *  - ShellEmbeddedWindow is shown with gtk_widget_show_all()
 19  *  - ShellGtkEmbed is created for the ShellEmbeddedWindow
 20  *  - actor is added to a stage
 21  *
 22  * The way it works is that the GtkWindow is mapped if and only if both:
 23  *
 24  * - gtk_widget_visible (window) [widget has been shown]
 25  * - Actor is mapped [actor and all parents visible, actor in stage]
 26  */
 27 
 28 G_DEFINE_TYPE (ShellEmbeddedWindow, shell_embedded_window, GTK_TYPE_WINDOW);
 29 
 30 enum {
 31    PROP_0,
 32 
 33    PROP_STAGE
 34 };
 35 
 36 struct _ShellEmbeddedWindowPrivate {
 37   ShellGtkEmbed *actor;
 38 
 39   GdkRectangle position;
 40   Window stage_xwindow;
 41 };
 42 
 43 /*
 44  * The normal gtk_window_show() starts all of the complicated asynchronous
 45  * window resizing code running; we don't want or need any of that.
 46  * Bypassing the normal code does mean that the extra geometry management
 47  * available on GtkWindow: gridding, maximum sizes, etc, is ignored; we
 48  * don't really want that anyways - we just want a way of embedding a
 49  * GtkWidget into a Clutter stage.
 50  */
 51 static void
 52 shell_embedded_window_show (GtkWidget *widget)
 53 {
 54   ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
 55   GtkWidgetClass *widget_class;
 56 
 57   /* Skip GtkWindow, but run the default GtkWidget handling which
 58    * marks the widget visible */
 59   widget_class = g_type_class_peek (GTK_TYPE_WIDGET);
 60   widget_class->show (widget);
 61 
 62   if (window->priv->actor)
 63     {
 64       /* Size is 0x0 if the GtkWindow is not shown */
 65       clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
 66 
 67       if (CLUTTER_ACTOR_IS_REALIZED (window->priv->actor))
 68         gtk_widget_map (widget);
 69     }
 70 }
 71 
 72 static void
 73 shell_embedded_window_hide (GtkWidget *widget)
 74 {
 75   ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
 76 
 77   if (window->priv->actor)
 78     clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
 79 
 80   GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->hide (widget);
 81 }
 82 
 83 static void
 84 shell_embedded_window_realize (GtkWidget *widget)
 85 {
 86   ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
 87 
 88   GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->realize (widget);
 89 
 90 
 91   /* Using XReparentWindow() is simpler than using gdk_window_reparent(),
 92    * since it avoids maybe having to create a new foreign GDK window for
 93    * the stage. However, GDK will be left thinking that the parent of
 94    * window->window is the root window - it's not immediately clear
 95    * to me whether that is more or less likely to cause problems than
 96    * modifying the GDK hierarchy.
 97    */
 98   XReparentWindow (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)),
 99                    gdk_x11_window_get_xid (gtk_widget_get_window (widget)),
100                    window->priv->stage_xwindow,
101                    window->priv->position.x, window->priv->position.y);
102 }
103 
104 static gboolean
105 shell_embedded_window_configure_event (GtkWidget         *widget,
106                                        GdkEventConfigure *event)
107 {
108   /* Normally a configure event coming back from X triggers the
109    * resizing logic inside GtkWindow; we just ignore them
110    * since we are handling the resizing logic separately.
111    */
112   return FALSE;
113 }
114 
115 static void
116 shell_embedded_window_check_resize (GtkContainer *container)
117 {
118   ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (container);
119 
120   /* Check resize is called when a resize is queued on something
121    * inside the GtkWindow; we need to make sure that in response
122    * to this gtk_widget_size_request() and then
123    * gtk_widget_size_allocate() are called; we defer to the Clutter
124    * logic and assume it will do the right thing.
125    */
126   if (window->priv->actor)
127     clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
128 }
129 
130 static void
131 shell_embedded_window_set_property (GObject         *object,
132                                     guint            prop_id,
133                                     const GValue    *value,
134                                     GParamSpec      *pspec)
135 {
136   ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (object);
137 
138   switch (prop_id)
139     {
140     case PROP_STAGE:
141       window->priv->stage_xwindow =
142         clutter_x11_get_stage_window (g_value_get_object (value));
143       break;
144 
145     default:
146       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147       break;
148     }
149 }
150 
151 static GObject *
152 shell_embedded_window_constructor (GType                  gtype,
153                                    guint                  n_properties,
154                                    GObjectConstructParam *properties)
155 {
156   GObject *object;
157   GObjectClass *parent_class;
158 
159   parent_class = G_OBJECT_CLASS (shell_embedded_window_parent_class);
160   object = parent_class->constructor (gtype, n_properties, properties);
161 
162   /* Setting the resize mode to immediate means that calling queue_resize()
163    * on a widget within the window will immmediately call check_resize()
164    * to be called, instead of having it queued to an idle. From our perspective,
165    * this is ideal since we just are going to queue a resize to Clutter's
166    * idle resize anyways.
167    */
168   g_object_set (object,
169                 "resize-mode", GTK_RESIZE_IMMEDIATE,
170                 "type", GTK_WINDOW_POPUP,
171                 NULL);
172 
173   return object;
174 }
175 
176 static void
177 shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
178 {
179   GObjectClass *object_class = G_OBJECT_CLASS (klass);
180   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
181   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
182 
183   g_type_class_add_private (klass, sizeof (ShellEmbeddedWindowPrivate));
184 
185   object_class->set_property    = shell_embedded_window_set_property;
186   object_class->constructor     = shell_embedded_window_constructor;
187 
188   widget_class->show            = shell_embedded_window_show;
189   widget_class->hide            = shell_embedded_window_hide;
190   widget_class->realize         = shell_embedded_window_realize;
191   widget_class->configure_event = shell_embedded_window_configure_event;
192 
193   container_class->check_resize    = shell_embedded_window_check_resize;
194 
195   g_object_class_install_property (object_class,
196                                    PROP_STAGE,
197                                    g_param_spec_object ("stage",
198                                                         "Stage",
199                                                         "ClutterStage to embed on",
200                                                         CLUTTER_TYPE_STAGE,
201                                                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
202 }
203 
204 static void
205 shell_embedded_window_init (ShellEmbeddedWindow *window)
206 {
207   window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, SHELL_TYPE_EMBEDDED_WINDOW,
208                                               ShellEmbeddedWindowPrivate);
209 }
210 
211 /*
212  * Private routines called by ShellGtkEmbed
213  */
214 
215 void
216 _shell_embedded_window_set_actor (ShellEmbeddedWindow  *window,
217                                   ShellGtkEmbed        *actor)
218 
219 {
220   g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
221 
222   window->priv->actor = actor;
223 
224   if (actor &&
225       CLUTTER_ACTOR_IS_MAPPED (actor) &&
226       gtk_widget_get_visible (GTK_WIDGET (window)))
227     gtk_widget_map (GTK_WIDGET (window));
228 }
229 
230 void
231 _shell_embedded_window_allocate (ShellEmbeddedWindow *window,
232                                  int                  x,
233                                  int                  y,
234                                  int                  width,
235                                  int                  height)
236 {
237   GtkAllocation allocation;
238 
239   g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
240 
241   if (window->priv->position.x == x &&
242       window->priv->position.y == y &&
243       window->priv->position.width == width &&
244       window->priv->position.height == height)
245     return;
246 
247   window->priv->position.x = x;
248   window->priv->position.y = y;
249   window->priv->position.width = width;
250   window->priv->position.height = height;
251 
252   if (gtk_widget_get_realized (GTK_WIDGET (window)))
253     gdk_window_move_resize (gtk_widget_get_window (GTK_WIDGET (window)),
254                             x, y, width, height);
255 
256   allocation.x = 0;
257   allocation.y = 0;
258   allocation.width = width;
259   allocation.height = height;
260 
261   gtk_widget_size_allocate (GTK_WIDGET (window), &allocation);
262 }
263 
264 void
265 _shell_embedded_window_map (ShellEmbeddedWindow *window)
266 {
267   g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
268 
269   if (gtk_widget_get_visible (GTK_WIDGET (window)))
270     gtk_widget_map (GTK_WIDGET (window));
271 }
272 
273 void
274 _shell_embedded_window_unmap (ShellEmbeddedWindow *window)
275 {
276   g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
277 
278   gtk_widget_unmap (GTK_WIDGET (window));
279 }
280 
281 /*
282  * Public API
283  */
284 GtkWidget *
285 shell_embedded_window_new (ClutterStage *stage)
286 {
287   return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW,
288                        "stage", stage,
289                        NULL);
290 }