gtk: add GtkGLArea

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@gmail.com>
Acked-by: Fabiano Fidêncio <fidencio@redhat.com>
This commit is contained in:
Marc-André Lureau 2016-01-11 11:57:41 +01:00
parent 2cb74f4ac1
commit 6d8ca210d8
3 changed files with 82 additions and 9 deletions

View File

@ -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);

View File

@ -137,6 +137,7 @@ struct _SpiceDisplayPrivate {
guint tex_pointer_id;
guint prog;
EGLImageKHR image;
gboolean call_draw_done;
SpiceGlScanout scanout;
} egl;
};

View File

@ -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.call_draw_done) {
spice_display_gl_draw_done(SPICE_DISPLAY_CHANNEL(d->display));
d->egl.call_draw_done = FALSE;
}
return TRUE;
}
static void
drawing_area_realize(GtkWidget *area, gpointer user_data)
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.call_draw_done = 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)