28882e94fc0fce17e83ff2d7ebdb761193495ca9
[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  * Since: 0.6
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <glib-object.h>
43
44 #include <clutter/clutter-main.h>
45 #include <clutter/clutter-stage.h>
46
47 #include <gdk/gdk.h>
48 #include <gdk/gdkx.h>
49 #include <clutter/clutter-x11.h>
50
51 #include "gtk-clutter-embed.h"
52
53 G_DEFINE_TYPE (GtkClutterEmbed, gtk_clutter_embed, GTK_TYPE_WIDGET);
54
55 #define GTK_CLUTTER_EMBED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_CLUTTER_EMBED, GtkClutterEmbedPrivate))
56
57 struct _GtkClutterEmbedPrivate
58 {
59   ClutterActor *stage;
60 };
61
62 static void
63 gtk_clutter_embed_send_configure (GtkClutterEmbed *embed)
64 {
65   GtkWidget *widget;
66   GdkEvent *event = gdk_event_new (GDK_CONFIGURE);
67
68   widget = GTK_WIDGET (embed);
69
70   event->configure.window = g_object_ref (widget->window);
71   event->configure.send_event = TRUE;
72   event->configure.x = widget->allocation.x;
73   event->configure.y = widget->allocation.y;
74   event->configure.width = widget->allocation.width;
75   event->configure.height = widget->allocation.height;
76   
77   gtk_widget_event (widget, event);
78   gdk_event_free (event);
79 }
80
81 static void
82 gtk_clutter_embed_dispose (GObject *gobject)
83 {
84   G_OBJECT_CLASS (gtk_clutter_embed_parent_class)->dispose (gobject);
85 }
86
87 static void
88 gtk_clutter_embed_realize (GtkWidget *widget)
89 {
90   GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv; 
91   GdkWindowAttr attributes;
92   int attributes_mask;
93   
94   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
95
96   attributes.window_type = GDK_WINDOW_CHILD;
97   attributes.x = widget->allocation.x;
98   attributes.y = widget->allocation.y;
99   attributes.width = widget->allocation.width;
100   attributes.height = widget->allocation.height;
101   attributes.wclass = GDK_INPUT_OUTPUT;
102   attributes.visual = gtk_widget_get_visual (widget);
103   attributes.colormap = gtk_widget_get_colormap (widget);
104   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
105
106   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
107
108   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
109                                    &attributes,
110                                    attributes_mask);
111   gdk_window_set_user_data (widget->window, widget);
112
113   widget->style = gtk_style_attach (widget->style, widget->window);
114   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
115   
116   gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
117
118   clutter_x11_set_stage_foreign (CLUTTER_STAGE (priv->stage), 
119                                  GDK_WINDOW_XID (widget->window));
120
121   /* allow a redraw here */
122   clutter_actor_queue_redraw (priv->stage);
123
124   gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
125 }
126
127 static void
128 gtk_clutter_embed_size_allocate (GtkWidget     *widget,
129                                  GtkAllocation *allocation)
130 {
131   GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
132
133   widget->allocation = *allocation;
134
135   if (GTK_WIDGET_REALIZED (widget))
136     {
137       gdk_window_move_resize (widget->window,
138                               allocation->x, allocation->y,
139                               allocation->width, allocation->height);
140
141       gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
142     }
143
144   clutter_actor_set_size (priv->stage,
145                           allocation->width,
146                           allocation->height);
147
148   if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
149     clutter_actor_queue_redraw (priv->stage);
150 }
151
152 static gboolean
153 gtk_clutter_embed_button_event (GtkWidget      *widget,
154                                 GdkEventButton *event)
155 {
156   ClutterEvent cevent = { 0, };
157
158   if (event->type == GDK_BUTTON_PRESS ||
159       event->type == GDK_2BUTTON_PRESS ||
160       event->type == GDK_3BUTTON_PRESS)
161     cevent.type = cevent.button.type = CLUTTER_BUTTON_PRESS;
162   else if (event->type == GDK_BUTTON_RELEASE)
163     cevent.type = cevent.button.type = CLUTTER_BUTTON_RELEASE;
164   else
165     return FALSE;
166
167   cevent.button.x = event->x;
168   cevent.button.y = event->y;
169   cevent.button.time = event->time;
170   cevent.button.click_count =
171     (event->type == GDK_BUTTON_PRESS ? 1
172                                      : (event->type == GDK_2BUTTON_PRESS ? 2
173                                                                          : 3));
174   cevent.button.modifier_state = event->state;
175   cevent.button.button = event->button;
176
177   clutter_do_event (&cevent);
178
179   return TRUE;
180 }
181
182 static gboolean
183 gtk_clutter_embed_key_event (GtkWidget   *widget,
184                              GdkEventKey *event)
185 {
186   ClutterEvent cevent = { 0, };
187
188   if (event->type == GDK_KEY_PRESS)
189     cevent.type = cevent.key.type = CLUTTER_KEY_PRESS;
190   else if (event->type == GDK_KEY_RELEASE)
191     cevent.type = cevent.key.type = CLUTTER_KEY_RELEASE;
192   else
193     return FALSE;
194
195   cevent.key.time = event->time;
196   cevent.key.modifier_state = event->state;
197   cevent.key.keyval = event->keyval;
198   cevent.key.hardware_keycode = event->hardware_keycode;
199
200   clutter_do_event (&cevent);
201
202   return TRUE;
203 }
204
205 static void
206 gtk_clutter_embed_class_init (GtkClutterEmbedClass *klass)
207 {
208   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
209   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
210
211   g_type_class_add_private (klass, sizeof (GtkClutterEmbedPrivate));
212
213   gobject_class->dispose = gtk_clutter_embed_dispose;
214
215   widget_class->size_allocate = gtk_clutter_embed_size_allocate;
216   widget_class->realize = gtk_clutter_embed_realize;
217   widget_class->button_press_event = gtk_clutter_embed_button_event;
218   widget_class->button_release_event = gtk_clutter_embed_button_event;
219   widget_class->key_press_event = gtk_clutter_embed_key_event;
220   widget_class->key_release_event = gtk_clutter_embed_key_event;
221 }
222
223 static void
224 gtk_clutter_embed_init (GtkClutterEmbed *embed)
225 {
226   GtkClutterEmbedPrivate *priv;
227   const XVisualInfo *xvinfo;
228   GdkVisual *visual;
229   GdkColormap *colormap;
230
231   embed->priv = priv = GTK_CLUTTER_EMBED_GET_PRIVATE (embed);
232
233   gtk_widget_set_double_buffered (GTK_WIDGET (embed), FALSE);
234
235   /* note we never ref or unref this */
236   priv->stage = clutter_stage_get_default ();
237
238   /* We need to use the colormap from the Clutter visual */
239   xvinfo = clutter_x11_get_stage_visual (CLUTTER_STAGE (priv->stage));
240   visual = gdk_x11_screen_lookup_visual (gdk_screen_get_default (),
241                                          xvinfo->visualid);
242   colormap = gdk_colormap_new (visual, FALSE);
243   gtk_widget_set_colormap (GTK_WIDGET (embed), colormap);
244 }
245
246 /**
247  * gtk_clutter_embed_new:
248  *
249  * FIXME
250  *
251  * Return value: the newly created #GtkClutterEmbed
252  *
253  * Since: 0.6
254  */
255 GtkWidget *
256 gtk_clutter_embed_new (void)
257 {
258   return g_object_new (GTK_TYPE_CLUTTER_EMBED, NULL);
259 }
260
261 /**
262  * gtk_clutter_embed_get_stage:
263  * @embed: a #GtkClutterEmbed
264  *
265  * Retrieves the #ClutterStage from @embed. The returned stage can be
266  * used to add actors to the Clutter scene.
267  *
268  * Return value: the Clutter stage. You should never destroy or unref
269  *   the returned actor.
270  *
271  * Since: 0.6
272  */
273 ClutterActor *
274 gtk_clutter_embed_get_stage (GtkClutterEmbed *embed)
275 {
276   g_return_val_if_fail (GTK_IS_CLUTTER_EMBED (embed), NULL);
277
278   return embed->priv->stage;
279 }