94594c0fc638a2c4f11f67bd35ea59eab7826ccb
[clutter-gtk] / clutter-gtk / gtk-clutter-embed.c
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  * @short_description: Widget for embedding a Clutter scene
26  *
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.
30  *
31  * <note>You should never resize the #ClutterStage embedded into the
32  * #GtkClutterEmbed widget. Instead, resize the widget using
33  * gtk_widget_set_size_request().</note>
34  *
35  * <note>You should only call #clutter_actor_show_all() after the
36  * widget itself has been shown</note>
37  *
38  * <note>Only a single #GtkClutterEmbed instace per application is
39  * currently supported</note>
40  *
41  * Since: 0.6
42  */
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #include <glib-object.h>
49
50 #include <clutter/clutter-main.h>
51 #include <clutter/clutter-stage.h>
52
53 #include <gdk/gdk.h>
54 #include <gdk/gdkx.h>
55 #include <clutter/clutter-x11.h>
56
57 #include "gtk-clutter-embed.h"
58
59 G_DEFINE_TYPE (GtkClutterEmbed, gtk_clutter_embed, GTK_TYPE_WIDGET);
60
61 #define GTK_CLUTTER_EMBED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_CLUTTER_EMBED, GtkClutterEmbedPrivate))
62
63 struct _GtkClutterEmbedPrivate
64 {
65   ClutterActor *stage;
66 };
67
68 static void
69 gtk_clutter_embed_send_configure (GtkClutterEmbed *embed)
70 {
71   GtkWidget *widget;
72   GdkEvent *event = gdk_event_new (GDK_CONFIGURE);
73
74   widget = GTK_WIDGET (embed);
75
76   event->configure.window = g_object_ref (widget->window);
77   event->configure.send_event = TRUE;
78   event->configure.x = widget->allocation.x;
79   event->configure.y = widget->allocation.y;
80   event->configure.width = widget->allocation.width;
81   event->configure.height = widget->allocation.height;
82   
83   gtk_widget_event (widget, event);
84   gdk_event_free (event);
85 }
86
87 static void
88 gtk_clutter_embed_dispose (GObject *gobject)
89 {
90   G_OBJECT_CLASS (gtk_clutter_embed_parent_class)->dispose (gobject);
91 }
92
93 static void
94 gtk_clutter_embed_show (GtkWidget *widget)
95 {
96   GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
97
98   /* Make sure the widget is realised before we show */
99   gtk_widget_realize(widget);
100
101   GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->show (widget);
102
103   clutter_actor_show (priv->stage);
104 }
105
106 static void
107 gtk_clutter_embed_hide (GtkWidget *widget)
108 {
109   GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
110
111   GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->hide (widget);
112
113   clutter_actor_hide (priv->stage);
114 }
115
116 static void
117 gtk_clutter_embed_realize (GtkWidget *widget)
118 {
119   GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv; 
120   GdkWindowAttr attributes;
121   int attributes_mask;
122   
123   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
124
125   attributes.window_type = GDK_WINDOW_CHILD;
126   attributes.x = widget->allocation.x;
127   attributes.y = widget->allocation.y;
128   attributes.width = widget->allocation.width;
129   attributes.height = widget->allocation.height;
130   attributes.wclass = GDK_INPUT_OUTPUT;
131   attributes.visual = gtk_widget_get_visual (widget);
132   attributes.colormap = gtk_widget_get_colormap (widget);
133   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
134
135   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
136
137   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
138                                    &attributes,
139                                    attributes_mask);
140   gdk_window_set_user_data (widget->window, widget);
141
142   widget->style = gtk_style_attach (widget->style, widget->window);
143   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
144   
145   gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
146
147   clutter_x11_set_stage_foreign (CLUTTER_STAGE (priv->stage), 
148                                  GDK_WINDOW_XID (widget->window));
149   clutter_redraw ();
150
151   gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
152 }
153
154 static void
155 gtk_clutter_embed_size_allocate (GtkWidget     *widget,
156                                  GtkAllocation *allocation)
157 {
158   GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
159
160   widget->allocation = *allocation;
161
162   if (GTK_WIDGET_REALIZED (widget))
163     {
164       gdk_window_move_resize (widget->window,
165                               allocation->x, allocation->y,
166                               allocation->width, allocation->height);
167
168       gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
169     }
170
171   clutter_actor_set_size (priv->stage,
172                           allocation->width,
173                           allocation->height);
174
175   if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
176     clutter_actor_queue_redraw (priv->stage);
177 }
178
179 static gboolean
180 gtk_clutter_embed_button_event (GtkWidget      *widget,
181                                 GdkEventButton *event)
182 {
183   ClutterEvent cevent = { 0, };
184
185   if (event->type == GDK_BUTTON_PRESS ||
186       event->type == GDK_2BUTTON_PRESS ||
187       event->type == GDK_3BUTTON_PRESS)
188     cevent.type = cevent.button.type = CLUTTER_BUTTON_PRESS;
189   else if (event->type == GDK_BUTTON_RELEASE)
190     cevent.type = cevent.button.type = CLUTTER_BUTTON_RELEASE;
191   else
192     return FALSE;
193
194   cevent.button.x = event->x;
195   cevent.button.y = event->y;
196   cevent.button.time = event->time;
197   cevent.button.click_count =
198     (event->type == GDK_BUTTON_PRESS ? 1
199                                      : (event->type == GDK_2BUTTON_PRESS ? 2
200                                                                          : 3));
201   cevent.button.modifier_state = event->state;
202   cevent.button.button = event->button;
203
204   clutter_do_event (&cevent);
205
206   return TRUE;
207 }
208
209 static gboolean
210 gtk_clutter_embed_key_event (GtkWidget   *widget,
211                              GdkEventKey *event)
212 {
213   ClutterEvent cevent = { 0, };
214
215   if (event->type == GDK_KEY_PRESS)
216     cevent.type = cevent.key.type = CLUTTER_KEY_PRESS;
217   else if (event->type == GDK_KEY_RELEASE)
218     cevent.type = cevent.key.type = CLUTTER_KEY_RELEASE;
219   else
220     return FALSE;
221
222   cevent.key.time = event->time;
223   cevent.key.modifier_state = event->state;
224   cevent.key.keyval = event->keyval;
225   cevent.key.hardware_keycode = event->hardware_keycode;
226
227   clutter_do_event (&cevent);
228
229   return TRUE;
230 }
231
232 static gboolean
233 gtk_clutter_embed_expose_event (GtkWidget *widget, GdkEventExpose *event)
234 {
235   GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
236
237   if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
238     clutter_actor_queue_redraw (priv->stage);
239
240   return TRUE;
241 }
242
243 static gboolean
244 gtk_clutter_embed_map_event (GtkWidget       *widget,
245                              GdkEventAny     *event)
246 {
247   GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
248
249   /* The backend wont get the XEvent as we go strait to do_event().
250    * So we have to make sure we set the event here.
251   */
252   CLUTTER_ACTOR_SET_FLAGS (priv->stage, CLUTTER_ACTOR_MAPPED);
253
254   return TRUE;
255 }
256
257 static void
258 gtk_clutter_embed_class_init (GtkClutterEmbedClass *klass)
259 {
260   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
261   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
262
263   g_type_class_add_private (klass, sizeof (GtkClutterEmbedPrivate));
264
265   gobject_class->dispose = gtk_clutter_embed_dispose;
266
267   widget_class->size_allocate = gtk_clutter_embed_size_allocate;
268   widget_class->realize = gtk_clutter_embed_realize;
269   widget_class->show = gtk_clutter_embed_show;
270   widget_class->hide = gtk_clutter_embed_hide;
271   widget_class->button_press_event = gtk_clutter_embed_button_event;
272   widget_class->button_release_event = gtk_clutter_embed_button_event;
273   widget_class->key_press_event = gtk_clutter_embed_key_event;
274   widget_class->key_release_event = gtk_clutter_embed_key_event;
275   widget_class->expose_event = gtk_clutter_embed_expose_event;
276   widget_class->map_event = gtk_clutter_embed_map_event;
277 }
278
279 static void
280 gtk_clutter_embed_init (GtkClutterEmbed *embed)
281 {
282   GtkClutterEmbedPrivate *priv;
283   const XVisualInfo *xvinfo;
284   GdkVisual *visual;
285   GdkColormap *colormap;
286
287   embed->priv = priv = GTK_CLUTTER_EMBED_GET_PRIVATE (embed);
288
289   gtk_widget_set_double_buffered (GTK_WIDGET (embed), FALSE);
290
291   /* note we never ref or unref this */
292   priv->stage = clutter_stage_get_default ();
293
294   /* We need to use the colormap from the Clutter visual */
295   xvinfo = clutter_x11_get_stage_visual (CLUTTER_STAGE (priv->stage));
296   visual = gdk_x11_screen_lookup_visual (gdk_screen_get_default (),
297                                          xvinfo->visualid);
298   colormap = gdk_colormap_new (visual, FALSE);
299   gtk_widget_set_colormap (GTK_WIDGET (embed), colormap);
300 }
301
302 /**
303  * gtk_clutter_init:
304  *
305  * This function should be called instead of #clutter_init() and after
306  * #gtk_init()
307  *
308  * Return value: 1 on success, < 0 on failure.
309  *
310  * Since: 0.8
311  */
312 ClutterInitError
313 gtk_clutter_init (int *argc, char ***argv)
314 {
315   clutter_x11_set_display (GDK_DISPLAY());
316   clutter_x11_disable_event_retrieval ();
317
318   /* FIXME: call gtk_init() here? */
319
320   return clutter_init (argc, argv);
321 }
322
323 /**
324  * gtk_clutter_embed_new:
325  *
326  * Creates a new embedded Clutter widget.
327  *
328  * Return value: the newly created #GtkClutterEmbed
329  *
330  * Since: 0.6
331  */
332 GtkWidget *
333 gtk_clutter_embed_new (void)
334 {
335   return g_object_new (GTK_TYPE_CLUTTER_EMBED, NULL);
336 }
337
338 /**
339  * gtk_clutter_embed_get_stage:
340  * @embed: a #GtkClutterEmbed
341  *
342  * Retrieves the #ClutterStage from @embed. The returned stage can be
343  * used to add actors to the Clutter scene.
344  *
345  * Return value: the Clutter stage. You should never destroy or unref
346  *   the returned actor.
347  *
348  * Since: 0.6
349  */
350 ClutterActor *
351 gtk_clutter_embed_get_stage (GtkClutterEmbed *embed)
352 {
353   g_return_val_if_fail (GTK_IS_CLUTTER_EMBED (embed), NULL);
354
355   return embed->priv->stage;
356 }