diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2013-04-17 15:33:36 +0100 |
---|---|---|
committer | Emmanuele Bassi <ebassi@gnome.org> | 2013-05-17 15:35:53 +0100 |
commit | 0ef8b581bac4c3eb1c8cc2dc4d93fb605d96e8cd (patch) | |
tree | 418bd4cc182c6f3cbf495944b14bddf01e44e0ad /clutter-gtk | |
parent | 9b64df88b855cb501d97987ced99ba879f8824cd (diff) | |
download | clutter-gtk-0ef8b581bac4c3eb1c8cc2dc4d93fb605d96e8cd.tar.gz |
Provide a fallback for embedding widgets into actors
Currently, we can only embed a GtkWidget into a ClutterActor on X11 by
using a Pixmap and the GLX texture_from_pixmap extension. There is no
reason why we shouldn't be able to allow the same to happen by using
Cairo surfaces — except the performance implications of making copies of
the contents of the Drawable used by GDK.
This should allow embedding widgets on platforms that do not have the
texture_from_pixmap extension.
Diffstat (limited to 'clutter-gtk')
-rw-r--r-- | clutter-gtk/gtk-clutter-actor.c | 139 |
1 files changed, 130 insertions, 9 deletions
diff --git a/clutter-gtk/gtk-clutter-actor.c b/clutter-gtk/gtk-clutter-actor.c index f95caed..d1e7c64 100644 --- a/clutter-gtk/gtk-clutter-actor.c +++ b/clutter-gtk/gtk-clutter-actor.c @@ -69,6 +69,14 @@ G_DEFINE_TYPE (GtkClutterActor, gtk_clutter_actor, CLUTTER_TYPE_ACTOR) #define GTK_CLUTTER_ACTOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_CLUTTER_TYPE_ACTOR, GtkClutterActorPrivate)) +/* #define ENABLE_DEBUG 1 */ + +#ifdef ENABLE_DEBUG +#define DEBUG(x) g_printerr(x) +#else +#define DEBUG(x) +#endif + struct _GtkClutterActorPrivate { GtkWidget *widget; @@ -76,6 +84,11 @@ struct _GtkClutterActorPrivate cairo_surface_t *surface; + /* canvas instance used as a fallback; owned + * by the texture actor below + */ + ClutterContent *canvas; + ClutterActor *texture; }; @@ -86,6 +99,47 @@ enum PROP_CONTENTS }; +/* we allow overriding the default platform-specific code with an + * environment variable + */ +static inline gboolean +gtk_clutter_actor_use_image_surface (void) +{ + static const char *env = NULL; + + if (G_UNLIKELY (env == NULL)) + env = g_getenv ("GTK_CLUTTER_ACTOR_SURFACE"); + + return g_strcmp0 (env, "image") == 0; +} + +/* paints the GtkClutterActorPrivate:surface on the cairo_t provided by + * a ClutterCanvas, if we use the fallback path. this implies a copy, + * plus a copy when we move the contents of the surface we use in the + * ClutterCanvas on to the GPU, but it's the most portable method we have + * at our disposal to implement embedding GTK widgets into Clutter actors. + */ +static gboolean +gtk_clutter_actor_draw_canvas (ClutterCanvas *canvas, + cairo_t *cr, + int width, + int height, + GtkClutterActor *actor) +{ + /* clear the surface */ + cairo_save (cr); + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + cairo_restore (cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface (cr, actor->priv->surface, 0.0, 0.0); + cairo_paint (cr); + + return TRUE; +} + static void gtk_clutter_actor_dispose (GObject *object) { @@ -122,7 +176,8 @@ gtk_clutter_actor_realize (ClutterActor *actor) priv->surface = _gtk_clutter_offscreen_get_surface (GTK_CLUTTER_OFFSCREEN (priv->widget)); #if defined(CLUTTER_WINDOWING_X11) && defined(CAIRO_HAS_XLIB_SURFACE) - if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11) && + if (!gtk_clutter_actor_use_image_surface () && + clutter_check_windowing_backend (CLUTTER_WINDOWING_X11) && cairo_surface_get_type (priv->surface) == CAIRO_SURFACE_TYPE_XLIB) { Drawable pixmap; @@ -135,7 +190,41 @@ gtk_clutter_actor_realize (ClutterActor *actor) clutter_x11_texture_pixmap_set_pixmap (CLUTTER_X11_TEXTURE_PIXMAP (priv->texture), pixmap); clutter_actor_set_size (priv->texture, pixmap_width, pixmap_height); } + else #endif + { + int width = gtk_widget_get_allocated_width (priv->widget); + int height = gtk_widget_get_allocated_height (priv->widget); + int canvas_width = 0, canvas_height = 0; + + DEBUG (G_STRLOC ": Using image surface.\n"); + + g_object_get (priv->canvas, + "width", &canvas_width, + "height", &canvas_height, + NULL); + + + clutter_actor_set_size (priv->texture, width, height); + + /* clutter_canvas_set_size() will invalidate its contents only + * if the size differs, but we want to invalidate the contents + * in any case; we cannot call clutter_content_invalidate(), + * though, because that may cause two invalidations in a row, + * so we check the size of the canvas first. + */ + if (width != canvas_width || + height != canvas_height) + { + clutter_canvas_set_size (CLUTTER_CANVAS (priv->canvas), + width, + height); + } + else + { + clutter_content_invalidate (priv->canvas); + } + } } static void @@ -257,17 +346,26 @@ gtk_clutter_actor_allocate (ClutterActor *actor, surface = gdk_offscreen_window_get_surface (window); if (surface != priv->surface) { + priv->surface = surface; + #if defined(CLUTTER_WINDOWING_X11) && defined(CAIRO_HAS_XLIB_SURFACE) - if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11) && + if (!gtk_clutter_actor_use_image_surface () && + clutter_check_windowing_backend (CLUTTER_WINDOWING_X11) && cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB) { Drawable pixmap = cairo_xlib_surface_get_drawable (surface); clutter_x11_texture_pixmap_set_pixmap (CLUTTER_X11_TEXTURE_PIXMAP (priv->texture), pixmap); } + else #endif + { + DEBUG (G_STRLOC ": Using image surface.\n"); - priv->surface = surface; + clutter_canvas_set_size (CLUTTER_CANVAS (priv->canvas), + gtk_widget_get_allocated_width (priv->widget), + gtk_widget_get_allocated_height (priv->widget)); + } } } @@ -449,7 +547,8 @@ gtk_clutter_actor_init (GtkClutterActor *self) clutter_actor_set_reactive (actor, TRUE); #if defined(CLUTTER_WINDOWING_X11) - if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11)) + if (!gtk_clutter_actor_use_image_surface () && + clutter_check_windowing_backend (CLUTTER_WINDOWING_X11)) { priv->texture = clutter_x11_texture_pixmap_new (); @@ -460,9 +559,22 @@ gtk_clutter_actor_init (GtkClutterActor *self) } else #endif - g_critical ("Embedding GtkWidget inside ClutterActor through " - "GtkClutterActor does not yet work on non-X11 " - "platforms."); + { + DEBUG (G_STRLOC ": Using image surface.\n"); + + priv->canvas = clutter_canvas_new (); + g_signal_connect (priv->canvas, "draw", + G_CALLBACK (gtk_clutter_actor_draw_canvas), + actor); + + priv->texture = clutter_actor_new (); + clutter_actor_set_content (priv->texture, priv->canvas); + clutter_actor_add_child (actor, priv->texture); + clutter_actor_set_name (priv->texture, "Onscreen Texture"); + clutter_actor_show (priv->texture); + + g_object_unref (priv->canvas); + } g_signal_connect (self, "notify::reactive", G_CALLBACK (on_reactive_change), NULL); } @@ -480,15 +592,24 @@ _gtk_clutter_actor_update (GtkClutterActor *actor, gint width, gint height) { + GtkClutterActorPrivate *priv = actor->priv; + + if (gtk_clutter_actor_use_image_surface ()) + { + clutter_content_invalidate (priv->canvas); + } + else #if defined(CLUTTER_WINDOWING_X11) if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11)) { - GtkClutterActorPrivate *priv = actor->priv; - clutter_x11_texture_pixmap_update_area (CLUTTER_X11_TEXTURE_PIXMAP (priv->texture), x, y, width, height); } + else #endif + { + /* ... */ + } clutter_actor_queue_redraw (CLUTTER_ACTOR (actor)); } |