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 }