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, &registry_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