* a #GtkClutterEmbed widget is possible to build, show and interact with
* a scene built using Clutter inside a GTK+ application.
*
- * <note>You should never resize the #ClutterStage embedded into the
- * #GtkClutterEmbed widget. Instead, resize the widget using
- * gtk_widget_set_size_request().</note>
- *
- * <note>You should only call #clutter_actor_show_all() after the
- * widget itself has been shown</note>
- *
- * <note>Only a single #GtkClutterEmbed instace per application is
- * currently supported</note>
+ * <note>To avoid flickering on show, you should call gtk_widget_show()
+ * or gtk_widget_realize() before calling clutter_actor_show() on the
+ * embedded #ClutterStage actor. This is needed for Clutter to be able
+ * to paint on the #GtkClutterEmbed widget.</note>
*
* Since: 0.6
*/
#include <glib-object.h>
+#include <gdk/gdk.h>
+#include <gtk/gtkmain.h>
+
#include <clutter/clutter-main.h>
#include <clutter/clutter-stage.h>
+#include <clutter/clutter-container.h>
-#include <gdk/gdk.h>
+#if defined(HAVE_CLUTTER_GTK_X11)
+
+#include <clutter/x11/clutter-x11.h>
#include <gdk/gdkx.h>
-#include <clutter/clutter-x11.h>
+
+#elif defined(HAVE_CLUTTER_GTK_WIN32)
+
+#include <clutter/clutter-win32.h>
+#include <gdk/gdkwin32.h>
+
+#endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
#include "gtk-clutter-embed.h"
static void
gtk_clutter_embed_dispose (GObject *gobject)
{
+ GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (gobject)->priv;
+
+ if (priv->stage)
+ {
+ clutter_actor_destroy (priv->stage);
+ priv->stage = NULL;
+ }
+
G_OBJECT_CLASS (gtk_clutter_embed_parent_class)->dispose (gobject);
}
GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
/* Make sure the widget is realised before we show */
- gtk_widget_realize(widget);
-
- GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->show (widget);
+ if (!GTK_WIDGET_REALIZED (widget))
+ gtk_widget_realize (widget);
clutter_actor_show (priv->stage);
+
+ GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->show (widget);
}
static void
{
GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
- GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->hide (widget);
-
clutter_actor_hide (priv->stage);
+
+ GTK_WIDGET_CLASS (gtk_clutter_embed_parent_class)->hide (widget);
}
static void
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
- attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
+
+ /* NOTE: GDK_MOTION_NOTIFY above should be safe as Clutter does its own
+ * throtling.
+ */
+ attributes.event_mask = gtk_widget_get_events (widget)
+ | GDK_EXPOSURE_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
+#if defined(HAVE_CLUTTER_GTK_X11)
clutter_x11_set_stage_foreign (CLUTTER_STAGE (priv->stage),
GDK_WINDOW_XID (widget->window));
- clutter_redraw ();
+#elif defined(HAVE_CLUTTER_GTK_WIN32)
+ clutter_win32_set_stage_foreign (CLUTTER_STAGE (priv->stage),
+ GDK_WINDOW_HWND (widget->window));
+#endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (priv->stage));
gtk_clutter_embed_send_configure (GTK_CLUTTER_EMBED (widget));
}
allocation->width,
allocation->height);
- if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
- clutter_actor_queue_redraw (priv->stage);
+ clutter_actor_queue_relayout (priv->stage);
+}
+
+static gboolean
+gtk_clutter_embed_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
+ ClutterEvent cevent = { 0, };
+
+ cevent.type = CLUTTER_MOTION;
+ cevent.any.stage = CLUTTER_STAGE (priv->stage);
+ cevent.motion.x = event->x;
+ cevent.motion.y = event->y;
+ cevent.motion.time = event->time;
+
+ clutter_do_event (&cevent);
+
+ /* doh - motion events can push ENTER/LEAVE events onto Clutters
+ * internal event queue which we do really ever touch (essentially
+ * proxying from gtks queue). The below pumps them back out and
+ * processes.
+ * *could* be side effects with below though doubful as no other
+ * events reach the queue (we shut down event collection). Maybe
+ * a peek_mask type call could be even safer.
+ */
+ while (clutter_events_pending())
+ {
+ ClutterEvent *ev = clutter_event_get ();
+ if (ev)
+ {
+ clutter_do_event (ev);
+ clutter_event_free (ev);
+ }
+ }
+
+ return FALSE;
}
static gboolean
gtk_clutter_embed_button_event (GtkWidget *widget,
GdkEventButton *event)
{
+ GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
ClutterEvent cevent = { 0, };
if (event->type == GDK_BUTTON_PRESS ||
else
return FALSE;
+ cevent.any.stage = CLUTTER_STAGE (priv->stage);
cevent.button.x = event->x;
cevent.button.y = event->y;
cevent.button.time = event->time;
clutter_do_event (&cevent);
- return TRUE;
+ return FALSE;
}
static gboolean
gtk_clutter_embed_key_event (GtkWidget *widget,
GdkEventKey *event)
{
+ GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
ClutterEvent cevent = { 0, };
if (event->type == GDK_KEY_PRESS)
else
return FALSE;
+ cevent.any.stage = CLUTTER_STAGE (priv->stage);
cevent.key.time = event->time;
cevent.key.modifier_state = event->state;
cevent.key.keyval = event->keyval;
clutter_do_event (&cevent);
- return TRUE;
+ return FALSE;
}
static gboolean
-gtk_clutter_embed_expose_event (GtkWidget *widget, GdkEventExpose *event)
+gtk_clutter_embed_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
{
GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
if (CLUTTER_ACTOR_IS_VISIBLE (priv->stage))
clutter_actor_queue_redraw (priv->stage);
- return TRUE;
+ return FALSE;
}
static gboolean
-gtk_clutter_embed_map_event (GtkWidget *widget,
- GdkEventAny *event)
+gtk_clutter_embed_map_event (GtkWidget *widget,
+ GdkEventAny *event)
{
GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
*/
CLUTTER_ACTOR_SET_FLAGS (priv->stage, CLUTTER_ACTOR_MAPPED);
- return TRUE;
+ return FALSE;
+}
+
+static gboolean
+gtk_clutter_embed_focus_out (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ GtkClutterEmbedPrivate *priv = GTK_CLUTTER_EMBED (widget)->priv;
+
+ /* give back key focus to the stage */
+ clutter_stage_set_key_focus (CLUTTER_STAGE (priv->stage), NULL);
+
+ return FALSE;
}
static void
widget_class->button_release_event = gtk_clutter_embed_button_event;
widget_class->key_press_event = gtk_clutter_embed_key_event;
widget_class->key_release_event = gtk_clutter_embed_key_event;
+ widget_class->motion_notify_event = gtk_clutter_embed_motion_notify_event;
widget_class->expose_event = gtk_clutter_embed_expose_event;
widget_class->map_event = gtk_clutter_embed_map_event;
+ widget_class->focus_out_event = gtk_clutter_embed_focus_out;
}
static void
gtk_clutter_embed_init (GtkClutterEmbed *embed)
{
GtkClutterEmbedPrivate *priv;
- const XVisualInfo *xvinfo;
- GdkVisual *visual;
- GdkColormap *colormap;
embed->priv = priv = GTK_CLUTTER_EMBED_GET_PRIVATE (embed);
- gtk_widget_set_double_buffered (GTK_WIDGET (embed), FALSE);
+ GTK_WIDGET_SET_FLAGS (embed, GTK_CAN_FOCUS);
- /* note we never ref or unref this */
- priv->stage = clutter_stage_get_default ();
+ /* disable double-buffering: it's automatically provided
+ * by OpenGL
+ */
+ gtk_widget_set_double_buffered (GTK_WIDGET (embed), FALSE);
- /* We need to use the colormap from the Clutter visual */
- xvinfo = clutter_x11_get_stage_visual (CLUTTER_STAGE (priv->stage));
- visual = gdk_x11_screen_lookup_visual (gdk_screen_get_default (),
- xvinfo->visualid);
- colormap = gdk_colormap_new (visual, FALSE);
- gtk_widget_set_colormap (GTK_WIDGET (embed), colormap);
+ /* we always create new stages rather than use the default */
+ priv->stage = clutter_stage_new ();
+
+ /* we must realize the stage to get it ready for embedding */
+ clutter_actor_realize (priv->stage);
+
+#ifdef HAVE_CLUTTER_GTK_X11
+ {
+ const XVisualInfo *xvinfo;
+ GdkVisual *visual;
+ GdkColormap *colormap;
+
+ /* We need to use the colormap from the Clutter visual */
+ xvinfo = clutter_x11_get_stage_visual (CLUTTER_STAGE (priv->stage));
+ visual = gdk_x11_screen_lookup_visual (gdk_screen_get_default (),
+ xvinfo->visualid);
+ colormap = gdk_colormap_new (visual, FALSE);
+ gtk_widget_set_colormap (GTK_WIDGET (embed), colormap);
+ }
+#endif
}
/**
* gtk_clutter_init:
+ * @argc: pointer to the arguments count, or %NULL
+ * @argv: pointer to the arguments vector, or %NULL
*
- * This function should be called instead of #clutter_init() and after
- * #gtk_init()
+ * This function should be called instead of clutter_init() and
+ * gtk_init().
*
- * Return value: 1 on success, < 0 on failure.
+ * Return value: %CLUTTER_INIT_SUCCESS on success, a negative integer
+ * on failure.
*
* Since: 0.8
*/
ClutterInitError
-gtk_clutter_init (int *argc, char ***argv)
+gtk_clutter_init (int *argc,
+ char ***argv)
{
+ if (!gtk_init_check (argc, argv))
+ return CLUTTER_INIT_ERROR_GTK;
+
+#if defined(HAVE_CLUTTER_GTK_X11)
clutter_x11_set_display (GDK_DISPLAY());
clutter_x11_disable_event_retrieval ();
-
- /* FIXME: call gtk_init() here? */
+#elif defined(HAVE_CLUTTER_GTK_WIN32)
+ clutter_win32_disable_event_retrieval ();
+#endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
return clutter_init (argc, argv);
}
/**
* gtk_clutter_embed_new:
*
- * Creates a new embedded Clutter widget.
+ * Creates a new #GtkClutterEmbed widget. This widget can be
+ * used to build a scene using Clutter API into a GTK+ application.
*
* Return value: the newly created #GtkClutterEmbed
*