[Spice-devel] [PATCH 9/9] gtk: add GtkGLArea

Marc-André Lureau marcandre.lureau at gmail.com
Tue Feb 16 14:49:17 UTC 2016


GtkGLArea is the proper modern way to have opengl in an gtk+
application. Unfortunately, it may use various backends and interfaces
to initialize it, but dmabuf image sharing requires egl atm.

This patch keeps using our egl setup on X11, while it uses gtkglarea on
known gdk backend based on egl, such as the wayland one. This brings
wayland support for local gl to spice-gtk.

Signed-off-by: Marc-André Lureau <marcandre.lureau at gmail.com>
---
 src/spice-widget-egl.c  |  9 ++++++
 src/spice-widget-priv.h |  1 +
 src/spice-widget.c      | 79 ++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 81 insertions(+), 8 deletions(-)

diff --git a/src/spice-widget-egl.c b/src/spice-widget-egl.c
index 1748189..1b3cd07 100644
--- a/src/spice-widget-egl.c
+++ b/src/spice-widget-egl.c
@@ -29,6 +29,7 @@
 #include <libdrm/drm_fourcc.h>
 
 #include <gdk/gdkx.h>
+#include <gdk/gdkwayland.h>
 
 #define VERTS_ARRAY_SIZE (sizeof(GLfloat) * 4 * 4)
 #define TEX_ARRAY_SIZE (sizeof(GLfloat) * 4 * 2)
@@ -200,6 +201,14 @@ gboolean spice_egl_init(SpiceDisplay *display, GError **err)
     EGLNativeDisplayType dpy = 0;
     GdkDisplay *gdk_dpy = gdk_display_get_default();
 
+#ifdef GDK_WINDOWING_WAYLAND
+    if (GDK_IS_WAYLAND_DISPLAY(gdk_dpy)) {
+        d->egl.ctx = eglGetCurrentContext();
+        dpy = (EGLNativeDisplayType)gdk_wayland_display_get_wl_display(gdk_dpy);
+        d->egl.display = eglGetDisplay(dpy);
+        return spice_egl_init_shaders(display, err);
+    }
+#endif
 #ifdef GDK_WINDOWING_X11
     if (GDK_IS_X11_DISPLAY(gdk_dpy)) {
         dpy = (EGLNativeDisplayType)gdk_x11_display_get_xdisplay(gdk_dpy);
diff --git a/src/spice-widget-priv.h b/src/spice-widget-priv.h
index cf055d3..a60e72c 100644
--- a/src/spice-widget-priv.h
+++ b/src/spice-widget-priv.h
@@ -137,6 +137,7 @@ struct _SpiceDisplayPrivate {
         guint               tex_pointer_id;
         guint               prog;
         EGLImageKHR         image;
+        gboolean            ack;
         SpiceGlScanout      scanout;
     } egl;
 };
diff --git a/src/spice-widget.c b/src/spice-widget.c
index 286eeca..9399e86 100644
--- a/src/spice-widget.c
+++ b/src/spice-widget.c
@@ -539,12 +539,48 @@ static void grab_notify(SpiceDisplay *display, gboolean was_grabbed)
         release_keys(display);
 }
 
+static gboolean
+gl_area_render(GtkGLArea *area, GdkGLContext *context, gpointer user_data)
+{
+    SpiceDisplay *display = SPICE_DISPLAY(user_data);
+    SpiceDisplayPrivate *d = display->priv;
+
+    spice_egl_update_display(display);
+    glFlush();
+    if (d->egl.ack) {
+        spice_display_gl_draw_done(SPICE_DISPLAY_CHANNEL(d->display));
+        d->egl.ack = false;
+    }
+
+    return TRUE;
+}
+
+static void
+gl_area_realize(GtkGLArea *area, gpointer user_data)
+{
+    SpiceDisplay *display = SPICE_DISPLAY(user_data);
+    GError *err = NULL;
+
+    gtk_gl_area_make_current(area);
+    if (gtk_gl_area_get_error(area) != NULL)
+        return;
+
+    if (!spice_egl_init(display, &err)) {
+        g_critical("egl init failed: %s", err->message);
+        g_clear_error(&err);
+    }
+}
+
 static void
 drawing_area_realize(GtkWidget *area, gpointer user_data)
 {
+#ifdef GDK_WINDOWING_X11
     SpiceDisplay *display = SPICE_DISPLAY(user_data);
     GError *err = NULL;
 
+    if (!GDK_IS_X11_DISPLAY(gdk_display_get_default()))
+        return;
+
     if (!spice_egl_init(display, &err)) {
         g_critical("egl init failed: %s", err->message);
         g_clear_error(&err);
@@ -554,6 +590,7 @@ drawing_area_realize(GtkWidget *area, gpointer user_data)
         g_critical("egl realize failed: %s", err->message);
         g_clear_error(&err);
     }
+#endif
 }
 
 static void spice_display_init(SpiceDisplay *display)
@@ -574,6 +611,16 @@ static void spice_display_init(SpiceDisplay *display)
     gtk_widget_set_double_buffered(area, true);
     gtk_stack_set_visible_child(GTK_STACK(widget), area);
 
+    area = gtk_gl_area_new();
+    gtk_gl_area_set_required_version(GTK_GL_AREA(area), 3, 2);
+    gtk_gl_area_set_auto_render(GTK_GL_AREA(area), false);
+    g_object_connect(area,
+                     "signal::render", gl_area_render, display,
+                     "signal::realize", gl_area_realize, display,
+                     NULL);
+    gtk_stack_add_named(GTK_STACK(widget), area, "gl-area");
+    gtk_widget_show_all(widget);
+
     g_signal_connect(display, "grab-broken-event", G_CALLBACK(grab_broken), NULL);
     g_signal_connect(display, "grab-notify", G_CALLBACK(grab_notify), NULL);
 
@@ -1152,11 +1199,20 @@ static void set_egl_enabled(SpiceDisplay *display, bool enabled)
     if (d->egl.enabled == enabled)
         return;
 
-    /* even though the function is marked as deprecated, it's the
-     * only way I found to prevent glitches when the window is
-     * resized. */
-    area = gtk_stack_get_child_by_name(GTK_STACK(display), "draw-area");
-    gtk_widget_set_double_buffered(GTK_WIDGET(area), !enabled);
+#ifdef GDK_WINDOWING_X11
+    if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+        /* even though the function is marked as deprecated, it's the
+         * only way I found to prevent glitches when the window is
+         * resized. */
+        area = gtk_stack_get_child_by_name(GTK_STACK(display), "draw-area");
+        gtk_widget_set_double_buffered(GTK_WIDGET(area), !enabled);
+    } else
+#endif
+    {
+        area = gtk_stack_get_child_by_name(GTK_STACK(display), "gl-area");
+        gtk_stack_set_visible_child_name(GTK_STACK(display),
+                                         enabled ? "gl-area" : "draw-area");
+    }
 
     if (enabled) {
         spice_egl_resize_display(display, d->ww, d->wh);
@@ -1171,7 +1227,8 @@ static gboolean draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
     SpiceDisplayPrivate *d = display->priv;
     g_return_val_if_fail(d != NULL, false);
 
-    if (d->egl.enabled) {
+    if (d->egl.enabled &&
+        g_str_equal(gtk_stack_get_visible_child_name(GTK_STACK(display)), "draw-area")) {
         spice_egl_update_display(display);
         return false;
     }
@@ -2437,12 +2494,18 @@ static void gl_draw(SpiceDisplay *display,
                     guint32 x, guint32 y, guint32 w, guint32 h)
 {
     SpiceDisplayPrivate *d = display->priv;
+    GtkWidget *gl = gtk_stack_get_child_by_name(GTK_STACK(display), "gl-area");
 
     SPICE_DEBUG("%s",  __FUNCTION__);
     set_egl_enabled(display, true);
 
-    spice_egl_update_display(display);
-    spice_display_gl_draw_done(SPICE_DISPLAY_CHANNEL(d->display));
+    if (gtk_stack_get_visible_child(GTK_STACK(display)) == gl) {
+        gtk_gl_area_queue_render(GTK_GL_AREA(gl));
+        d->egl.ack = true;
+    } else {
+        spice_egl_update_display(display);
+        spice_display_gl_draw_done(SPICE_DISPLAY_CHANNEL(d->display));
+    }
 }
 
 static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
-- 
2.5.0



More information about the Spice-devel mailing list