1 /* gtk-clutter-embed.c: Embeddable ClutterStage
2 *
3 * Copyright (C) 2007 OpenedHand
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not see <http://www.fsf.org/licensing>.
17 *
18 * Authors:
19 * Iain Holmes <iain@openedhand.com>
20 * Emmanuele Bassi <ebassi@openedhand.com>
21 */
22
23 /**
24 * SECTION:gtk-clutter-embed
25 * @Title: GtkClutterEmbed
26 * @short_description: Widget for embedding a Clutter scene
27 * @See_Also: #ClutterStage
28 *
29 * #GtkClutterEmbed is a GTK+ widget embedding a #ClutterStage inside
30 * a GTK+ application.
31 *
32 * By using a #GtkClutterEmbed widget is possible to build, show and
33 * interact with a scene built using Clutter inside a GTK+ application.
34 *
35 * ## Event handling with GtkClutterEmbed
36 *
37 * Due to re-entrancy concerns, you should not use GTK event-related
38 * API from within event handling signals emitted by Clutter actors
39 * inside a #GtkClutterEmbed.
40 *
41 * Event-related API, like the GTK drag and drop functions, or the
42 * GTK grab ones, cause events to be processed inside the GDK event
43 * loop; #GtkClutterEmbed and the Clutter event loop may use those
44 * events to generate Clutter events, and thus emit signals on
45 * #ClutterActors. If you use the event-related signals of a
46 * #ClutterActor to call the GTK API, one of the two event loops
47 * will try to re-enter into each other, and either cause a crash
48 * or simply block your application.
49 *
50 * To avoid this behavior, you can either:
51 *
52 * - only use GTK+ event handling signals to call event-related
53 * GTK functions
54 * - let the main loop re-enter, by calling event-related GTK
55 * functions from within an idle or a timeout callback
56 *
57 * You should also make sure you're not using GTK widgets that call
58 * event-related GTK API, like the grab functions in a #GtkMenu, in
59 * response to Clutter actor events.
60 *
61 * ## Using GtkClutterEmbed as a container
62 *
63 * Though #GtkClutterEmbed is a #GtkContainer subclass, it is not a
64 * real GTK+ container; #GtkClutterEmbed is required to implement the
65 * #GtkContainer virtual functions in order to embed a #GtkWidget
66 * through the #GtkClutterActor class. Calling gtk_container_add()
67 * on a #GtkClutterEmbed will trigger an assertion. It is strongly
68 * advised not to override the #GtkContainer implementation when
69 * subclassing #GtkClutterEmbed, to avoid breaking internal state.
70 */
71
72 #include "config.h"
73
74 #include <math.h>
75 #include <string.h>
76 #include "gtk-clutter-embed.h"
77 #include "gtk-clutter-offscreen.h"
78 #include "gtk-clutter-actor.h"
79
80 #include <glib-object.h>
81
82 #include <gdk/gdk.h>
83
84 #if defined(CLUTTER_WINDOWING_X11)
85 #include <clutter/x11/clutter-x11.h>
86 #endif
87
88 #if defined(CLUTTER_WINDOWING_GDK)
89 #include <clutter/gdk/clutter-gdk.h>
90 #endif
91
92 #if defined(CLUTTER_WINDOWING_WIN32)
93 #include <clutter/win32/clutter-win32.h>
94 #endif
95
96 #if defined(CLUTTER_WINDOWING_WAYLAND)
97 #include <clutter/wayland/clutter-wayland.h>
98 #endif
99
100 #if defined(GDK_WINDOWING_X11)
101 #include <gdk/gdkx.h>
102 #endif
103
104 #if defined(GDK_WINDOWING_WIN32)
105 #include <gdk/gdkwin32.h>
106 #endif
107
108 #if defined(GDK_WINDOWING_WAYLAND)
109 #include <gdk/gdkwayland.h>
110 #endif
111
112 struct _GtkClutterEmbedPrivate
113 {
114 ClutterActor *stage;
115
116 GList *children;
117 int n_active_children;
118
119 guint queue_redraw_id;
120 guint queue_relayout_id;
121
122 guint geometry_changed : 1;
123 guint use_layout_size : 1;
124
125 #if defined(GDK_WINDOWING_WAYLAND) && defined(CLUTTER_WINDOWING_WAYLAND)
126 struct wl_subcompositor *subcompositor;
127 struct wl_surface *clutter_surface;
128 struct wl_subsurface *subsurface;
129 #endif
130 };
131
132 static gint num_filter = 0;
133
134 enum
135 {
136 PROP_0,
137
138 PROP_USE_LAYOUT_SIZE
139 };
140
G_DEFINE_TYPE_WITH_PRIVATE(GtkClutterEmbed,gtk_clutter_embed,GTK_TYPE_CONTAINER)141 G_DEFINE_TYPE_WITH_PRIVATE (GtkClutterEmbed, gtk_clutter_embed, GTK_TYPE_CONTAINER)
142
143 static void
144 gtk_clutter_embed_send_configure (GtkClutterEmbed *embed)
145 {
146 GtkWidget *widget;
147 GtkAllocation allocation;
148 GdkEvent *event = gdk_event_new (GDK_CONFIGURE);
149
150 widget = GTK_WIDGET (embed);
151 gtk_widget_get_allocation (widget, &allocation);
152
153 event->configure.window = g_object_ref (gtk_widget_get_window (widget));
154 event->configure.send_event = TRUE;
155 event->configure.x = allocation.x;
156 event->configure.y = allocation.y;
157 event->configure.width = allocation.width;
158 event->configure.height = allocation.height;
159
160 gtk_widget_event (widget, event);
161 gdk_event_free (event);
162 }
163
164 #if defined(GDK_WINDOWING_WAYLAND) && defined(CLUTTER_WINDOWING_WAYLAND)
165 static void
gtk_clutter_embed_ensure_surface(GtkClutterEmbed * embed)166 gtk_clutter_embed_ensure_surface (GtkClutterEmbed *embed)
167 {
168 GtkClutterEmbedPrivate *priv = embed->priv;
169
170 if (priv->subcompositor && !priv->clutter_surface)
171 {
172 GdkDisplay *display;
173 struct wl_compositor *compositor;
174
175 display = gtk_widget_get_display (GTK_WIDGET (embed));
176 compositor = gdk_wayland_display_get_wl_compositor (display);
177 priv->clutter_surface = wl_compositor_create_surface (compositor);
178 }
179 }
180
181 static void
gtk_clutter_embed_ensure_subsurface(GtkClutterEmbed * embed)182 gtk_clutter_embed_ensure_subsurface (GtkClutterEmbed *embed)
183 {
184 GtkClutterEmbedPrivate *priv;
185 GtkWidget *widget;
186 struct wl_surface *gtk_surface;
187 GdkWindow *window;
188 gint x, y;
189
190 widget = GTK_WIDGET (embed);
191 priv = embed->priv;
192
193 if (priv->subsurface)
194 return;
195
196 window = gtk_widget_get_window (widget);
197 gtk_surface = gdk_wayland_window_get_wl_surface (gdk_window_get_toplevel (window));
198 priv->subsurface =
199 wl_subcompositor_get_subsurface (priv->subcompositor,
200 priv->clutter_surface,
201 gtk_surface);
202
203 gdk_window_get_origin (window, &x, &y);
204 wl_subsurface_set_position (priv->subsurface, x, y);
205 wl_subsurface_set_desync (priv->subsurface);
206 }
207 #endif
208
209 static void
gtk_clutter_embed_ensure_stage_realized(GtkClutterEmbed * embed)210 gtk_clutter_embed_ensure_stage_realized (GtkClutterEmbed *embed)
211 {
212 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (embed)->priv;
213
214 if (!gtk_widget_get_realized (GTK_WIDGET (embed)))
215 return;
216
217 if (!clutter_actor_is_realized (priv->stage))
218 {
219 GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (embed));
220
221 #if defined(CLUTTER_WINDOWING_GDK)
222 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_GDK))
223 {
224 clutter_gdk_set_stage_foreign (CLUTTER_STAGE (priv->stage), window);
225 }
226 else
227 #endif
228 #if defined(GDK_WINDOWING_X11) && defined(CLUTTER_WINDOWING_X11)
229 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11) &&
230 GDK_IS_X11_WINDOW (window))
231 {
232 clutter_x11_set_stage_foreign (CLUTTER_STAGE (priv->stage),
233 GDK_WINDOW_XID (window));
234 }
235 else
236 #endif
237 #if defined(GDK_WINDOWING_WIN32) && defined(CLUTTER_WINDOWING_WIN32)
238 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_WIN32) &&
239 GDK_IS_WIN32_WINDOW (window))
240 {
241 clutter_win32_set_stage_foreign (CLUTTER_STAGE (priv->stage),
242 GDK_WINDOW_HWND (window));
243 }
244 else
245 #endif
246 #if defined(GDK_WINDOWING_WAYLAND) && defined (CLUTTER_WINDOWING_WAYLAND)
247 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_WAYLAND) &&
248 GDK_IS_WAYLAND_WINDOW (window))
249 {
250 gtk_clutter_embed_ensure_surface (embed);
251 clutter_wayland_stage_set_wl_surface (CLUTTER_STAGE (priv->stage),
252 priv->clutter_surface);
253 }
254 else
255 #endif
256 {
257 g_warning ("No backend found!");
258 }
259
260 clutter_actor_realize (priv->stage);
261 }
262
263 /* A stage cannot really be unmapped because it is the top of
264 * Clutter's scene tree. So if the Gtk embedder is mapped, we
265 * translate this as visible for the ClutterStage. */
266 if (gtk_widget_get_mapped (GTK_WIDGET (embed)))
267 clutter_actor_show (priv->stage);
268
269 clutter_actor_queue_relayout (priv->stage);
270
271 gtk_clutter_embed_send_configure (embed);
272
273 #if defined(GDK_WINDOWING_WAYLAND) && defined (CLUTTER_WINDOWING_WAYLAND)
274 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_WAYLAND))
275 gtk_clutter_embed_ensure_subsurface (embed);
276 #endif
277 }
278
279 static void
gtk_clutter_embed_stage_unrealize(GtkClutterEmbed * embed)280 gtk_clutter_embed_stage_unrealize (GtkClutterEmbed *embed)
281 {
282 GtkClutterEmbedPrivate *priv = embed->priv;
283
284 #if defined(GDK_WINDOWING_WAYLAND) && defined(CLUTTER_WINDOWING_WAYLAND)
285 g_clear_pointer (&priv->subsurface, wl_subsurface_destroy);
286 g_clear_pointer (&priv->clutter_surface, wl_surface_destroy);
287 #endif
288
289 /* gtk may emit an unmap signal after dispose, so it's possible we
290 * may have already disposed priv->stage. */
291 if (priv->stage != NULL)
292 {
293 clutter_actor_hide (priv->stage);
294 clutter_actor_unrealize (priv->stage);
295 }
296 }
297
298 static void
on_stage_queue_redraw(ClutterStage * stage,ClutterActor * origin,gpointer user_data)299 on_stage_queue_redraw (ClutterStage *stage,
300 ClutterActor *origin,
301 gpointer user_data)
302 {
303 GtkWidget *embed = user_data;
304 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (embed)->priv;
305
306 if (priv->n_active_children > 0)
307 priv->geometry_changed = TRUE;
308
309 gtk_widget_queue_draw (embed);
310 }
311
312 static void
on_stage_queue_relayout(ClutterStage * stage,gpointer user_data)313 on_stage_queue_relayout (ClutterStage *stage,
314 gpointer user_data)
315 {
316 GtkWidget *embed = user_data;
317 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (embed)->priv;
318
319 if (priv->use_layout_size)
320 gtk_widget_queue_resize (embed);
321 }
322
323 static void
gtk_clutter_embed_dispose(GObject * gobject)324 gtk_clutter_embed_dispose (GObject *gobject)
325 {
326 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (gobject)->priv;
327
328
329 if (priv->stage)
330 {
331 if (priv->queue_redraw_id)
332 g_signal_handler_disconnect (priv->stage, priv->queue_redraw_id);
333
334 if (priv->queue_relayout_id)
335 g_signal_handler_disconnect (priv->stage, priv->queue_relayout_id);
336
337 priv->queue_redraw_id = 0;
338 priv->queue_relayout_id = 0;
339
340 clutter_actor_destroy (priv->stage);
341 priv->stage = NULL;
342
343 #if defined(GDK_WINDOWING_WAYLAND) && defined(CLUTTER_WINDOWING_WAYLAND)
344 g_clear_pointer (&priv->subsurface, wl_subsurface_destroy);
345 #endif
346 }
347
348 G_OBJECT_CLASS (gtk_clutter_embed_parent_class)->dispose (gobject);
349 }
350
351 static void
gtk_clutter_embed_show(GtkWidget * widget)352 gtk_clutter_embed_show (GtkWidget *widget)
353 {
354 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->show (widget);
355
356 gtk_clutter_embed_ensure_stage_realized (GTK_CLUTTER_EMBED (widget));
357 }
358
359 static GdkWindow *
pick_embedded_child(GdkWindow * offscreen_window,double widget_x,double widget_y,GtkClutterEmbed * embed)360 pick_embedded_child (GdkWindow *offscreen_window,
361 double widget_x,
362 double widget_y,
363 GtkClutterEmbed *embed)
364 {
365 GtkClutterEmbedPrivate *priv = embed->priv;
366 ClutterActor *a;
367 GtkWidget *widget;
368
369 a = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (priv->stage),
370 CLUTTER_PICK_REACTIVE,
371 widget_x, widget_y);
372 if (GTK_CLUTTER_IS_ACTOR (a))
373 {
374 widget = gtk_clutter_actor_get_widget (GTK_CLUTTER_ACTOR (a));
375
376 if (GTK_CLUTTER_OFFSCREEN (widget)->active)
377 return gtk_widget_get_window (widget);
378 }
379
380 return NULL;
381 }
382
383 static GdkFilterReturn
gtk_clutter_filter_func(GdkXEvent * native_event,GdkEvent * event G_GNUC_UNUSED,gpointer user_data G_GNUC_UNUSED)384 gtk_clutter_filter_func (GdkXEvent *native_event,
385 GdkEvent *event G_GNUC_UNUSED,
386 gpointer user_data G_GNUC_UNUSED)
387 {
388 #if defined(CLUTTER_WINDOWING_X11)
389 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11))
390 {
391 XEvent *xevent = native_event;
392
393 /* let Clutter handle all events coming from the windowing system */
394 clutter_x11_handle_event (xevent);
395 }
396 else
397 #endif
398 #if defined(CLUTTER_WINDOWING_WIN32)
399 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_WIN32))
400 {
401 MSG *msg = native_event;
402
403 clutter_win32_handle_event (msg);
404 }
405 else
406 #endif
407 g_critical ("Unsuppored Clutter backend");
408
409 /* we don't care if Clutter handled the event: we want GDK to continue
410 * the event processing as usual
411 */
412 return GDK_FILTER_CONTINUE;
413 }
414
415 static gboolean
gtk_clutter_embed_draw(GtkWidget * widget,cairo_t * cr)416 gtk_clutter_embed_draw (GtkWidget *widget, cairo_t *cr)
417 {
418 #if defined(CLUTTER_WINDOWING_GDK)
419 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
420
421 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_GDK))
422 clutter_stage_ensure_redraw (CLUTTER_STAGE (priv->stage));
423 #endif
424
425 return GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->draw (widget, cr);
426 }
427
428 static void
gtk_clutter_embed_realize(GtkWidget * widget)429 gtk_clutter_embed_realize (GtkWidget *widget)
430 {
431 GtkAllocation allocation;
432 GtkStyleContext *style_context;
433 GdkWindow *window;
434 GdkWindowAttr attributes;
435 gint attributes_mask;
436 gint border_width;
437
438 #if defined(CLUTTER_WINDOWING_GDK)
439 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_GDK))
440 {
441 GdkVisual *visual = clutter_gdk_get_visual ();
442 gtk_widget_set_visual (widget, visual);
443 }
444 #endif
445 #if defined(GDK_WINDOWING_X11) && defined(CLUTTER_WINDOWING_X11)
446 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11))
447 {
448 const XVisualInfo *xvinfo;
449 GdkVisual *visual;
450
451 /* We need to use the colormap from the Clutter visual, since
452 * the visual is tied to the GLX context
453 */
454 xvinfo = clutter_x11_get_visual_info ();
455 if (xvinfo == None)
456 {
457 g_critical ("Unable to retrieve the XVisualInfo from Clutter");
458 return;
459 }
460
461 visual = gdk_x11_screen_lookup_visual (gtk_widget_get_screen (widget),
462 xvinfo->visualid);
463 gtk_widget_set_visual (widget, visual);
464 }
465 #endif
466
467 gtk_widget_set_realized (widget, TRUE);
468
469 gtk_widget_get_allocation (widget, &allocation);
470 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
471
472 attributes.window_type = GDK_WINDOW_CHILD;
473 attributes.x = allocation.x + border_width;
474 attributes.y = allocation.y + border_width;
475 attributes.width = allocation.width - 2 * border_width;
476 attributes.height = allocation.height - 2 * border_width;
477 attributes.wclass = GDK_INPUT_OUTPUT;
478 attributes.visual = gtk_widget_get_visual (widget);
479
480 /* NOTE: GDK_MOTION_NOTIFY above should be safe as Clutter does its own
481 * throttling.
482 */
483 attributes.event_mask = gtk_widget_get_events (widget)
484 | GDK_EXPOSURE_MASK
485 | GDK_SCROLL_MASK
486 | GDK_BUTTON_PRESS_MASK
487 | GDK_BUTTON_RELEASE_MASK
488 | GDK_KEY_PRESS_MASK
489 | GDK_KEY_RELEASE_MASK
490 | GDK_POINTER_MOTION_MASK
491 | GDK_ENTER_NOTIFY_MASK
492 | GDK_LEAVE_NOTIFY_MASK
493 | GDK_TOUCH_MASK
494 | GDK_SMOOTH_SCROLL_MASK
495 | GDK_STRUCTURE_MASK;
496
497 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
498
499 window = gdk_window_new (gtk_widget_get_parent_window (widget),
500 &attributes,
501 attributes_mask);
502
503 gtk_widget_set_window (widget, window);
504 gdk_window_set_user_data (window, widget);
505
506 /* this does the translation of the event from Clutter to GDK
507 * we embedding a GtkWidget inside a GtkClutterActor
508 */
509 g_signal_connect (window, "pick-embedded-child",
510 G_CALLBACK (pick_embedded_child),
511 widget);
512
513 style_context = gtk_widget_get_style_context (widget);
514 gtk_style_context_set_background (style_context, window);
515
516 #if defined(GDK_WINDOWING_X11) && defined(CLUTTER_WINDOWING_X11)
517 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11) &&
518 GDK_IS_X11_WINDOW (window))
519 {
520 if (num_filter == 0)
521 gdk_window_add_filter (NULL, gtk_clutter_filter_func, widget);
522 num_filter++;
523 }
524 else
525 #endif
526 #if defined(GDK_WINDOWING_WIN32) && defined(CLUTTER_WINDOWING_WIN32)
527 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_WIN32) &&
528 GDK_IS_WIN32_WINDOW (window))
529 {
530 if (num_filter == 0)
531 gdk_window_add_filter (NULL, gtk_clutter_filter_func, widget);
532 num_filter++;
533 }
534 else
535 #endif
536 {
537 /* Nothing to do. */
538 }
539
540 gtk_clutter_embed_ensure_stage_realized (GTK_CLUTTER_EMBED (widget));
541 }
542
543 static void
gtk_clutter_embed_unrealize(GtkWidget * widget)544 gtk_clutter_embed_unrealize (GtkWidget *widget)
545 {
546 GtkClutterEmbed *embed = GTK_CLUTTER_EMBED (widget);
547
548 if (num_filter > 0)
549 {
550 num_filter--;
551 if (num_filter == 0)
552 gdk_window_remove_filter (NULL, gtk_clutter_filter_func, widget);
553 }
554
555 gtk_clutter_embed_stage_unrealize (embed);
556
557 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->unrealize (widget);
558 }
559
560 static GtkSizeRequestMode
gtk_clutter_embed_get_request_mode(GtkWidget * widget)561 gtk_clutter_embed_get_request_mode (GtkWidget *widget)
562 {
563 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
564 GtkSizeRequestMode mode;
565
566 mode = GTK_SIZE_REQUEST_CONSTANT_SIZE;
567 if (priv->stage != NULL &&
568 priv->use_layout_size &&
569 clutter_actor_get_layout_manager (priv->stage) != NULL)
570 {
571 switch (clutter_actor_get_request_mode (priv->stage))
572 {
573 case CLUTTER_REQUEST_HEIGHT_FOR_WIDTH:
574 mode = GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
575 break;
576 case CLUTTER_REQUEST_WIDTH_FOR_HEIGHT:
577 mode = GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
578 break;
579 case CLUTTER_REQUEST_CONTENT_SIZE:
580 mode = GTK_SIZE_REQUEST_CONSTANT_SIZE;
581 break;
582 }
583 }
584
585 return mode;
586 }
587
588 static void
gtk_clutter_embed_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum,gint * natural)589 gtk_clutter_embed_get_preferred_width_for_height (GtkWidget *widget,
590 gint height,
591 gint *minimum,
592 gint *natural)
593 {
594 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
595 float min, nat;
596
597 min = 0;
598 nat = 0;
599
600 if (priv->stage != NULL &&
601 priv->use_layout_size)
602 {
603 ClutterLayoutManager *manager = clutter_actor_get_layout_manager (priv->stage);
604 if (manager)
605 clutter_layout_manager_get_preferred_width (manager,
606 CLUTTER_CONTAINER (priv->stage),
607 (float)height, &min, &nat);
608 }
609
610 min = ceilf (min);
611 nat = ceilf (nat);
612
613 if (minimum)
614 *minimum = min;
615
616 if (natural)
617 *natural = nat;
618 }
619
620 static void
gtk_clutter_embed_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural)621 gtk_clutter_embed_get_preferred_height_for_width (GtkWidget *widget,
622 gint width,
623 gint *minimum,
624 gint *natural)
625 {
626 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
627 float min, nat;
628
629 min = 0;
630 nat = 0;
631
632 if (priv->stage != NULL &&
633 priv->use_layout_size)
634 {
635 ClutterLayoutManager *manager = clutter_actor_get_layout_manager (priv->stage);
636 if (manager)
637 clutter_layout_manager_get_preferred_height (manager,
638 CLUTTER_CONTAINER (priv->stage),
639 (float)width, &min, &nat);
640 }
641
642 min = ceilf (min);
643 nat = ceilf (nat);
644
645 if (minimum)
646 *minimum = min;
647
648 if (natural)
649 *natural = nat;
650 }
651
652 static void
gtk_clutter_embed_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)653 gtk_clutter_embed_get_preferred_width (GtkWidget *widget,
654 gint *minimum,
655 gint *natural)
656 {
657 gtk_clutter_embed_get_preferred_width_for_height (widget, -1, minimum, natural);
658 }
659
660 static void
gtk_clutter_embed_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)661 gtk_clutter_embed_get_preferred_height (GtkWidget *widget,
662 gint *minimum,
663 gint *natural)
664 {
665 gtk_clutter_embed_get_preferred_height_for_width (widget, -1, minimum, natural);
666 }
667
668 static void
gtk_clutter_embed_size_allocate(GtkWidget * widget,GtkAllocation * allocation)669 gtk_clutter_embed_size_allocate (GtkWidget *widget,
670 GtkAllocation *allocation)
671 {
672 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
673 int scale_factor = gtk_widget_get_scale_factor (widget);
674
675 gtk_widget_set_allocation (widget, allocation);
676
677 /* change the size of the stage and ensure that the viewport
678 * has been updated as well
679 */
680 clutter_actor_set_size (priv->stage, allocation->width, allocation->height);
681
682 if (gtk_widget_get_realized (widget))
683 {
684 gdk_window_move_resize (gtk_widget_get_window (widget),
685 allocation->x,
686 allocation->y,
687 allocation->width,
688 allocation->height);
689
690 clutter_stage_ensure_viewport (CLUTTER_STAGE (priv->stage));
691
692 gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
693
694 #if defined(GDK_WINDOWING_X11) && defined(CLUTTER_WINDOWING_X11)
695 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11) &&
696 GDK_IS_X11_WINDOW (gtk_widget_get_window (widget)))
697 {
698 XConfigureEvent xevent = { ConfigureNotify };
699 xevent.window = GDK_WINDOW_XID (gtk_widget_get_window (widget));
700 xevent.width = allocation->width * scale_factor;
701 xevent.height = allocation->height * scale_factor;
702
703 /* Ensure cogl knows about the new size immediately, as we will
704 draw before we get the ConfigureNotify response. */
705 clutter_x11_handle_event ((XEvent *)&xevent);
706 }
707 #endif
708 #if defined(GDK_WINDOWING_WAYLAND) && defined(CLUTTER_WINDOWING_WAYLAND)
709 if (priv->subsurface)
710 {
711 gint x, y;
712 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
713 wl_subsurface_set_position (priv->subsurface, x, y);
714 }
715 #endif
716 }
717 }
718
719 static gboolean
gtk_clutter_embed_map_event(GtkWidget * widget,GdkEventAny * event)720 gtk_clutter_embed_map_event (GtkWidget *widget,
721 GdkEventAny *event)
722 {
723 GtkClutterEmbed *embed = GTK_CLUTTER_EMBED (widget);
724 GtkClutterEmbedPrivate *priv = embed->priv;
725 GtkWidgetClass *parent_class;
726 gboolean res = FALSE;
727
728 parent_class = GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class);
729 if (parent_class->map_event)
730 res = parent_class->map_event (widget, event);
731
732 gtk_clutter_embed_ensure_stage_realized (embed);
733
734 clutter_actor_queue_redraw (priv->stage);
735
736 return res;
737 }
738
739 static gboolean
gtk_clutter_embed_unmap_event(GtkWidget * widget,GdkEventAny * event)740 gtk_clutter_embed_unmap_event (GtkWidget *widget,
741 GdkEventAny *event)
742 {
743 GtkClutterEmbed *embed = GTK_CLUTTER_EMBED (widget);
744 GtkWidgetClass *parent_class;
745 gboolean res = FALSE;
746
747 parent_class = GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class);
748 if (parent_class->unmap_event)
749 res = parent_class->unmap_event (widget, event);
750
751 gtk_clutter_embed_stage_unrealize (embed);
752
753 return res;
754 }
755
756 static gboolean
gtk_clutter_embed_focus_in(GtkWidget * widget,GdkEventFocus * event)757 gtk_clutter_embed_focus_in (GtkWidget *widget,
758 GdkEventFocus *event)
759 {
760 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
761
762 g_signal_emit_by_name (priv->stage, "activate");
763
764 clutter_stage_set_key_focus (CLUTTER_STAGE (priv->stage), NULL);
765
766 return FALSE;
767 }
768
769 static gboolean
gtk_clutter_embed_focus_out(GtkWidget * widget,GdkEventFocus * event)770 gtk_clutter_embed_focus_out (GtkWidget *widget,
771 GdkEventFocus *event)
772 {
773 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
774
775 g_signal_emit_by_name (priv->stage, "deactivate");
776
777 /* give back key focus to the stage */
778 clutter_stage_set_key_focus (CLUTTER_STAGE (priv->stage), NULL);
779
780 return FALSE;
781 }
782
783 static gboolean
gtk_clutter_embed_key_event(GtkWidget * widget,GdkEventKey * event)784 gtk_clutter_embed_key_event (GtkWidget *widget,
785 GdkEventKey *event)
786 {
787 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
788 ClutterDeviceManager *manager;
789 ClutterInputDevice *device;
790 ClutterEvent cevent = { 0, };
791
792 if (event->type == GDK_KEY_PRESS)
793 cevent.key.type = CLUTTER_KEY_PRESS;
794 else if (event->type == GDK_KEY_RELEASE)
795 cevent.key.type = CLUTTER_KEY_RELEASE;
796 else
797 return FALSE;
798
799 manager = clutter_device_manager_get_default ();
800 device = clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE);
801
802 cevent.key.stage = CLUTTER_STAGE (priv->stage);
803 cevent.key.time = event->time;
804 cevent.key.modifier_state = event->state;
805 cevent.key.keyval = event->keyval;
806 cevent.key.hardware_keycode = event->hardware_keycode;
807 cevent.key.unicode_value = gdk_keyval_to_unicode (event->keyval);
808 cevent.key.device = device;
809
810 clutter_do_event (&cevent);
811
812 return FALSE;
813 }
814
815 static void
gtk_clutter_embed_style_updated(GtkWidget * widget)816 gtk_clutter_embed_style_updated (GtkWidget *widget)
817 {
818 GdkScreen *screen;
819 GtkSettings *gtk_settings;
820 ClutterSettings *clutter_settings;
821 gchar *font_name;
822 gint double_click_time, double_click_distance;
823 #if defined(GDK_WINDOWING_X11) && defined(CLUTTER_WINDOWING_X11)
824 gint xft_dpi, xft_hinting, xft_antialias;
825 gchar *xft_hintstyle, *xft_rgba;
826 #endif
827
828 if (gtk_widget_get_realized (widget))
829 {
830 #if 0
831 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
832 GtkStyleContext *style_context;
833 GtkStateFlags state_flags;
834 GdkRGBA *bg_color;
835 ClutterColor color;
836
837 style_context = gtk_widget_get_style_context (widget);
838 state_flags = gtk_widget_get_state_flags (widget);
839 gtk_style_context_get (style_context, state_flags,
840 "background-color", &bg_color,
841 NULL);
842
843 color.red = CLAMP (bg_color->red * 255, 0, 255);
844 color.green = CLAMP (bg_color->green * 255, 0, 255);
845 color.blue = CLAMP (bg_color->blue * 255, 0, 255);
846 color.alpha = CLAMP (bg_color->alpha * 255, 0, 255);
847 clutter_stage_set_color (CLUTTER_STAGE (priv->stage), &color);
848
849 gdk_rgba_free (bg_color);
850 #endif
851 }
852
853 if (gtk_widget_has_screen (widget))
854 screen = gtk_widget_get_screen (widget);
855 else
856 screen = gdk_screen_get_default ();
857
858 gtk_settings = gtk_settings_get_for_screen (screen);
859 g_object_get (G_OBJECT (gtk_settings),
860 "gtk-font-name", &font_name,
861 "gtk-double-click-time", &double_click_time,
862 "gtk-double-click-distance", &double_click_distance,
863 NULL);
864
865 #if defined(GDK_WINDOWING_X11) && defined(CLUTTER_WINDOWING_X11)
866 if (GDK_IS_X11_SCREEN (screen))
867 {
868 g_object_get (G_OBJECT (gtk_settings),
869 "gtk-xft-dpi", &xft_dpi,
870 "gtk-xft-antialias", &xft_antialias,
871 "gtk-xft-hinting", &xft_hinting,
872 "gtk-xft-hintstyle", &xft_hintstyle,
873 "gtk-xft-rgba", &xft_rgba,
874 NULL);
875 }
876 #endif
877
878 /* copy all settings and values coming from GTK+ into
879 * the ClutterBackend; this way, a scene embedded into
880 * a GtkClutterEmbed will not look completely alien
881 */
882 clutter_settings = clutter_settings_get_default ();
883
884 #if defined(GDK_WINDOWING_X11) && defined(CLUTTER_WINDOWING_X11)
885 if (GDK_IS_X11_SCREEN (screen))
886 {
887 g_object_set (G_OBJECT (clutter_settings),
888 "font-name", font_name,
889 "double-click-time", double_click_time,
890 "double-click-distance", double_click_distance,
891 "font-antialias", xft_antialias,
892 "font-dpi", xft_dpi,
893 "font-hinting", xft_hinting,
894 "font-hint-style", xft_hintstyle,
895 "font-subpixel-order", xft_rgba,
896 NULL);
897 }
898 else
899 #endif
900 {
901 g_object_set (G_OBJECT (clutter_settings),
902 "font-name", font_name,
903 "double-click-time", double_click_time,
904 "double-click-distance", double_click_distance,
905 NULL);
906 }
907
908 #if defined(GDK_WINDOWING_X11) && defined(CLUTTER_WINDOWING_X11)
909 if (GDK_IS_X11_SCREEN (screen))
910 {
911 g_free (xft_hintstyle);
912 g_free (xft_rgba);
913 }
914 #endif
915
916 g_free (font_name);
917
918 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->style_updated (widget);
919 }
920
921 void
_gtk_clutter_embed_set_child_active(GtkClutterEmbed * embed,GtkWidget * child,gboolean active)922 _gtk_clutter_embed_set_child_active (GtkClutterEmbed *embed,
923 GtkWidget *child,
924 gboolean active)
925 {
926 GdkWindow *child_window;
927
928 child_window = gtk_widget_get_window (child);
929
930 if (active)
931 {
932 embed->priv->n_active_children++;
933 gdk_offscreen_window_set_embedder (child_window,
934 gtk_widget_get_window (GTK_WIDGET (embed)));
935 }
936 else
937 {
938 embed->priv->n_active_children--;
939 gdk_offscreen_window_set_embedder (child_window,
940 NULL);
941 }
942
943 }
944
945 static void
gtk_clutter_embed_add(GtkContainer * container,GtkWidget * widget)946 gtk_clutter_embed_add (GtkContainer *container,
947 GtkWidget *widget)
948 {
949 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
950
951 #ifndef G_DISABLE_ASSERT
952 if (G_UNLIKELY (!GTK_CLUTTER_IS_OFFSCREEN (widget)))
953 {
954 g_critical ("Widgets of type '%s' do not support children.",
955 G_OBJECT_TYPE_NAME (container));
956 return;
957 }
958 #endif
959
960 priv->children = g_list_prepend (priv->children, widget);
961 gtk_widget_set_parent (widget, GTK_WIDGET (container));
962 }
963
964 static void
gtk_clutter_embed_remove(GtkContainer * container,GtkWidget * widget)965 gtk_clutter_embed_remove (GtkContainer *container,
966 GtkWidget *widget)
967 {
968 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
969 GList *l;
970
971 l = g_list_find (priv->children, widget);
972 if (l != NULL)
973 {
974 priv->children = g_list_delete_link (priv->children, l);
975 gtk_widget_unparent (widget);
976 }
977 }
978
979 static void
gtk_clutter_embed_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)980 gtk_clutter_embed_forall (GtkContainer *container,
981 gboolean include_internals,
982 GtkCallback callback,
983 gpointer callback_data)
984 {
985 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
986 GList *l;
987
988 if (include_internals)
989 {
990 for (l = priv->children; l != NULL; l = l->next)
991 callback (l->data, callback_data);
992 }
993 }
994
995 static GType
gtk_clutter_embed_child_type(GtkContainer * container)996 gtk_clutter_embed_child_type (GtkContainer *container)
997 {
998 /* we only accept GtkClutterOffscreen children */
999 return GTK_CLUTTER_TYPE_OFFSCREEN;
1000 }
1001
1002 static gboolean
gtk_clutter_embed_event(GtkWidget * widget,GdkEvent * event)1003 gtk_clutter_embed_event (GtkWidget *widget,
1004 GdkEvent *event)
1005 {
1006 #if defined(CLUTTER_WINDOWING_GDK)
1007 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_GDK))
1008 clutter_gdk_handle_event (event);
1009 #endif
1010
1011 return FALSE;
1012 }
1013
1014 static void
gtk_clutter_embed_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)1015 gtk_clutter_embed_set_property (GObject *gobject,
1016 guint prop_id,
1017 const GValue *value,
1018 GParamSpec *pspec)
1019 {
1020 GtkClutterEmbed *embed = GTK_CLUTTER_EMBED (gobject);
1021
1022 switch (prop_id)
1023 {
1024 case PROP_USE_LAYOUT_SIZE:
1025 gtk_clutter_embed_set_use_layout_size (embed, g_value_get_boolean (value));
1026 break;
1027
1028 default:
1029 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1030 break;
1031 }
1032 }
1033
1034 static void
gtk_clutter_embed_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)1035 gtk_clutter_embed_get_property (GObject *gobject,
1036 guint prop_id,
1037 GValue *value,
1038 GParamSpec *pspec)
1039 {
1040 GtkClutterEmbed *embed = GTK_CLUTTER_EMBED (gobject);
1041
1042 switch (prop_id)
1043 {
1044 case PROP_USE_LAYOUT_SIZE:
1045 g_value_set_boolean (value, embed->priv->use_layout_size);
1046 break;
1047
1048 default:
1049 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1050 break;
1051 }
1052 }
1053
1054 static void
gtk_clutter_embed_class_init(GtkClutterEmbedClass * klass)1055 gtk_clutter_embed_class_init (GtkClutterEmbedClass *klass)
1056 {
1057 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1058 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1059 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
1060 GParamSpec *pspec;
1061
1062 gobject_class->dispose = gtk_clutter_embed_dispose;
1063 gobject_class->set_property = gtk_clutter_embed_set_property;
1064 gobject_class->get_property = gtk_clutter_embed_get_property;
1065
1066 widget_class->style_updated = gtk_clutter_embed_style_updated;
1067 widget_class->size_allocate = gtk_clutter_embed_size_allocate;
1068 widget_class->draw = gtk_clutter_embed_draw;
1069 widget_class->realize = gtk_clutter_embed_realize;
1070 widget_class->unrealize = gtk_clutter_embed_unrealize;
1071 widget_class->show = gtk_clutter_embed_show;
1072 widget_class->map_event = gtk_clutter_embed_map_event;
1073 widget_class->unmap_event = gtk_clutter_embed_unmap_event;
1074 widget_class->focus_in_event = gtk_clutter_embed_focus_in;
1075 widget_class->focus_out_event = gtk_clutter_embed_focus_out;
1076 widget_class->key_press_event = gtk_clutter_embed_key_event;
1077 widget_class->key_release_event = gtk_clutter_embed_key_event;
1078 widget_class->event = gtk_clutter_embed_event;
1079 widget_class->get_request_mode = gtk_clutter_embed_get_request_mode;
1080 widget_class->get_preferred_width = gtk_clutter_embed_get_preferred_width;
1081 widget_class->get_preferred_height = gtk_clutter_embed_get_preferred_height;
1082 widget_class->get_preferred_width_for_height = gtk_clutter_embed_get_preferred_width_for_height;
1083 widget_class->get_preferred_height_for_width = gtk_clutter_embed_get_preferred_height_for_width;
1084
1085 container_class->add = gtk_clutter_embed_add;
1086 container_class->remove = gtk_clutter_embed_remove;
1087 container_class->forall = gtk_clutter_embed_forall;
1088 container_class->child_type = gtk_clutter_embed_child_type;
1089
1090
1091 /**
1092 * GtkClutterEmbed:use-layout-size:
1093 *
1094 * The #GtkWidget to be embedded into the #GtkClutterActor
1095 *
1096 * Since: 1.4
1097 */
1098 pspec = g_param_spec_boolean ("use-layout-size",
1099 "Use layout size",
1100 "Whether to use the reported size of the LayoutManager on the stage as the widget size.",
1101 FALSE,
1102 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1103 g_object_class_install_property (gobject_class, PROP_USE_LAYOUT_SIZE, pspec);
1104 }
1105
1106 #if defined(GDK_WINDOWING_WAYLAND) && defined(CLUTTER_WINDOWING_WAYLAND)
1107 static void
registry_handle_global(void * data,struct wl_registry * registry,uint32_t name,const char * interface,uint32_t version)1108 registry_handle_global (void *data,
1109 struct wl_registry *registry,
1110 uint32_t name,
1111 const char *interface,
1112 uint32_t version)
1113 {
1114 GtkClutterEmbed *embed = data;
1115 GtkClutterEmbedPrivate *priv = embed->priv;
1116
1117 if (strcmp (interface, "wl_subcompositor") == 0)
1118 {
1119 priv->subcompositor = wl_registry_bind (registry,
1120 name,
1121 &wl_subcompositor_interface,
1122 1);
1123 }
1124 }
1125
1126 static void
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)1127 registry_handle_global_remove (void *data,
1128 struct wl_registry *registry,
1129 uint32_t name)
1130 {
1131 }
1132
1133 static const struct wl_registry_listener registry_listener = {
1134 registry_handle_global,
1135 registry_handle_global_remove
1136 };
1137 #endif
1138
1139 static void
gtk_clutter_embed_init(GtkClutterEmbed * embed)1140 gtk_clutter_embed_init (GtkClutterEmbed *embed)
1141 {
1142 GtkClutterEmbedPrivate *priv;
1143 GtkWidget *widget;
1144
1145 embed->priv = priv = gtk_clutter_embed_get_instance_private (embed);
1146 widget = GTK_WIDGET (embed);
1147
1148 /* we have a real window backing our drawing */
1149 gtk_widget_set_has_window (widget, TRUE);
1150
1151 /* we accept key focus */
1152 gtk_widget_set_can_focus (widget, TRUE);
1153
1154 /* we own the whole drawing of this widget, including the background */
1155 gtk_widget_set_app_paintable (widget, TRUE);
1156
1157 /* this widget should expand in both directions */
1158 gtk_widget_set_hexpand (widget, TRUE);
1159 gtk_widget_set_vexpand (widget, TRUE);
1160
1161 /* we always create new stages rather than use the default */
1162 priv->stage = clutter_stage_new ();
1163 g_object_set_data (G_OBJECT (priv->stage),
1164 "gtk-clutter-embed",
1165 embed);
1166
1167 /* intercept the queue-redraw signal of the stage to know when
1168 * Clutter-side requests a redraw; this way we can also request
1169 * a redraw GTK-side
1170 */
1171 priv->queue_redraw_id =
1172 g_signal_connect (priv->stage,
1173 "queue-redraw", G_CALLBACK (on_stage_queue_redraw),
1174 embed);
1175
1176 /* intercept the queue-relayout signal of the stage to know when
1177 * Clutter-side needs to renegotiate it's size; this way we can
1178 * also request a resize GTK-side
1179 */
1180 priv->queue_relayout_id =
1181 g_signal_connect (priv->stage,
1182 "queue-relayout", G_CALLBACK (on_stage_queue_relayout),
1183 embed);
1184
1185
1186 #if defined(GDK_WINDOWING_WAYLAND) && defined(CLUTTER_WINDOWING_WAYLAND)
1187 {
1188 GdkDisplay *gdk_display = gtk_widget_get_display (widget);
1189 if (clutter_check_windowing_backend (CLUTTER_WINDOWING_WAYLAND) &&
1190 GDK_IS_WAYLAND_DISPLAY (gdk_display))
1191 {
1192 struct wl_display *display;
1193 struct wl_registry *registry;
1194
1195 display = gdk_wayland_display_get_wl_display (gdk_display);
1196 registry = wl_display_get_registry (display);
1197 wl_registry_add_listener (registry, ®istry_listener, embed);
1198
1199 wl_display_roundtrip (display);
1200 }
1201 }
1202 #endif
1203 }
1204
1205 /**
1206 * gtk_clutter_embed_new:
1207 *
1208 * Creates a new #GtkClutterEmbed widget. This widget can be
1209 * used to build a scene using Clutter API into a GTK+ application.
1210 *
1211 * Return value: the newly created #GtkClutterEmbed
1212 */
1213 GtkWidget *
gtk_clutter_embed_new(void)1214 gtk_clutter_embed_new (void)
1215 {
1216 return g_object_new (GTK_CLUTTER_TYPE_EMBED, NULL);
1217 }
1218
1219 /**
1220 * gtk_clutter_embed_get_stage:
1221 * @embed: a #GtkClutterEmbed
1222 *
1223 * Retrieves the #ClutterStage from @embed. The returned stage can be
1224 * used to add actors to the Clutter scene.
1225 *
1226 * Return value: (transfer none): the Clutter stage. You should never
1227 * destroy or unref the returned actor.
1228 */
1229 ClutterActor *
gtk_clutter_embed_get_stage(GtkClutterEmbed * embed)1230 gtk_clutter_embed_get_stage (GtkClutterEmbed *embed)
1231 {
1232 g_return_val_if_fail (GTK_CLUTTER_IS_EMBED (embed), NULL);
1233
1234 return embed->priv->stage;
1235 }
1236
1237 /**
1238 * gtk_clutter_embed_set_use_layout_size:
1239 * @embed: a #GtkClutterEmbed
1240 * @use_layout_size: a boolean
1241 *
1242 * Changes the way @embed requests size. If @use_layout_size is
1243 * %TRUE, the @embed widget will request the size that the
1244 * LayoutManager reports as the preferred size. This means that
1245 * a Gtk+ window will automatically get the natural and minimum
1246 * toplevel window sizes. This is useful when the contents of the
1247 * clutter stage is similar to a traditional UI.
1248 *
1249 * If @use_layout_size is %FALSE (which is the default) then @embed
1250 * will not request any size and its up to the embedder to make sure
1251 * there is some size (by setting a custom size on the widget or a default
1252 * size on the toplevel. This makes more sense when using the @embed
1253 * as a viewport into a potentially unlimited clutter space.
1254 *
1255 * Since: 1.4
1256 */
1257 void
gtk_clutter_embed_set_use_layout_size(GtkClutterEmbed * embed,gboolean use_layout_size)1258 gtk_clutter_embed_set_use_layout_size (GtkClutterEmbed *embed,
1259 gboolean use_layout_size)
1260 {
1261 GtkClutterEmbedPrivate *priv = embed->priv;
1262
1263 g_return_if_fail (GTK_CLUTTER_IS_EMBED (embed));
1264
1265 use_layout_size = !!use_layout_size;
1266 if (use_layout_size != priv->use_layout_size)
1267 {
1268 priv->use_layout_size = use_layout_size;
1269 gtk_widget_queue_resize (GTK_WIDGET (embed));
1270 g_object_notify (G_OBJECT (embed), "use-layout-size");
1271 }
1272 }
1273
1274 extern gboolean
1275 gtk_clutter_embed_get_honor_stage_size (GtkClutterEmbed *embed);
1276
1277 gboolean
gtk_clutter_embed_get_honor_stage_size(GtkClutterEmbed * embed)1278 gtk_clutter_embed_get_honor_stage_size (GtkClutterEmbed *embed)
1279 {
1280 return gtk_clutter_embed_get_use_layout_size (embed);
1281 }
1282
1283 /**
1284 * gtk_clutter_embed_get_use_layout_size:
1285 * @embed: a #GtkClutterEmbed
1286 *
1287 * Retrieves whether the embedding uses the layout size, see
1288 * gtk_clutter_embed_set_use_layout_size() for details.
1289 *
1290 * Return value: %TRUE if reporting stage size as widget size, %FALSE otherwise.
1291 *
1292 * Since: 1.4
1293 */
1294 gboolean
gtk_clutter_embed_get_use_layout_size(GtkClutterEmbed * embed)1295 gtk_clutter_embed_get_use_layout_size (GtkClutterEmbed *embed)
1296 {
1297 GtkClutterEmbedPrivate *priv = embed->priv;
1298
1299 g_return_val_if_fail (GTK_CLUTTER_IS_EMBED (embed), FALSE);
1300
1301 return priv->use_layout_size;
1302 }
1303