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