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 * There can be as many #GtkClutterEmbed widgets per application, but
32 * they will embed and show the same #ClutterStage as a general limitation
35 * <note>To avoid flickering on show, you should call gtk_widget_show()
36 * or gtk_widget_realize() before calling clutter_actor_show() on the
37 * embedded #ClutterStage actor. This is needed for Clutter to be able
38 * to paint on the #GtkClutterEmbed widget.</note>
47 #include <glib-object.h>
49 #include <clutter/clutter-main.h>
50 #include <clutter/clutter-stage.h>
54 #include <clutter/clutter-x11.h>
56 #include "gtk-clutter-embed.h"
58 G_DEFINE_TYPE (GtkClutterEmbed, gtk_clutter_embed, GTK_TYPE_WIDGET);
60 #define GTK_CLUTTER_EMBED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_CLUTTER_EMBED, GtkClutterEmbedPrivate))
62 struct _GtkClutterEmbedPrivate
68 gtk_clutter_embed_send_configure (GtkClutterEmbed *embed)
71 GdkEvent *event = gdk_event_new (GDK_CONFIGURE);
73 widget = GTK_WIDGET (embed);
75 event->configure.window = g_object_ref (widget->window);
76 event->configure.send_event = TRUE;
77 event->configure.x = widget->allocation.x;
78 event->configure.y = widget->allocation.y;
79 event->configure.width = widget->allocation.width;
80 event->configure.height = widget->allocation.height;
82 gtk_widget_event (widget, event);
83 gdk_event_free (event);
87 gtk_clutter_embed_dispose (GObject *gobject)
89 G_OBJECT_CLASS (gtk_clutter_embed_parent_class)->dispose (gobject);
93 gtk_clutter_embed_show (GtkWidget *widget)
95 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
97 /* Make sure the widget is realised before we show */
98 gtk_widget_realize (widget);
100 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->show (widget);
102 clutter_actor_show (priv->stage);
106 gtk_clutter_embed_hide (GtkWidget *widget)
108 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
110 GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->hide (widget);
112 clutter_actor_hide (priv->stage);
116 gtk_clutter_embed_realize (GtkWidget *widget)
118 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
119 GdkWindowAttr attributes;
122 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
124 attributes.window_type = GDK_WINDOW_CHILD;
125 attributes.x = widget->allocation.x;
126 attributes.y = widget->allocation.y;
127 attributes.width = widget->allocation.width;
128 attributes.height = widget->allocation.height;
129 attributes.wclass = GDK_INPUT_OUTPUT;
130 attributes.visual = gtk_widget_get_visual (widget);
131 attributes.colormap = gtk_widget_get_colormap (widget);
132 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
134 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
136 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
139 gdk_window_set_user_data (widget->window, widget);
141 widget->style = gtk_style_attach (widget->style, widget->window);
142 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
144 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
146 clutter_x11_set_stage_foreign (CLUTTER_STAGE (priv->stage),
147 GDK_WINDOW_XID (widget->window));
150 gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
154 gtk_clutter_embed_size_allocate (GtkWidget *widget,
155 GtkAllocation *allocation)
157 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
159 widget->allocation = *allocation;
161 if (GTK_WIDGET_REALIZED (widget))
163 gdk_window_move_resize (widget->window,
164 allocation->x, allocation->y,
165 allocation->width, allocation->height);
167 gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
170 clutter_actor_set_size (priv->stage,
174 if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
175 clutter_actor_queue_redraw (priv->stage);
179 gtk_clutter_embed_button_event (GtkWidget *widget,
180 GdkEventButton *event)
182 ClutterEvent cevent = { 0, };
184 if (event->type == GDK_BUTTON_PRESS ||
185 event->type == GDK_2BUTTON_PRESS ||
186 event->type == GDK_3BUTTON_PRESS)
187 cevent.type = cevent.button.type = CLUTTER_BUTTON_PRESS;
188 else if (event->type == GDK_BUTTON_RELEASE)
189 cevent.type = cevent.button.type = CLUTTER_BUTTON_RELEASE;
193 cevent.button.x = event->x;
194 cevent.button.y = event->y;
195 cevent.button.time = event->time;
196 cevent.button.click_count =
197 (event->type == GDK_BUTTON_PRESS ? 1
198 : (event->type == GDK_2BUTTON_PRESS ? 2
200 cevent.button.modifier_state = event->state;
201 cevent.button.button = event->button;
203 clutter_do_event (&cevent);
209 gtk_clutter_embed_key_event (GtkWidget *widget,
212 ClutterEvent cevent = { 0, };
214 if (event->type == GDK_KEY_PRESS)
215 cevent.type = cevent.key.type = CLUTTER_KEY_PRESS;
216 else if (event->type == GDK_KEY_RELEASE)
217 cevent.type = cevent.key.type = CLUTTER_KEY_RELEASE;
221 cevent.key.time = event->time;
222 cevent.key.modifier_state = event->state;
223 cevent.key.keyval = event->keyval;
224 cevent.key.hardware_keycode = event->hardware_keycode;
226 clutter_do_event (&cevent);
232 gtk_clutter_embed_expose_event (GtkWidget *widget, GdkEventExpose *event)
234 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
236 if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
237 clutter_actor_queue_redraw (priv->stage);
243 gtk_clutter_embed_map_event (GtkWidget *widget,
246 GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
248 /* The backend wont get the XEvent as we go strait to do_event().
249 * So we have to make sure we set the event here.
251 CLUTTER_ACTOR_SET_FLAGS (priv->stage, CLUTTER_ACTOR_MAPPED);
257 gtk_clutter_embed_class_init (GtkClutterEmbedClass *klass)
259 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
260 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
262 g_type_class_add_private (klass, sizeof (GtkClutterEmbedPrivate));
264 gobject_class->dispose = gtk_clutter_embed_dispose;
266 widget_class->size_allocate = gtk_clutter_embed_size_allocate;
267 widget_class->realize = gtk_clutter_embed_realize;
268 widget_class->show = gtk_clutter_embed_show;
269 widget_class->hide = gtk_clutter_embed_hide;
270 widget_class->button_press_event = gtk_clutter_embed_button_event;
271 widget_class->button_release_event = gtk_clutter_embed_button_event;
272 widget_class->key_press_event = gtk_clutter_embed_key_event;
273 widget_class->key_release_event = gtk_clutter_embed_key_event;
274 widget_class->expose_event = gtk_clutter_embed_expose_event;
275 widget_class->map_event = gtk_clutter_embed_map_event;
279 gtk_clutter_embed_init (GtkClutterEmbed *embed)
281 GtkClutterEmbedPrivate *priv;
282 const XVisualInfo *xvinfo;
284 GdkColormap *colormap;
286 embed->priv = priv = GTK_CLUTTER_EMBED_GET_PRIVATE (embed);
288 gtk_widget_set_double_buffered (GTK_WIDGET (embed), FALSE);
290 /* note we never ref or unref this */
291 priv->stage = clutter_stage_get_default ();
293 /* We need to use the colormap from the Clutter visual */
294 xvinfo = clutter_x11_get_stage_visual (CLUTTER_STAGE (priv->stage));
295 visual = gdk_x11_screen_lookup_visual (gdk_screen_get_default (),
297 colormap = gdk_colormap_new (visual, FALSE);
298 gtk_widget_set_colormap (GTK_WIDGET (embed), colormap);
304 * This function should be called instead of #clutter_init() and after
307 * Return value: %CLUTTER_INIT_SUCCESS on success, a negative integer
313 gtk_clutter_init (int *argc, char ***argv)
315 clutter_x11_set_display (GDK_DISPLAY());
316 clutter_x11_disable_event_retrieval ();
318 /* FIXME: call gtk_init() here? */
320 return clutter_init (argc, argv);
324 * gtk_clutter_embed_new:
326 * Creates a new #GtkClutterEmbed widget. This widget can be
327 * used to build a scene using Clutter API into a GTK+ application.
329 * Return value: the newly created #GtkClutterEmbed
334 gtk_clutter_embed_new (void)
336 return g_object_new (GTK_TYPE_CLUTTER_EMBED, NULL);
340 * gtk_clutter_embed_get_stage:
341 * @embed: a #GtkClutterEmbed
343 * Retrieves the #ClutterStage from @embed. The returned stage can be
344 * used to add actors to the Clutter scene.
346 * Return value: the Clutter stage. You should never destroy or unref
347 * the returned actor.
352 gtk_clutter_embed_get_stage (GtkClutterEmbed *embed)
354 g_return_val_if_fail (GTK_IS_CLUTTER_EMBED (embed), NULL);
356 return embed->priv->stage;