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 "gtk-clutter-embed.h"
45 #include <glib-object.h>
49 #if defined(HAVE_CLUTTER_GTK_X11)
51 #include <clutter/x11/clutter-x11.h>
54 #elif defined(HAVE_CLUTTER_GTK_WIN32)
56 #include <clutter/clutter-win32.h>
57 #include <gdk/gdkwin32.h>
59 #endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
61 G_DEFINE_TYPE (GtkClutterEmbed, gtk_clutter_embed, GTK_TYPE_WIDGET);
63 #define GTK_CLUTTER_EMBED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_CLUTTER_EMBED, GtkClutterEmbedPrivate))
65 struct _GtkClutterEmbedPrivate
71 gtk_clutter_embed_send_configure (GtkClutterEmbed *embed)
74 GdkEvent *event = gdk_event_new (GDK_CONFIGURE);
76 widget = GTK_WIDGET (embed);
78 event->configure.window = g_object_ref (widget->window);
79 event->configure.send_event = TRUE;
80 event->configure.x = widget->allocation.x;
81 event->configure.y = widget->allocation.y;
82 event->configure.width = widget->allocation.width;
83 event->configure.height = widget->allocation.height;
85 gtk_widget_event (widget, event);
86 gdk_event_free (event);
90 gtk_clutter_embed_dispose (GObject *gobject)
92 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (gobject)->priv;
96 clutter_actor_destroy (priv->stage);
100 G_OBJECT_CLASS (gtk_clutter_embed_parent_class)->dispose (gobject);
104 gtk_clutter_embed_show (GtkWidget *widget)
106 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
108 /* Make sure the widget is realised before we show */
109 if (!GTK_WIDGET_REALIZED (widget))
110 gtk_widget_realize (widget);
112 clutter_actor_show (priv->stage);
114 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->show (widget);
118 gtk_clutter_embed_hide (GtkWidget *widget)
120 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
122 clutter_actor_hide (priv->stage);
124 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->hide (widget);
128 gtk_clutter_embed_realize (GtkWidget *widget)
130 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
131 GdkWindowAttr attributes;
134 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
136 attributes.window_type = GDK_WINDOW_CHILD;
137 attributes.x = widget->allocation.x;
138 attributes.y = widget->allocation.y;
139 attributes.width = widget->allocation.width;
140 attributes.height = widget->allocation.height;
141 attributes.wclass = GDK_INPUT_OUTPUT;
142 attributes.visual = gtk_widget_get_visual (widget);
143 attributes.colormap = gtk_widget_get_colormap (widget);
145 /* NOTE: GDK_MOTION_NOTIFY above should be safe as Clutter does its own
148 attributes.event_mask = gtk_widget_get_events (widget)
150 | GDK_BUTTON_PRESS_MASK
151 | GDK_BUTTON_RELEASE_MASK
153 | GDK_KEY_RELEASE_MASK
154 | GDK_POINTER_MOTION_MASK;
156 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
158 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
161 gdk_window_set_user_data (widget->window, widget);
163 widget->style = gtk_style_attach (widget->style, widget->window);
164 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
166 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
168 #if defined(HAVE_CLUTTER_GTK_X11)
169 clutter_x11_set_stage_foreign (CLUTTER_STAGE (priv->stage),
170 GDK_WINDOW_XID (widget->window));
171 #elif defined(HAVE_CLUTTER_GTK_WIN32)
172 clutter_win32_set_stage_foreign (CLUTTER_STAGE (priv->stage),
173 GDK_WINDOW_HWND (widget->window));
174 #endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
176 clutter_actor_queue_redraw (CLUTTER_ACTOR (priv->stage));
178 gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
182 gtk_clutter_embed_size_allocate (GtkWidget *widget,
183 GtkAllocation *allocation)
185 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
187 widget->allocation = *allocation;
189 if (GTK_WIDGET_REALIZED (widget))
191 gdk_window_move_resize (widget->window,
192 allocation->x, allocation->y,
193 allocation->width, allocation->height);
195 gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
198 /* change the size of the stage and ensure that the viewport
199 * has been updated as well
201 clutter_actor_set_size (priv->stage,
205 clutter_stage_ensure_viewport (CLUTTER_STAGE (priv->stage));
209 gtk_clutter_embed_motion_notify_event (GtkWidget *widget,
210 GdkEventMotion *event)
212 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
213 ClutterEvent cevent = { 0, };
215 cevent.type = CLUTTER_MOTION;
216 cevent.any.stage = CLUTTER_STAGE (priv->stage);
217 cevent.motion.x = event->x;
218 cevent.motion.y = event->y;
219 cevent.motion.time = event->time;
220 cevent.motion.modifier_state = event->state;
222 clutter_do_event (&cevent);
224 /* doh - motion events can push ENTER/LEAVE events onto Clutters
225 * internal event queue which we do really ever touch (essentially
226 * proxying from gtks queue). The below pumps them back out and
228 * *could* be side effects with below though doubful as no other
229 * events reach the queue (we shut down event collection). Maybe
230 * a peek_mask type call could be even safer.
232 while (clutter_events_pending())
234 ClutterEvent *ev = clutter_event_get ();
237 clutter_do_event (ev);
238 clutter_event_free (ev);
246 gtk_clutter_embed_button_event (GtkWidget *widget,
247 GdkEventButton *event)
249 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
250 ClutterEvent cevent = { 0, };
252 if (event->type == GDK_BUTTON_PRESS ||
253 event->type == GDK_2BUTTON_PRESS ||
254 event->type == GDK_3BUTTON_PRESS)
255 cevent.type = cevent.button.type = CLUTTER_BUTTON_PRESS;
256 else if (event->type == GDK_BUTTON_RELEASE)
257 cevent.type = cevent.button.type = CLUTTER_BUTTON_RELEASE;
261 cevent.any.stage = CLUTTER_STAGE (priv->stage);
262 cevent.button.x = event->x;
263 cevent.button.y = event->y;
264 cevent.button.time = event->time;
265 cevent.button.click_count =
266 (event->type == GDK_BUTTON_PRESS ? 1
267 : (event->type == GDK_2BUTTON_PRESS ? 2
269 cevent.button.modifier_state = event->state;
270 cevent.button.button = event->button;
272 clutter_do_event (&cevent);
278 gtk_clutter_embed_key_event (GtkWidget *widget,
281 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
282 ClutterEvent cevent = { 0, };
284 if (event->type == GDK_KEY_PRESS)
285 cevent.type = cevent.key.type = CLUTTER_KEY_PRESS;
286 else if (event->type == GDK_KEY_RELEASE)
287 cevent.type = cevent.key.type = CLUTTER_KEY_RELEASE;
291 cevent.any.stage = CLUTTER_STAGE (priv->stage);
292 cevent.key.time = event->time;
293 cevent.key.modifier_state = event->state;
294 cevent.key.keyval = event->keyval;
295 cevent.key.hardware_keycode = event->hardware_keycode;
297 clutter_do_event (&cevent);
303 gtk_clutter_embed_expose_event (GtkWidget *widget,
304 GdkEventExpose *event)
306 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
308 if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
309 clutter_actor_queue_redraw (priv->stage);
315 gtk_clutter_embed_map_event (GtkWidget *widget,
318 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
320 /* The backend wont get the XEvent as we go strait to do_event().
321 * So we have to make sure we set the event here.
323 CLUTTER_ACTOR_SET_FLAGS (priv->stage, CLUTTER_ACTOR_MAPPED);
329 gtk_clutter_embed_focus_in (GtkWidget *widget,
330 GdkEventFocus *event)
332 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
334 g_signal_emit_by_name (priv->stage, "activate");
340 gtk_clutter_embed_focus_out (GtkWidget *widget,
341 GdkEventFocus *event)
343 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
345 g_signal_emit_by_name (priv->stage, "deactivate");
347 /* give back key focus to the stage */
348 clutter_stage_set_key_focus (CLUTTER_STAGE (priv->stage), NULL);
354 gtk_clutter_embed_scroll_event (GtkWidget *widget,
355 GdkEventScroll *event)
357 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
359 ClutterEvent cevent = { 0, };
361 if (event->type == GDK_SCROLL)
362 cevent.type = cevent.scroll.type = CLUTTER_SCROLL;
366 cevent.any.stage = CLUTTER_STAGE (priv->stage);
367 cevent.scroll.x = (gint) event->x;
368 cevent.scroll.y = (gint) event->y;
369 cevent.scroll.time = event->time;
370 cevent.scroll.direction = event->direction;
371 cevent.scroll.modifier_state = event->state;
373 clutter_do_event (&cevent);
379 gtk_clutter_embed_class_init (GtkClutterEmbedClass *klass)
381 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
382 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
384 g_type_class_add_private (klass, sizeof (GtkClutterEmbedPrivate));
386 gobject_class->dispose = gtk_clutter_embed_dispose;
388 widget_class->size_allocate = gtk_clutter_embed_size_allocate;
389 widget_class->realize = gtk_clutter_embed_realize;
390 widget_class->show = gtk_clutter_embed_show;
391 widget_class->hide = gtk_clutter_embed_hide;
392 widget_class->button_press_event = gtk_clutter_embed_button_event;
393 widget_class->button_release_event = gtk_clutter_embed_button_event;
394 widget_class->key_press_event = gtk_clutter_embed_key_event;
395 widget_class->key_release_event = gtk_clutter_embed_key_event;
396 widget_class->motion_notify_event = gtk_clutter_embed_motion_notify_event;
397 widget_class->expose_event = gtk_clutter_embed_expose_event;
398 widget_class->map_event = gtk_clutter_embed_map_event;
399 widget_class->focus_in_event = gtk_clutter_embed_focus_in;
400 widget_class->focus_out_event = gtk_clutter_embed_focus_out;
401 widget_class->scroll_event = gtk_clutter_embed_scroll_event;
405 gtk_clutter_embed_init (GtkClutterEmbed *embed)
407 GtkClutterEmbedPrivate *priv;
409 embed->priv = priv = GTK_CLUTTER_EMBED_GET_PRIVATE (embed);
411 GTK_WIDGET_SET_FLAGS (embed, GTK_CAN_FOCUS);
413 /* disable double-buffering: it's automatically provided
416 gtk_widget_set_double_buffered (GTK_WIDGET (embed), FALSE);
418 /* we always create new stages rather than use the default */
419 priv->stage = clutter_stage_new ();
421 /* we must realize the stage to get it ready for embedding */
422 clutter_actor_realize (priv->stage);
424 #ifdef HAVE_CLUTTER_GTK_X11
426 const XVisualInfo *xvinfo;
428 GdkColormap *colormap;
430 /* We need to use the colormap from the Clutter visual */
431 xvinfo = clutter_x11_get_stage_visual (CLUTTER_STAGE (priv->stage));
432 visual = gdk_x11_screen_lookup_visual (gdk_screen_get_default (),
434 colormap = gdk_colormap_new (visual, FALSE);
435 gtk_widget_set_colormap (GTK_WIDGET (embed), colormap);
442 * @argc: pointer to the arguments count, or %NULL
443 * @argv: pointer to the arguments vector, or %NULL
445 * This function should be called instead of clutter_init() and
448 * Return value: %CLUTTER_INIT_SUCCESS on success, a negative integer
454 gtk_clutter_init (int *argc,
457 if (!gtk_init_check (argc, argv))
458 return CLUTTER_INIT_ERROR_GTK;
460 #if defined(HAVE_CLUTTER_GTK_X11)
461 clutter_x11_set_display (GDK_DISPLAY());
462 clutter_x11_disable_event_retrieval ();
463 #elif defined(HAVE_CLUTTER_GTK_WIN32)
464 clutter_win32_disable_event_retrieval ();
465 #endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
467 return clutter_init (argc, argv);
471 * gtk_clutter_embed_new:
473 * Creates a new #GtkClutterEmbed widget. This widget can be
474 * used to build a scene using Clutter API into a GTK+ application.
476 * Return value: the newly created #GtkClutterEmbed
481 gtk_clutter_embed_new (void)
483 return g_object_new (GTK_TYPE_CLUTTER_EMBED, NULL);
487 * gtk_clutter_embed_get_stage:
488 * @embed: a #GtkClutterEmbed
490 * Retrieves the #ClutterStage from @embed. The returned stage can be
491 * used to add actors to the Clutter scene.
493 * Return value: the Clutter stage. You should never destroy or unref
494 * the returned actor.
499 gtk_clutter_embed_get_stage (GtkClutterEmbed *embed)
501 g_return_val_if_fail (GTK_IS_CLUTTER_EMBED (embed), NULL);
503 return embed->priv->stage;