1 /* gtk-clutter-embed.c: Embeddable ClutterStage
3 * Copyright (C) 2007 OpenedHand
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.
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.
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>.
19 * Iain Holmes <iain@openedhand.com>
20 * Emmanuele Bassi <ebassi@openedhand.com>
24 * SECTION:gtk-clutter-embed
25 * @short_description: Widget for embedding a Clutter scene
27 * #GtkClutterEmbed is a GTK+ widget embedding a #ClutterStage. Using
28 * a #GtkClutterEmbed widget is possible to build, show and interact with
29 * a scene built using Clutter inside a GTK+ application.
31 * <note>To avoid flickering on show, you should call gtk_widget_show()
32 * or gtk_widget_realize() before calling clutter_actor_show() on the
33 * embedded #ClutterStage actor. This is needed for Clutter to be able
34 * to paint on the #GtkClutterEmbed widget.</note>
43 #include <glib-object.h>
46 #include <gtk/gtkmain.h>
48 #include <clutter/clutter-main.h>
49 #include <clutter/clutter-stage.h>
50 #include <clutter/clutter-container.h>
52 #if defined(HAVE_CLUTTER_GTK_X11)
54 #include <clutter/x11/clutter-x11.h>
57 #elif defined(HAVE_CLUTTER_GTK_WIN32)
59 #include <clutter/clutter-win32.h>
60 #include <gdk/gdkwin32.h>
62 #endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
64 #include "gtk-clutter-embed.h"
66 static void clutter_container_iface_init (ClutterContainerIface *iface);
68 G_DEFINE_TYPE_WITH_CODE (GtkClutterEmbed,
71 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
72 clutter_container_iface_init));
74 #define GTK_CLUTTER_EMBED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_CLUTTER_EMBED, GtkClutterEmbedPrivate))
76 struct _GtkClutterEmbedPrivate
82 gtk_clutter_embed_send_configure (GtkClutterEmbed *embed)
85 GdkEvent *event = gdk_event_new (GDK_CONFIGURE);
87 widget = GTK_WIDGET (embed);
89 event->configure.window = g_object_ref (widget->window);
90 event->configure.send_event = TRUE;
91 event->configure.x = widget->allocation.x;
92 event->configure.y = widget->allocation.y;
93 event->configure.width = widget->allocation.width;
94 event->configure.height = widget->allocation.height;
96 gtk_widget_event (widget, event);
97 gdk_event_free (event);
101 gtk_clutter_embed_dispose (GObject *gobject)
103 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (gobject)->priv;
107 clutter_actor_destroy (priv->stage);
111 G_OBJECT_CLASS (gtk_clutter_embed_parent_class)->dispose (gobject);
115 gtk_clutter_embed_show (GtkWidget *widget)
117 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
119 /* Make sure the widget is realised before we show */
120 gtk_widget_realize (widget);
122 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->show (widget);
124 clutter_actor_show (priv->stage);
128 gtk_clutter_embed_hide (GtkWidget *widget)
130 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
132 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->hide (widget);
134 clutter_actor_hide (priv->stage);
138 gtk_clutter_embed_realize (GtkWidget *widget)
140 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
141 GdkWindowAttr attributes;
144 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
146 attributes.window_type = GDK_WINDOW_CHILD;
147 attributes.x = widget->allocation.x;
148 attributes.y = widget->allocation.y;
149 attributes.width = widget->allocation.width;
150 attributes.height = widget->allocation.height;
151 attributes.wclass = GDK_INPUT_OUTPUT;
152 attributes.visual = gtk_widget_get_visual (widget);
153 attributes.colormap = gtk_widget_get_colormap (widget);
155 /* NOTE: GDK_MOTION_NOTIFY above should be safe as Clutter does its own
158 attributes.event_mask = gtk_widget_get_events (widget)
159 | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
160 | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_MOTION_NOTIFY;
163 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
165 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
168 gdk_window_set_user_data (widget->window, widget);
170 widget->style = gtk_style_attach (widget->style, widget->window);
171 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
173 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
175 #if defined(HAVE_CLUTTER_GTK_X11)
176 clutter_x11_set_stage_foreign (CLUTTER_STAGE (priv->stage),
177 GDK_WINDOW_XID (widget->window));
178 #elif defined(HAVE_CLUTTER_GTK_WIN32)
179 clutter_win32_set_stage_foreign (CLUTTER_STAGE (priv->stage),
180 GDK_WINDOW_HWND (widget->window));
181 #endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
183 clutter_redraw (CLUTTER_STAGE (priv->stage));
185 gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
189 gtk_clutter_embed_size_allocate (GtkWidget *widget,
190 GtkAllocation *allocation)
192 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
194 widget->allocation = *allocation;
196 if (GTK_WIDGET_REALIZED (widget))
198 gdk_window_move_resize (widget->window,
199 allocation->x, allocation->y,
200 allocation->width, allocation->height);
202 gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
205 clutter_actor_set_size (priv->stage,
209 if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
210 clutter_actor_queue_redraw (priv->stage);
214 gtk_clutter_embed_motion_notify_event (GtkWidget *widget,
215 GdkEventMotion *event)
217 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
218 ClutterEvent cevent = { 0, };
220 cevent.type = CLUTTER_MOTION;
221 cevent.any.stage = CLUTTER_STAGE (priv->stage);
222 cevent.motion.x = event->x;
223 cevent.motion.y = event->y;
224 cevent.motion.time = event->time;
226 clutter_do_event (&cevent);
228 /* doh - motion events can push ENTER/LEAVE events onto Clutters
229 * internal event queue which we do really ever touch (essentially
230 * proxying from gtks queue). The below pumps them back out and
232 * *could* be side effects with below though doubful as no other
233 * events reach the queue (we shut down event collection). Maybe
234 * a peek_mask type call could be even safer.
236 while (clutter_events_pending())
238 ClutterEvent *ev = clutter_event_get ();
241 clutter_do_event (ev);
242 clutter_event_free (ev);
250 gtk_clutter_embed_button_event (GtkWidget *widget,
251 GdkEventButton *event)
253 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
254 ClutterEvent cevent = { 0, };
256 if (event->type == GDK_BUTTON_PRESS ||
257 event->type == GDK_2BUTTON_PRESS ||
258 event->type == GDK_3BUTTON_PRESS)
259 cevent.type = cevent.button.type = CLUTTER_BUTTON_PRESS;
260 else if (event->type == GDK_BUTTON_RELEASE)
261 cevent.type = cevent.button.type = CLUTTER_BUTTON_RELEASE;
265 cevent.any.stage = CLUTTER_STAGE (priv->stage);
266 cevent.button.x = event->x;
267 cevent.button.y = event->y;
268 cevent.button.time = event->time;
269 cevent.button.click_count =
270 (event->type == GDK_BUTTON_PRESS ? 1
271 : (event->type == GDK_2BUTTON_PRESS ? 2
273 cevent.button.modifier_state = event->state;
274 cevent.button.button = event->button;
276 clutter_do_event (&cevent);
282 gtk_clutter_embed_key_event (GtkWidget *widget,
285 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
286 ClutterEvent cevent = { 0, };
288 if (event->type == GDK_KEY_PRESS)
289 cevent.type = cevent.key.type = CLUTTER_KEY_PRESS;
290 else if (event->type == GDK_KEY_RELEASE)
291 cevent.type = cevent.key.type = CLUTTER_KEY_RELEASE;
295 cevent.any.stage = CLUTTER_STAGE (priv->stage);
296 cevent.key.time = event->time;
297 cevent.key.modifier_state = event->state;
298 cevent.key.keyval = event->keyval;
299 cevent.key.hardware_keycode = event->hardware_keycode;
301 clutter_do_event (&cevent);
307 gtk_clutter_embed_expose_event (GtkWidget *widget, GdkEventExpose *event)
309 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
311 if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
312 clutter_actor_queue_redraw (priv->stage);
318 gtk_clutter_embed_map_event (GtkWidget *widget,
321 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
323 /* The backend wont get the XEvent as we go strait to do_event().
324 * So we have to make sure we set the event here.
326 CLUTTER_ACTOR_SET_FLAGS (priv->stage, CLUTTER_ACTOR_MAPPED);
332 gtk_clutter_embed_add (ClutterContainer *container,
335 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
336 ClutterContainer *stage = CLUTTER_CONTAINER (priv->stage);
338 clutter_container_add_actor (stage, actor);
339 g_signal_emit_by_name (container, "actor-added", actor);
343 gtk_clutter_embed_remove (ClutterContainer *container,
346 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
347 ClutterContainer *stage = CLUTTER_CONTAINER (priv->stage);
349 g_object_ref (actor);
351 clutter_container_remove_actor (stage, actor);
352 g_signal_emit_by_name (container, "actor-removed", actor);
354 g_object_unref (actor);
358 gtk_clutter_embed_foreach (ClutterContainer *container,
359 ClutterCallback callback,
360 gpointer callback_data)
362 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
363 ClutterContainer *stage = CLUTTER_CONTAINER (priv->stage);
365 clutter_container_foreach (stage, callback, callback_data);
369 gtk_clutter_embed_raise (ClutterContainer *container,
371 ClutterActor *sibling)
373 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
374 ClutterContainer *stage = CLUTTER_CONTAINER (priv->stage);
376 clutter_container_raise_child (stage, child, sibling);
380 gtk_clutter_embed_lower (ClutterContainer *container,
382 ClutterActor *sibling)
384 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
385 ClutterContainer *stage = CLUTTER_CONTAINER (priv->stage);
387 clutter_container_lower_child (stage, child, sibling);
391 gtk_clutter_embed_sort_depth_order (ClutterContainer *container)
393 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (container)->priv;
394 ClutterContainer *stage = CLUTTER_CONTAINER (priv->stage);
396 clutter_container_sort_depth_order (stage);
400 clutter_container_iface_init (ClutterContainerIface *iface)
402 iface->add = gtk_clutter_embed_add;
403 iface->remove = gtk_clutter_embed_remove;
404 iface->foreach = gtk_clutter_embed_foreach;
405 iface->raise = gtk_clutter_embed_raise;
406 iface->lower = gtk_clutter_embed_lower;
407 iface->sort_depth_order = gtk_clutter_embed_sort_depth_order;
411 gtk_clutter_embed_class_init (GtkClutterEmbedClass *klass)
413 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
414 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
416 g_type_class_add_private (klass, sizeof (GtkClutterEmbedPrivate));
418 gobject_class->dispose = gtk_clutter_embed_dispose;
420 widget_class->size_allocate = gtk_clutter_embed_size_allocate;
421 widget_class->realize = gtk_clutter_embed_realize;
422 widget_class->show = gtk_clutter_embed_show;
423 widget_class->hide = gtk_clutter_embed_hide;
424 widget_class->button_press_event = gtk_clutter_embed_button_event;
425 widget_class->button_release_event = gtk_clutter_embed_button_event;
426 widget_class->key_press_event = gtk_clutter_embed_key_event;
427 widget_class->key_release_event = gtk_clutter_embed_key_event;
428 widget_class->motion_notify_event = gtk_clutter_embed_motion_notify_event;
429 widget_class->expose_event = gtk_clutter_embed_expose_event;
430 widget_class->map_event = gtk_clutter_embed_map_event;
434 gtk_clutter_embed_init (GtkClutterEmbed *embed)
436 GtkClutterEmbedPrivate *priv;
438 embed->priv = priv = GTK_CLUTTER_EMBED_GET_PRIVATE (embed);
440 /* disable double-buffering: it's automatically provided
443 gtk_widget_set_double_buffered (GTK_WIDGET (embed), FALSE);
445 /* we always create new stages rather than use the default */
446 priv->stage = clutter_stage_new ();
448 /* we must realize the stage to get it ready for embedding */
449 clutter_actor_realize (priv->stage);
451 #ifdef HAVE_CLUTTER_GTK_X11
453 const XVisualInfo *xvinfo;
455 GdkColormap *colormap;
457 /* We need to use the colormap from the Clutter visual */
458 xvinfo = clutter_x11_get_stage_visual (CLUTTER_STAGE (priv->stage));
459 visual = gdk_x11_screen_lookup_visual (gdk_screen_get_default (),
461 colormap = gdk_colormap_new (visual, FALSE);
462 gtk_widget_set_colormap (GTK_WIDGET (embed), colormap);
469 * @argc: pointer to the arguments count, or %NULL
470 * @argv: pointer to the arguments vector, or %NULL
472 * This function should be called instead of clutter_init() and
475 * Return value: %CLUTTER_INIT_SUCCESS on success, a negative integer
481 gtk_clutter_init (int *argc,
484 if (!gtk_init_check (argc, argv))
485 return CLUTTER_INIT_ERROR_GTK;
487 #if defined(HAVE_CLUTTER_GTK_X11)
488 clutter_x11_set_display (GDK_DISPLAY());
489 clutter_x11_disable_event_retrieval ();
490 #elif defined(HAVE_CLUTTER_GTK_WIN32)
491 clutter_win32_disable_event_retrieval ();
492 #endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
494 return clutter_init (argc, argv);
498 * gtk_clutter_embed_new:
500 * Creates a new #GtkClutterEmbed widget. This widget can be
501 * used to build a scene using Clutter API into a GTK+ application.
503 * Return value: the newly created #GtkClutterEmbed
508 gtk_clutter_embed_new (void)
510 return g_object_new (GTK_TYPE_CLUTTER_EMBED, NULL);
514 * gtk_clutter_embed_get_stage:
515 * @embed: a #GtkClutterEmbed
517 * Retrieves the #ClutterStage from @embed. The returned stage can be
518 * used to add actors to the Clutter scene.
520 * Return value: the Clutter stage. You should never destroy or unref
521 * the returned actor.
526 gtk_clutter_embed_get_stage (GtkClutterEmbed *embed)
528 g_return_val_if_fail (GTK_IS_CLUTTER_EMBED (embed), NULL);
530 return embed->priv->stage;