2
0
Эх сурвалжийг харах

Support Virgil 3D renderer on macOS

Virgil 3D renderer will be available with ui/cocoa on macOS.

The OpenGL backend is NSOpenGLContext for core profile, and
EGL (in particular, ANGLE) for ES profile.

As a notable change, CONFIG_OPENGL_DMABUF is replaced with the
combination of CONFIG_EGL and CONFIG_GBM. In fact, the old
detection of DMA-BUF in configure script was not working
because EGL_MESA_image_dma_buf_export is always defined in
<epoxy/egl.h>. Therefore, the new combination should
behave in the same way.

Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
Akihiko Odaki 4 жил өмнө
parent
commit
3184f37bbe

+ 14 - 11
configure

@@ -377,6 +377,7 @@ u2f="auto"
 libusb="auto"
 usb_redir="auto"
 opengl="$default_feature"
+egl="no"
 cpuid_h="no"
 avx2_opt="$default_feature"
 capstone="auto"
@@ -3380,18 +3381,7 @@ if $pkg_config gbm; then
 fi
 
 if test "$opengl" != "no" ; then
-  epoxy=no
   if $pkg_config epoxy; then
-    cat > $TMPC << EOF
-#include <epoxy/egl.h>
-int main(void) { return 0; }
-EOF
-    if compile_prog "" "" ; then
-      epoxy=yes
-    fi
-  fi
-
-  if test "$epoxy" = "yes" ; then
     opengl_cflags="$($pkg_config --cflags epoxy)"
     opengl_libs="$($pkg_config --libs epoxy)"
     opengl=yes
@@ -3405,6 +3395,16 @@ EOF
   fi
 fi
 
+if test "$opengl" = "yes"; then
+  cat > $TMPC << EOF
+#include <epoxy/egl.h>
+int main(void) { return 0; }
+EOF
+  if compile_prog "" "" ; then
+    egl=yes
+  fi
+fi
+
 ##########################################
 # libnuma probe
 
@@ -4658,6 +4658,9 @@ if test "$opengl" = "yes" ; then
   echo "CONFIG_OPENGL=y" >> $config_host_mak
   echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak
   echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak
+  if test "$egl" = "yes" ; then
+    echo "CONFIG_EGL=y" >> $config_host_mak
+  fi
 fi
 
 if test "$gbm" = "yes" ; then

+ 1 - 1
hw/display/vhost-user-gpu.c

@@ -249,7 +249,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
         }
 
         con = g->parent_obj.scanout[m->scanout_id].con;
-        if (!console_has_gl(con)) {
+        if (!console_has_gl()) {
             error_report("console doesn't support GL!");
             vhost_user_gpu_unblock(g);
             break;

+ 3 - 3
hw/display/virtio-gpu.c

@@ -521,7 +521,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
         for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
             scanout = &g->parent_obj.scanout[i];
             if (scanout->resource_id == res->resource_id &&
-                console_has_gl(scanout->con)) {
+                console_has_gl()) {
                 dpy_gl_update(scanout->con, 0, 0, scanout->width,
                               scanout->height);
                 return;
@@ -633,7 +633,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
     g->parent_obj.enable = 1;
 
     if (res->blob) {
-        if (console_has_gl(scanout->con)) {
+        if (console_has_gl()) {
             if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb)) {
                 virtio_gpu_update_scanout(g, scanout_id, res, r);
                 return;
@@ -646,7 +646,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
     }
 
     /* create a surface for this scanout */
-    if ((res->blob && !console_has_gl(scanout->con)) ||
+    if ((res->blob && !console_has_gl()) ||
         !scanout->ds ||
         surface_data(scanout->ds) != data + fb->offset ||
         scanout->width != r->width ||

+ 31 - 35
include/ui/console.h

@@ -207,54 +207,48 @@ typedef struct DisplayChangeListenerOps {
                               QEMUCursor *cursor);
 
     /* required if GL */
-    QEMUGLContext (*dpy_gl_ctx_create)(DisplayChangeListener *dcl,
-                                       QEMUGLParams *params);
-    /* required if GL */
-    void (*dpy_gl_ctx_destroy)(DisplayChangeListener *dcl,
-                               QEMUGLContext ctx);
-    /* required if GL */
-    int (*dpy_gl_ctx_make_current)(DisplayChangeListener *dcl,
-                                   QEMUGLContext ctx);
+    void (*dpy_gl_update)(DisplayChangeListener *dcl,
+                          uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 
-    /* required if GL */
-    void (*dpy_gl_scanout_disable)(DisplayChangeListener *dcl);
-    /* required if GL */
-    void (*dpy_gl_scanout_texture)(DisplayChangeListener *dcl,
+} DisplayChangeListenerOps;
+
+struct DisplayChangeListener {
+    uint64_t update_interval;
+    const DisplayChangeListenerOps *ops;
+    DisplayState *ds;
+    QemuConsole *con;
+
+    QLIST_ENTRY(DisplayChangeListener) next;
+};
+
+typedef struct DisplayGLOps {
+    QEMUGLContext (*dpy_gl_ctx_create)(void *dg, QEMUGLParams *params);
+    void (*dpy_gl_ctx_destroy)(void *dg, QEMUGLContext ctx);
+    int (*dpy_gl_ctx_make_current)(void *dg, QEMUGLContext ctx);
+
+    bool (*dpy_gl_scanout_get_enabled)(void *dg);
+    void (*dpy_gl_scanout_disable)(void *dg);
+    void (*dpy_gl_scanout_texture)(void *dg,
                                    uint32_t backing_id,
                                    bool backing_y_0_top,
                                    uint32_t backing_width,
                                    uint32_t backing_height,
                                    uint32_t x, uint32_t y,
                                    uint32_t w, uint32_t h);
+
     /* optional (default to true if has dpy_gl_scanout_dmabuf) */
-    bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl);
+    bool (*dpy_has_dmabuf)(void *dg);
     /* optional */
-    void (*dpy_gl_scanout_dmabuf)(DisplayChangeListener *dcl,
-                                  QemuDmaBuf *dmabuf);
+    void (*dpy_gl_scanout_dmabuf)(void *dg, QemuDmaBuf *dmabuf);
     /* optional */
-    void (*dpy_gl_cursor_dmabuf)(DisplayChangeListener *dcl,
+    void (*dpy_gl_cursor_dmabuf)(void *dg,
                                  QemuDmaBuf *dmabuf, bool have_hot,
                                  uint32_t hot_x, uint32_t hot_y);
     /* optional */
-    void (*dpy_gl_cursor_position)(DisplayChangeListener *dcl,
-                                   uint32_t pos_x, uint32_t pos_y);
+    void (*dpy_gl_cursor_position)(void *dg, uint32_t pos_x, uint32_t pos_y);
     /* optional */
-    void (*dpy_gl_release_dmabuf)(DisplayChangeListener *dcl,
-                                  QemuDmaBuf *dmabuf);
-    /* required if GL */
-    void (*dpy_gl_update)(DisplayChangeListener *dcl,
-                          uint32_t x, uint32_t y, uint32_t w, uint32_t h);
-
-} DisplayChangeListenerOps;
-
-struct DisplayChangeListener {
-    uint64_t update_interval;
-    const DisplayChangeListenerOps *ops;
-    DisplayState *ds;
-    QemuConsole *con;
-
-    QLIST_ENTRY(DisplayChangeListener) next;
-};
+    void (*dpy_gl_release_dmabuf)(void *dg, QemuDmaBuf *dmabuf);
+} DisplayGLOps;
 
 DisplayState *init_displaystate(void);
 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
@@ -278,6 +272,7 @@ static inline int is_placeholder(DisplaySurface *surface)
     return surface->flags & QEMU_PLACEHOLDER_FLAG;
 }
 
+void register_displayglops(const DisplayGLOps *dg_ops);
 void register_displaychangelistener(DisplayChangeListener *dcl);
 void update_displaychangelistener(DisplayChangeListener *dcl,
                                   uint64_t interval);
@@ -321,7 +316,8 @@ QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx);
 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx);
 
-bool console_has_gl(QemuConsole *con);
+void console_set_displayglcontext(QemuConsole *con, void *dg);
+bool console_has_gl(void);
 
 static inline int surface_stride(DisplaySurface *s)
 {

+ 3 - 5
include/ui/egl-context.h

@@ -4,10 +4,8 @@
 #include "ui/console.h"
 #include "ui/egl-helpers.h"
 
-QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
-                                      QEMUGLParams *params);
-void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx);
-int qemu_egl_make_context_current(DisplayChangeListener *dcl,
-                                  QEMUGLContext ctx);
+QEMUGLContext qemu_egl_create_context(void *dg, QEMUGLParams *params);
+void qemu_egl_destroy_context(void *dg, QEMUGLContext ctx);
+int qemu_egl_make_context_current(void *dg, QEMUGLContext ctx);
 
 #endif /* EGL_CONTEXT_H */

+ 3 - 1
include/ui/egl-helpers.h

@@ -48,7 +48,9 @@ void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf);
 
 #endif
 
-EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win);
+EGLSurface qemu_egl_init_surface(EGLContext ectx, EGLNativeWindowType win);
+
+int qemu_egl_init_dpy_cocoa(DisplayGLMode mode);
 
 #if defined(CONFIG_X11) || defined(CONFIG_GBM)
 

+ 18 - 25
include/ui/gtk.h

@@ -21,7 +21,7 @@
 #include "ui/clipboard.h"
 #include "ui/console.h"
 #include "ui/kbd-state.h"
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
 #include "ui/egl-helpers.h"
 #include "ui/egl-context.h"
 #endif
@@ -39,7 +39,7 @@ typedef struct VirtualGfxConsole {
     cairo_surface_t *surface;
     double scale_x;
     double scale_y;
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
     QemuGLShader *gls;
     EGLContext ectx;
     EGLSurface esurface;
@@ -161,30 +161,26 @@ void gd_egl_update(DisplayChangeListener *dcl,
 void gd_egl_refresh(DisplayChangeListener *dcl);
 void gd_egl_switch(DisplayChangeListener *dcl,
                    DisplaySurface *surface);
-QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl,
-                                    QEMUGLParams *params);
-void gd_egl_scanout_disable(DisplayChangeListener *dcl);
-void gd_egl_scanout_texture(DisplayChangeListener *dcl,
+QEMUGLContext gd_egl_create_context(void *dg, QEMUGLParams *params);
+bool gd_egl_scanout_get_enabled(void *dg);
+void gd_egl_scanout_disable(void *dg);
+void gd_egl_scanout_texture(void *dg,
                             uint32_t backing_id,
                             bool backing_y_0_top,
                             uint32_t backing_width,
                             uint32_t backing_height,
                             uint32_t x, uint32_t y,
                             uint32_t w, uint32_t h);
-void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
-                           QemuDmaBuf *dmabuf);
-void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
+void gd_egl_scanout_dmabuf(void *dg, QemuDmaBuf *dmabuf);
+void gd_egl_cursor_dmabuf(void *dg,
                           QemuDmaBuf *dmabuf, bool have_hot,
                           uint32_t hot_x, uint32_t hot_y);
-void gd_egl_cursor_position(DisplayChangeListener *dcl,
-                            uint32_t pos_x, uint32_t pos_y);
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-                           QemuDmaBuf *dmabuf);
+void gd_egl_cursor_position(void *dg, uint32_t pos_x, uint32_t pos_y);
+void gd_egl_release_dmabuf(void *dg, QemuDmaBuf *dmabuf);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
                           uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
-int gd_egl_make_current(DisplayChangeListener *dcl,
-                        QEMUGLContext ctx);
+int gd_egl_make_current(void *dg, QEMUGLContext ctx);
 
 /* ui/gtk-gl-area.c */
 void gd_gl_area_init(VirtualConsole *vc);
@@ -194,25 +190,22 @@ void gd_gl_area_update(DisplayChangeListener *dcl,
 void gd_gl_area_refresh(DisplayChangeListener *dcl);
 void gd_gl_area_switch(DisplayChangeListener *dcl,
                        DisplaySurface *surface);
-QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
-                                        QEMUGLParams *params);
-void gd_gl_area_destroy_context(DisplayChangeListener *dcl,
-                                QEMUGLContext ctx);
-void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
-                               QemuDmaBuf *dmabuf);
-void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
+QEMUGLContext gd_gl_area_create_context(void *dg, QEMUGLParams *params);
+void gd_gl_area_destroy_context(void *dg, QEMUGLContext ctx);
+bool gd_gl_area_scanout_get_enabled(void *dg);
+void gd_gl_area_scanout_dmabuf(void *dg, QemuDmaBuf *dmabuf);
+void gd_gl_area_scanout_texture(void *dg,
                                 uint32_t backing_id,
                                 bool backing_y_0_top,
                                 uint32_t backing_width,
                                 uint32_t backing_height,
                                 uint32_t x, uint32_t y,
                                 uint32_t w, uint32_t h);
-void gd_gl_area_scanout_disable(DisplayChangeListener *dcl);
+void gd_gl_area_scanout_disable(void *dg);
 void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
                               uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_gl_area_init(void);
-int gd_gl_area_make_current(DisplayChangeListener *dcl,
-                            QEMUGLContext ctx);
+int gd_gl_area_make_current(void *dg, QEMUGLContext ctx);
 
 /* gtk-clipboard.c */
 void gd_clipboard_init(GtkDisplayState *gd);

+ 8 - 9
include/ui/sdl2.h

@@ -11,7 +11,7 @@
 #endif
 
 #include "ui/kbd-state.h"
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
 # include "ui/egl-helpers.h"
 #endif
 
@@ -32,7 +32,7 @@ struct sdl2_console {
     int ignore_hotkeys;
     SDL_GLContext winctx;
     QKbdState *kbd;
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
     QemuGLShader *gls;
     egl_fb guest_fb;
     egl_fb win_fb;
@@ -65,14 +65,13 @@ void sdl2_gl_switch(DisplayChangeListener *dcl,
 void sdl2_gl_refresh(DisplayChangeListener *dcl);
 void sdl2_gl_redraw(struct sdl2_console *scon);
 
-QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl,
-                                     QEMUGLParams *params);
-void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx);
-int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
-                                 QEMUGLContext ctx);
+QEMUGLContext sdl2_gl_create_context(void *dg, QEMUGLParams *params);
+void sdl2_gl_destroy_context(void *dg, QEMUGLContext ctx);
+int sdl2_gl_make_context_current(void *dg, QEMUGLContext ctx);
 
-void sdl2_gl_scanout_disable(DisplayChangeListener *dcl);
-void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
+bool sdl2_gl_scanout_get_enabled(void *dg);
+void sdl2_gl_scanout_disable(void *dg);
+void sdl2_gl_scanout_texture(void *dg,
                              uint32_t backing_id,
                              bool backing_y_0_top,
                              uint32_t backing_width,

+ 1 - 1
include/ui/spice-display.h

@@ -27,7 +27,7 @@
 #include "ui/qemu-pixman.h"
 #include "ui/console.h"
 
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL) && defined(CONFIG_GBM)
 # if SPICE_SERVER_VERSION >= 0x000d01 /* release 0.13.1 */
 #  define HAVE_SPICE_GL 1
 #  include "ui/egl-helpers.h"

+ 1 - 1
qemu-options.hx

@@ -1800,7 +1800,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
     "-display cocoa[,full-grab=on|off]\n"
     "              [,swap-option-command=on|off]\n"
 #endif
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
     "-display egl-headless[,rendernode=<file>]\n"
 #endif
     "-display none\n"

+ 397 - 65
ui/cocoa.m

@@ -47,6 +47,10 @@
 #include <Carbon/Carbon.h>
 #include "hw/core/cpu.h"
 
+#ifdef CONFIG_EGL
+#include "ui/egl-context.h"
+#endif
+
 #ifndef MAC_OS_X_VERSION_10_13
 #define MAC_OS_X_VERSION_10_13 101300
 #endif
@@ -76,24 +80,10 @@
     bool swap_option_command;
 } QEMUScreen;
 
-static void cocoa_update(DisplayChangeListener *dcl,
-                         int x, int y, int w, int h);
-
-static void cocoa_switch(DisplayChangeListener *dcl,
-                         DisplaySurface *surface);
-
-static void cocoa_refresh(DisplayChangeListener *dcl);
-
 static NSWindow *normalWindow;
-static const DisplayChangeListenerOps dcl_ops = {
-    .dpy_name          = "cocoa",
-    .dpy_gfx_update = cocoa_update,
-    .dpy_gfx_switch = cocoa_switch,
-    .dpy_refresh = cocoa_refresh,
-};
-static DisplayChangeListener dcl = {
-    .ops = &dcl_ops,
-};
+static DisplayChangeListener dcl;
+static DisplaySurface *surface;
+static QemuMutex surface_mutex;
 static int cursor_hide = 1;
 
 static int gArgc;
@@ -109,6 +99,26 @@ static void cocoa_switch(DisplayChangeListener *dcl,
 static QemuClipboardInfo *cbinfo;
 static QemuEvent cbevent;
 
+#ifdef CONFIG_OPENGL
+
+typedef struct {
+    uint32_t scanout_id;
+    bool scanout_y0_top;
+    bool surface_dirty;
+} DisplayGL;
+
+static DisplayGL *dgs;
+static QEMUGLContext view_ctx;
+static QemuGLShader *gls;
+
+#ifdef CONFIG_EGL
+static EGLSurface egl_surface;
+#endif
+
+static void cocoa_gl_destroy_context(void *dg, QEMUGLContext ctx);
+
+#endif
+
 // Utility functions to run specified code block with iothread lock held
 typedef void (^CodeBlock)(void);
 typedef bool (^BoolCodeBlock)(void);
@@ -302,13 +312,11 @@ @interface QemuCocoaView : NSView
 {
     NSTrackingArea *trackingArea;
     QEMUScreen screen;
-    pixman_image_t *pixman_image;
     QKbdState *kbd;
     BOOL isMouseGrabbed;
     BOOL isAbsoluteEnabled;
     CFMachPortRef eventsTap;
 }
-- (void) switchSurface:(pixman_image_t *)image;
 - (void) grabMouse;
 - (void) ungrabMouse;
 - (void) setFullGrab:(id)sender;
@@ -327,7 +335,6 @@ - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
 - (BOOL) isMouseGrabbed;
 - (BOOL) isAbsoluteEnabled;
 - (BOOL) isSwapOptionCommandEnabled;
-- (QEMUScreen) gscreen;
 - (void) raiseAllKeys;
 @end
 
@@ -366,10 +373,6 @@ - (void) dealloc
 {
     COCOA_DEBUG("QemuCocoaView: dealloc\n");
 
-    if (pixman_image) {
-        pixman_image_unref(pixman_image);
-    }
-
     qkbd_state_free(kbd);
 
     if (eventsTap) {
@@ -441,29 +444,39 @@ - (void) drawRect:(NSRect) rect
 {
     COCOA_DEBUG("QemuCocoaView: drawRect\n");
 
+#ifdef CONFIG_OPENGL
+    if (display_opengl) {
+        return;
+    }
+#endif
+
     // get CoreGraphic context
     CGContextRef viewContextRef = [[NSGraphicsContext currentContext] CGContext];
 
     CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
     CGContextSetShouldAntialias (viewContextRef, NO);
 
+    qemu_mutex_lock(&surface_mutex);
+
     // draw screen bitmap directly to Core Graphics context
-    if (!pixman_image) {
+    if (!surface) {
         // Draw request before any guest device has set up a framebuffer:
         // just draw an opaque black rectangle
         CGContextSetRGBFillColor(viewContextRef, 0, 0, 0, 1.0);
         CGContextFillRect(viewContextRef, NSRectToCGRect(rect));
     } else {
-        int w = pixman_image_get_width(pixman_image);
-        int h = pixman_image_get_height(pixman_image);
-        int bitsPerPixel = PIXMAN_FORMAT_BPP(pixman_image_get_format(pixman_image));
-        int stride = pixman_image_get_stride(pixman_image);
+        int w = surface_width(surface);
+        int h = surface_height(surface);
+        int bitsPerPixel = PIXMAN_FORMAT_BPP(surface_format(surface));
+        int stride = surface_stride(surface);
+
         CGDataProviderRef dataProviderRef = CGDataProviderCreateWithData(
             NULL,
-            pixman_image_get_data(pixman_image),
+            surface_data(surface),
             stride * h,
             NULL
         );
+
         CGImageRef imageRef = CGImageCreate(
             w, //width
             h, //height
@@ -501,6 +514,8 @@ - (void) drawRect:(NSRect) rect
         CGImageRelease (imageRef);
         CGDataProviderRelease(dataProviderRef);
     }
+
+    qemu_mutex_unlock(&surface_mutex);
 }
 
 - (NSSize) computeUnzoomedSize
@@ -581,27 +596,16 @@ - (void) updateUIInfo
     dpy_set_ui_info(dcl.con, &info);
 }
 
-- (void) switchSurface:(pixman_image_t *)image
+- (void) updateScreenWidth:(int)w height:(int)h
 {
-    COCOA_DEBUG("QemuCocoaView: switchSurface\n");
-
-    int w = pixman_image_get_width(image);
-    int h = pixman_image_get_height(image);
+    COCOA_DEBUG("QemuCocoaView: updateScreenWidth:height:\n");
 
     if (w != screen.width || h != screen.height) {
-        // Resize before we trigger the redraw, or we'll redraw at the wrong size
-        COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h);
+        COCOA_DEBUG("updateScreenWidth:height: new size %d x %d\n", w, h);
         screen.width = w;
         screen.height = h;
         [self resizeWindow];
     }
-
-    // update screenBuffer
-    if (pixman_image) {
-        pixman_image_unref(pixman_image);
-    }
-
-    pixman_image = image;
 }
 
 - (void) setFullGrab:(id)sender
@@ -711,7 +715,7 @@ - (void) handleMonitorInput:(NSEvent *)event
 
 - (bool) handleEvent:(NSEvent *)event
 {
-    if(!allow_events) {
+    if(!qatomic_read(&allow_events)) {
         /*
          * Just let OSX have all events that arrive before
          * applicationDidFinishLaunching.
@@ -1095,7 +1099,6 @@ - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {
 - (BOOL) isMouseGrabbed {return isMouseGrabbed;}
 - (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
 - (BOOL) isSwapOptionCommandEnabled {return screen.swap_option_command;}
-- (QEMUScreen) gscreen {return screen;}
 
 /*
  * Makes the target think all down keys are being released.
@@ -1211,7 +1214,6 @@ - (void) dealloc
 - (void)applicationDidFinishLaunching: (NSNotification *) note
 {
     COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
-    allow_events = true;
     /* Tell cocoa_display_init to proceed */
     qemu_sem_post(&app_started_sem);
 }
@@ -1889,6 +1891,13 @@ static void cocoa_clipboard_request(QemuClipboardInfo *info,
     status = qemu_main(gArgc, gArgv, *_NSGetEnviron());
     COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n");
     [cbowner release];
+#ifdef CONFIG_OPENGL
+    g_free(dgs);
+    qemu_gl_fini_shader(gls);
+    if (view_ctx) {
+        cocoa_gl_destroy_context(NULL, view_ctx);
+    }
+#endif
     exit(status);
 }
 
@@ -1905,6 +1914,8 @@ int main (int argc, char **argv) {
     qemu_thread_create(&thread, "qemu_main", call_qemu_main,
                        NULL, QEMU_THREAD_DETACHED);
 
+    qemu_mutex_init(&surface_mutex);
+
     COCOA_DEBUG("Main thread: waiting for display_init_sem\n");
     qemu_sem_wait(&display_init_sem);
     COCOA_DEBUG("Main thread: initializing app\n");
@@ -1952,18 +1963,21 @@ static void cocoa_update(DisplayChangeListener *dcl,
                          int x, int y, int w, int h)
 {
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+    DisplaySurface *updated = surface;
 
     COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
 
     dispatch_async(dispatch_get_main_queue(), ^{
-        CGFloat d = [cocoaView frame].size.height / (CGFloat)[cocoaView gscreen].height;
-
-        NSRect rect = NSMakeRect(
-                x * d,
-                ([cocoaView gscreen].height - y - h) * d,
-                w * d,
-                h * d);
+        qemu_mutex_lock(&surface_mutex);
+        if (updated != surface) {
+            qemu_mutex_unlock(&surface_mutex);
+            return;
+        }
+        int full_height = surface_height(surface);
+        qemu_mutex_unlock(&surface_mutex);
 
+        CGFloat d = [cocoaView frame].size.height / full_height;
+        NSRect rect = NSMakeRect(x * d, (full_height - y - h) * d, w * d, h * d);
         [cocoaView setNeedsDisplayInRect:rect];
     });
 
@@ -1971,24 +1985,31 @@ static void cocoa_update(DisplayChangeListener *dcl,
 }
 
 static void cocoa_switch(DisplayChangeListener *dcl,
-                         DisplaySurface *surface)
+                         DisplaySurface *new_surface)
 {
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-    pixman_image_t *image = surface->image;
+    static bool updating_screen;
 
     COCOA_DEBUG("qemu_cocoa: cocoa_switch\n");
 
     [cocoaView updateUIInfo];
 
-    // The DisplaySurface will be freed as soon as this callback returns.
-    // We take a reference to the underlying pixman image here so it does
-    // not disappear from under our feet; the switchSurface method will
-    // deref the old image when it is done with it.
-    pixman_image_ref(image);
+    qemu_mutex_lock(&surface_mutex);
+    surface = new_surface;
+    if (!updating_screen) {
+        updating_screen = true;
 
-    dispatch_async(dispatch_get_main_queue(), ^{
-        [cocoaView switchSurface:image];
-    });
+        dispatch_async(dispatch_get_main_queue(), ^{
+            qemu_mutex_lock(&surface_mutex);
+            updating_screen = false;
+            int w = surface_width(surface);
+            int h = surface_height(surface);
+            qemu_mutex_unlock(&surface_mutex);
+
+            [cocoaView updateScreenWidth:w height:h];
+        });
+    }
+    qemu_mutex_unlock(&surface_mutex);
     [pool release];
 }
 
@@ -2024,6 +2045,263 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
     [pool release];
 }
 
+static const DisplayChangeListenerOps dcl_ops = {
+    .dpy_name          = "cocoa",
+    .dpy_gfx_update = cocoa_update,
+    .dpy_gfx_switch = cocoa_switch,
+    .dpy_refresh = cocoa_refresh,
+};
+
+#ifdef CONFIG_OPENGL
+
+static void with_view_ctx(CodeBlock block)
+{
+#ifdef CONFIG_EGL
+    if (egl_surface) {
+        eglMakeCurrent(qemu_egl_display, egl_surface, egl_surface, view_ctx);
+        block();
+        return;
+    }
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    [(NSOpenGLContext *)view_ctx lock];
+    [(NSOpenGLContext *)view_ctx makeCurrentContext];
+    block();
+    [(NSOpenGLContext *)view_ctx unlock];
+#pragma clang diagnostic pop
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+static NSOpenGLContext *cocoa_gl_create_context_ns(NSOpenGLContext *share_context,
+                                                   int bpp)
+{
+    NSOpenGLPixelFormatAttribute attributes[] = {
+        NSOpenGLPFAOpenGLProfile,
+        NSOpenGLProfileVersion4_1Core,
+        NSOpenGLPFAColorSize,
+        bpp,
+        NSOpenGLPFADoubleBuffer,
+        0,
+    };
+    NSOpenGLPixelFormat *format;
+    NSOpenGLContext *ctx;
+
+    format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
+    ctx = [[NSOpenGLContext alloc] initWithFormat:format shareContext:share_context];
+    [format release];
+
+    [ctx retain];
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [ctx setView:cocoaView];
+        [ctx release];
+    });
+
+    return (QEMUGLContext)ctx;
+}
+#pragma clang diagnostic pop
+
+static int cocoa_gl_make_context_current(void *dg, QEMUGLContext ctx)
+{
+#ifdef CONFIG_EGL
+    if (egl_surface) {
+        EGLSurface surface = ctx == EGL_NO_CONTEXT ? EGL_NO_SURFACE : egl_surface;
+        return eglMakeCurrent(qemu_egl_display, surface, surface, ctx);
+    }
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    if (ctx) {
+        [(NSOpenGLContext *)ctx makeCurrentContext];
+    } else {
+        [NSOpenGLContext clearCurrentContext];
+    }
+#pragma clang diagnostic pop
+
+    return 0;
+}
+
+static QEMUGLContext cocoa_gl_create_context(void *dg, QEMUGLParams *params)
+{
+#ifdef CONFIG_EGL
+    if (egl_surface) {
+        eglMakeCurrent(qemu_egl_display, egl_surface, egl_surface, view_ctx);
+        return qemu_egl_create_context(dg, params);
+    }
+#endif
+
+    int bpp = PIXMAN_FORMAT_BPP(surface_format(surface));
+    return cocoa_gl_create_context_ns(view_ctx, bpp);
+}
+
+static void cocoa_gl_destroy_context(void *dg, QEMUGLContext ctx)
+{
+#ifdef CONFIG_EGL
+    if (egl_surface) {
+        eglDestroyContext(qemu_egl_display, ctx);
+        return;
+    }
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    [(NSOpenGLContext *)ctx release];
+#pragma clang diagnostic pop
+}
+
+static void cocoa_gl_flush()
+{
+#ifdef CONFIG_EGL
+    if (egl_surface) {
+        eglSwapBuffers(qemu_egl_display, egl_surface);
+        return;
+    }
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    [[NSOpenGLContext currentContext] flushBuffer];
+
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [(NSOpenGLContext *)view_ctx update];
+    });
+#pragma clang diagnostic pop
+}
+
+static void cocoa_scanout_disable(DisplayGL *dg)
+{
+    if (!dg->scanout_id) {
+        return;
+    }
+
+    dg->scanout_id = 0;
+
+    if (surface) {
+        surface_gl_destroy_texture(gls, surface);
+        surface_gl_create_texture(gls, surface);
+    }
+}
+
+static void cocoa_gl_render_surface(DisplayGL *dg)
+{
+    cocoa_scanout_disable(dg);
+
+    NSSize size = [cocoaView convertSizeToBacking:[cocoaView frame].size];
+
+    surface_gl_setup_viewport(gls, surface, size.width, size.height);
+    surface_gl_render_texture(gls, surface);
+    cocoa_gl_flush();
+}
+
+static void cocoa_gl_update(DisplayChangeListener *dcl,
+                            int x, int y, int w, int h)
+{
+    with_view_ctx(^{
+        surface_gl_update_texture(gls, surface, x, y, w, h);
+        dgs[qemu_console_get_index(dcl->con)].surface_dirty = true;
+    });
+}
+
+static void cocoa_gl_switch(DisplayChangeListener *dcl,
+                            DisplaySurface *new_surface)
+{
+    cocoa_switch(dcl, new_surface);
+
+    with_view_ctx(^{
+        surface_gl_create_texture(gls, new_surface);
+    });
+}
+
+static void cocoa_gl_refresh(DisplayChangeListener *dcl)
+{
+    cocoa_refresh(dcl);
+
+    with_view_ctx(^{
+        DisplayGL *dg = dgs + qemu_console_get_index(dcl->con);
+
+        if (dg->surface_dirty && surface) {
+            dg->surface_dirty = false;
+            cocoa_gl_render_surface(dg);
+        }
+    });
+}
+
+static bool cocoa_gl_scanout_get_enabled(void *dg)
+{
+    return ((DisplayGL *)dg)->scanout_id != 0;
+}
+
+static void cocoa_gl_scanout_disable(void *dg)
+{
+    with_view_ctx(^{
+        cocoa_scanout_disable((DisplayGL *)dg);
+    });
+}
+
+static void cocoa_gl_scanout_texture(void *dg,
+                                     uint32_t backing_id,
+                                     bool backing_y_0_top,
+                                     uint32_t backing_width,
+                                     uint32_t backing_height,
+                                     uint32_t x, uint32_t y,
+                                     uint32_t w, uint32_t h)
+{
+    ((DisplayGL *)dg)->scanout_id = backing_id;
+    ((DisplayGL *)dg)->scanout_y0_top = backing_y_0_top;
+}
+
+static void cocoa_gl_scanout_flush(DisplayChangeListener *dcl,
+                                   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+    DisplayGL *dg = dgs + qemu_console_get_index(dcl->con);
+
+    if (!dg->scanout_id) {
+        return;
+    }
+
+    with_view_ctx(^{
+        NSSize size = [cocoaView convertSizeToBacking:[cocoaView frame].size];
+
+        glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
+        glViewport(0, 0, size.width, size.height);
+        glBindTexture(GL_TEXTURE_2D, dg->scanout_id);
+        qemu_gl_run_texture_blit(gls, dg->scanout_y0_top);
+        cocoa_gl_flush();
+    });
+}
+
+static const DisplayGLOps dg_ops = {
+    .dpy_gl_ctx_create           = cocoa_gl_create_context,
+    .dpy_gl_ctx_destroy          = cocoa_gl_destroy_context,
+    .dpy_gl_ctx_make_current     = cocoa_gl_make_context_current,
+    .dpy_gl_scanout_get_enabled  = cocoa_gl_scanout_get_enabled,
+    .dpy_gl_scanout_disable      = cocoa_gl_scanout_disable,
+    .dpy_gl_scanout_texture      = cocoa_gl_scanout_texture,
+};
+
+static const DisplayChangeListenerOps dcl_gl_ops = {
+    .dpy_name                = "cocoa-gl",
+    .dpy_gfx_update          = cocoa_gl_update,
+    .dpy_gfx_switch          = cocoa_gl_switch,
+    .dpy_gfx_check_format    = console_gl_check_format,
+    .dpy_refresh             = cocoa_gl_refresh,
+
+    .dpy_gl_update           = cocoa_gl_scanout_flush,
+};
+
+#endif
+
+static void cocoa_display_early_init(DisplayOptions *o)
+{
+    assert(o->type == DISPLAY_TYPE_COCOA);
+    if (o->has_gl && o->gl) {
+        display_opengl = 1;
+    }
+}
+
 static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
 {
     COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
@@ -2054,9 +2332,58 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
         cursor_hide = 0;
     }
 
-    // register vga output callbacks
+    if (display_opengl) {
+#ifdef CONFIG_OPENGL
+        unsigned int console_count = 0;
+        while (qemu_console_lookup_by_index(console_count)) {
+            console_count++;
+        }
+
+        dgs = g_new0(DisplayGL, console_count);
+
+        for (unsigned int index = 0; index < console_count; index++) {
+            QemuConsole *con = qemu_console_lookup_by_index(index);
+            console_set_displayglcontext(con, dgs + index);
+        }
+
+        if (opts->gl == DISPLAYGL_MODE_ES) {
+#ifdef CONFIG_EGL
+            qemu_egl_init_dpy_cocoa(DISPLAYGL_MODE_ES);
+            view_ctx = qemu_egl_init_ctx();
+            dispatch_sync(dispatch_get_main_queue(), ^{
+                egl_surface = qemu_egl_init_surface(view_ctx, [cocoaView layer]);
+            });
+#else
+            error_report("OpenGLES without EGL is not supported - exiting");
+            exit(1);
+#endif
+        } else {
+            view_ctx = cocoa_gl_create_context_ns(nil, 32);
+#ifdef CONFIG_EGL
+            egl_surface = EGL_NO_SURFACE;
+#endif
+            cocoa_gl_make_context_current(NULL, view_ctx);
+        }
+
+        gls = qemu_gl_init_shader();
+
+        // register vga output callbacks
+        dcl.ops = &dcl_gl_ops;
+
+        register_displayglops(&dg_ops);
+#else
+        error_report("OpenGL is not enabled - exiting");
+        exit(1);
+#endif
+    } else {
+        // register vga output callbacks
+        dcl.ops = &dcl_ops;
+    }
+
     register_displaychangelistener(&dcl);
 
+    qatomic_set(&allow_events, true);
+
     qemu_event_init(&cbevent, false);
     cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init];
     qemu_clipboard_peer_register(&cbpeer);
@@ -2064,6 +2391,7 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
 
 static QemuDisplay qemu_display_cocoa = {
     .type       = DISPLAY_TYPE_COCOA,
+    .early_init = cocoa_display_early_init,
     .init       = cocoa_display_init,
 };
 
@@ -2073,3 +2401,7 @@ static void register_cocoa(void)
 }
 
 type_init(register_cocoa);
+
+#ifdef CONFIG_OPENGL
+module_dep("ui-opengl");
+#endif

+ 80 - 54
ui/console.c

@@ -127,7 +127,7 @@ struct QemuConsole {
     DisplayState *ds;
     DisplaySurface *surface;
     int dcls;
-    DisplayChangeListener *gl;
+    void *dg;
     bool gl_block;
     int window_id;
 
@@ -184,6 +184,7 @@ struct DisplayState {
     QLIST_HEAD(, DisplayChangeListener) listeners;
 };
 
+static const DisplayGLOps *display_gl_ops;
 static DisplayState *display_state;
 static QemuConsole *active_console;
 static QTAILQ_HEAD(, QemuConsole) consoles =
@@ -1103,8 +1104,14 @@ void console_select(unsigned int index)
                 }
             }
             if (s->surface) {
-                dpy_gfx_update(s, 0, 0, surface_width(s->surface),
-                               surface_height(s->surface));
+                int width = surface_width(s->surface);
+                int height = surface_height(s->surface);
+                if (display_gl_ops &&
+                    display_gl_ops->dpy_gl_scanout_get_enabled(s->dg)) {
+                    dpy_gl_update(s, 0, 0, width, height);
+                } else {
+                    dpy_gfx_update(s, 0, 0, width, height);
+                }
             }
         }
         if (ds->have_text) {
@@ -1454,26 +1461,30 @@ void qemu_free_displaysurface(DisplaySurface *surface)
     g_free(surface);
 }
 
-bool console_has_gl(QemuConsole *con)
+bool console_has_gl(void)
+{
+    return display_gl_ops != NULL;
+}
+
+void console_set_displayglcontext(QemuConsole *con, void *dg)
 {
-    return con->gl != NULL;
+    con->dg = dg;
 }
 
-static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
+static bool console_has_dmabuf(QemuConsole *con)
 {
-    if (dcl->ops->dpy_has_dmabuf) {
-        return dcl->ops->dpy_has_dmabuf(dcl);
+    if (display_gl_ops->dpy_has_dmabuf) {
+        return display_gl_ops->dpy_has_dmabuf(con->dg);
     }
 
-    if (dcl->ops->dpy_gl_scanout_dmabuf) {
+    if (display_gl_ops->dpy_gl_scanout_dmabuf) {
         return true;
     }
 
     return false;
 }
 
-static bool dpy_compatible_with(QemuConsole *con,
-                                DisplayChangeListener *dcl, Error **errp)
+static bool dpy_compatible_with(QemuConsole *con, Error **errp)
 {
     ERRP_GUARD();
     int flags;
@@ -1481,14 +1492,13 @@ static bool dpy_compatible_with(QemuConsole *con,
     flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
 
     if (flags & GRAPHIC_FLAGS_GL &&
-        !console_has_gl(con)) {
+        !console_has_gl()) {
         error_setg(errp, "The console requires a GL context.");
         return false;
-
     }
 
     if (flags & GRAPHIC_FLAGS_DMABUF &&
-        !displaychangelistener_has_dmabuf(dcl)) {
+        !console_has_dmabuf(con)) {
         error_setg(errp, "The console requires display DMABUF support.");
         return false;
     }
@@ -1496,6 +1506,16 @@ static bool dpy_compatible_with(QemuConsole *con,
     return true;
 }
 
+void register_displayglops(const DisplayGLOps *dg_ops)
+{
+    if (display_gl_ops) {
+        error_report("can't register two opengl operators");
+        exit(1);
+    }
+
+    display_gl_ops = dg_ops;
+}
+
 void register_displaychangelistener(DisplayChangeListener *dcl)
 {
     static const char nodev[] =
@@ -1506,20 +1526,18 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
 
     assert(!dcl->ds);
 
-    if (dcl->ops->dpy_gl_ctx_create) {
-        /* display has opengl support */
-        assert(dcl->con);
-        if (dcl->con->gl) {
-            fprintf(stderr, "can't register two opengl displays (%s, %s)\n",
-                    dcl->ops->dpy_name, dcl->con->gl->ops->dpy_name);
+    if (dcl->con) {
+        if (!dpy_compatible_with(dcl->con, &err)) {
+            error_report_err(err);
             exit(1);
         }
-        dcl->con->gl = dcl;
-    }
-
-    if (dcl->con && !dpy_compatible_with(dcl->con, dcl, &err)) {
-        error_report_err(err);
-        exit(1);
+    } else {
+        QTAILQ_FOREACH(con, &consoles, next) {
+            if (!dpy_compatible_with(con, &err)) {
+                error_report_err(err);
+                exit(1);
+            }
+        }
     }
 
     trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
@@ -1836,26 +1854,26 @@ bool dpy_cursor_define_supported(QemuConsole *con)
 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
                                 struct QEMUGLParams *qparams)
 {
-    assert(con->gl);
-    return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
+    assert(display_gl_ops);
+    return display_gl_ops->dpy_gl_ctx_create(con->dg, qparams);
 }
 
 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
 {
-    assert(con->gl);
-    con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
+    assert(display_gl_ops);
+    display_gl_ops->dpy_gl_ctx_destroy(con->dg, ctx);
 }
 
 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
 {
-    assert(con->gl);
-    return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
+    assert(display_gl_ops);
+    return display_gl_ops->dpy_gl_ctx_make_current(con->dg, ctx);
 }
 
 void dpy_gl_scanout_disable(QemuConsole *con)
 {
-    assert(con->gl);
-    con->gl->ops->dpy_gl_scanout_disable(con->gl);
+    assert(display_gl_ops);
+    display_gl_ops->dpy_gl_scanout_disable(con->dg);
 }
 
 void dpy_gl_scanout_texture(QemuConsole *con,
@@ -1866,56 +1884,64 @@ void dpy_gl_scanout_texture(QemuConsole *con,
                             uint32_t x, uint32_t y,
                             uint32_t width, uint32_t height)
 {
-    assert(con->gl);
-    con->gl->ops->dpy_gl_scanout_texture(con->gl, backing_id,
-                                         backing_y_0_top,
-                                         backing_width, backing_height,
-                                         x, y, width, height);
+    assert(display_gl_ops);
+    display_gl_ops->dpy_gl_scanout_texture(con->dg, backing_id,
+                                           backing_y_0_top,
+                                           backing_width, backing_height,
+                                           x, y, width, height);
 }
 
 void dpy_gl_scanout_dmabuf(QemuConsole *con,
                            QemuDmaBuf *dmabuf)
 {
-    assert(con->gl);
-    con->gl->ops->dpy_gl_scanout_dmabuf(con->gl, dmabuf);
+    assert(display_gl_ops);
+    display_gl_ops->dpy_gl_scanout_dmabuf(con->dg, dmabuf);
 }
 
 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
                           bool have_hot, uint32_t hot_x, uint32_t hot_y)
 {
-    assert(con->gl);
+    assert(display_gl_ops);
 
-    if (con->gl->ops->dpy_gl_cursor_dmabuf) {
-        con->gl->ops->dpy_gl_cursor_dmabuf(con->gl, dmabuf,
-                                           have_hot, hot_x, hot_y);
+    if (display_gl_ops->dpy_gl_cursor_dmabuf) {
+        display_gl_ops->dpy_gl_cursor_dmabuf(con->dg, dmabuf,
+                                             have_hot, hot_x, hot_y);
     }
 }
 
 void dpy_gl_cursor_position(QemuConsole *con,
                             uint32_t pos_x, uint32_t pos_y)
 {
-    assert(con->gl);
+    assert(display_gl_ops);
 
-    if (con->gl->ops->dpy_gl_cursor_position) {
-        con->gl->ops->dpy_gl_cursor_position(con->gl, pos_x, pos_y);
+    if (display_gl_ops->dpy_gl_cursor_position) {
+        display_gl_ops->dpy_gl_cursor_position(con->dg, pos_x, pos_y);
     }
 }
 
 void dpy_gl_release_dmabuf(QemuConsole *con,
                           QemuDmaBuf *dmabuf)
 {
-    assert(con->gl);
+    assert(display_gl_ops);
 
-    if (con->gl->ops->dpy_gl_release_dmabuf) {
-        con->gl->ops->dpy_gl_release_dmabuf(con->gl, dmabuf);
+    if (display_gl_ops->dpy_gl_release_dmabuf) {
+        display_gl_ops->dpy_gl_release_dmabuf(con->dg, dmabuf);
     }
 }
 
 void dpy_gl_update(QemuConsole *con,
                    uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
-    assert(con->gl);
-    con->gl->ops->dpy_gl_update(con->gl, x, y, w, h);
+    DisplayChangeListener *dcl;
+
+    QLIST_FOREACH(dcl, &con->ds->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
+        if (dcl->ops->dpy_gl_update) {
+        dcl->ops->dpy_gl_update(dcl, x, y, w, h);
+    }
+}
 }
 
 /***********************************************************/
@@ -2026,7 +2052,7 @@ void graphic_console_close(QemuConsole *con)
     object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
     graphic_console_set_hwops(con, &unused_ops, NULL);
 
-    if (con->gl) {
+    if (display_gl_ops) {
         dpy_gl_scanout_disable(con);
     }
     surface = qemu_create_placeholder_surface(width, height, unplugged);

+ 3 - 5
ui/egl-context.c

@@ -1,8 +1,7 @@
 #include "qemu/osdep.h"
 #include "ui/egl-context.h"
 
-QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
-                                      QEMUGLParams *params)
+QEMUGLContext qemu_egl_create_context(void *dg, QEMUGLParams *params)
 {
    EGLContext ctx;
    EGLint ctx_att_core[] = {
@@ -24,13 +23,12 @@ QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
    return ctx;
 }
 
-void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+void qemu_egl_destroy_context(void *dg, QEMUGLContext ctx)
 {
     eglDestroyContext(qemu_egl_display, ctx);
 }
 
-int qemu_egl_make_context_current(DisplayChangeListener *dcl,
-                                  QEMUGLContext ctx)
+int qemu_egl_make_context_current(void *dg, QEMUGLContext ctx)
 {
    return eglMakeCurrent(qemu_egl_display,
                          EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);

+ 36 - 27
ui/egl-headless.c

@@ -38,23 +38,27 @@ static void egl_gfx_switch(DisplayChangeListener *dcl,
     edpy->ds = new_surface;
 }
 
-static QEMUGLContext egl_create_context(DisplayChangeListener *dcl,
+static QEMUGLContext egl_create_context(void *dg,
                                         QEMUGLParams *params)
 {
     eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
                    qemu_egl_rn_ctx);
-    return qemu_egl_create_context(dcl, params);
+    return qemu_egl_create_context(dg, params);
 }
 
-static void egl_scanout_disable(DisplayChangeListener *dcl)
+static bool egl_scanout_get_enabled(void *dg)
 {
-    egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+    return ((egl_dpy *)dg)->guest_fb.texture != 0;
+}
 
+static void egl_scanout_disable(void *dg)
+{
+    egl_dpy *edpy = dg;
     egl_fb_destroy(&edpy->guest_fb);
     egl_fb_destroy(&edpy->blit_fb);
 }
 
-static void egl_scanout_texture(DisplayChangeListener *dcl,
+static void egl_scanout_texture(void *dg,
                                 uint32_t backing_id,
                                 bool backing_y_0_top,
                                 uint32_t backing_width,
@@ -62,7 +66,7 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
                                 uint32_t x, uint32_t y,
                                 uint32_t w, uint32_t h)
 {
-    egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+    egl_dpy *edpy = dg;
 
     edpy->y_0_top = backing_y_0_top;
 
@@ -78,24 +82,23 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
     }
 }
 
-static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
-                               QemuDmaBuf *dmabuf)
+static void egl_scanout_dmabuf(void *dg, QemuDmaBuf *dmabuf)
 {
     egl_dmabuf_import_texture(dmabuf);
     if (!dmabuf->texture) {
         return;
     }
 
-    egl_scanout_texture(dcl, dmabuf->texture,
+    egl_scanout_texture(dg, dmabuf->texture,
                         false, dmabuf->width, dmabuf->height,
                         0, 0, dmabuf->width, dmabuf->height);
 }
 
-static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
+static void egl_cursor_dmabuf(void *dg,
                               QemuDmaBuf *dmabuf, bool have_hot,
                               uint32_t hot_x, uint32_t hot_y)
 {
-    egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+    egl_dpy *edpy = dg;
 
     if (dmabuf) {
         egl_dmabuf_import_texture(dmabuf);
@@ -109,17 +112,16 @@ static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
     }
 }
 
-static void egl_cursor_position(DisplayChangeListener *dcl,
+static void egl_cursor_position(void *dg,
                                 uint32_t pos_x, uint32_t pos_y)
 {
-    egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+    egl_dpy *edpy = dg;
 
     edpy->pos_x = pos_x;
     edpy->pos_y = pos_y;
 }
 
-static void egl_release_dmabuf(DisplayChangeListener *dcl,
-                               QemuDmaBuf *dmabuf)
+static void egl_release_dmabuf(void *dg, QemuDmaBuf *dmabuf)
 {
     egl_dmabuf_release_texture(dmabuf);
 }
@@ -151,22 +153,26 @@ static void egl_scanout_flush(DisplayChangeListener *dcl,
     dpy_gfx_update(edpy->dcl.con, x, y, w, h);
 }
 
-static const DisplayChangeListenerOps egl_ops = {
+static const DisplayGLOps dg_egl_ops = {
+    .dpy_gl_ctx_create       = egl_create_context,
+    .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
+    .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
+
+    .dpy_gl_scanout_get_enabled = egl_scanout_get_enabled,
+    .dpy_gl_scanout_disable     = egl_scanout_disable,
+    .dpy_gl_scanout_texture     = egl_scanout_texture,
+    .dpy_gl_scanout_dmabuf      = egl_scanout_dmabuf,
+    .dpy_gl_cursor_dmabuf       = egl_cursor_dmabuf,
+    .dpy_gl_cursor_position     = egl_cursor_position,
+    .dpy_gl_release_dmabuf      = egl_release_dmabuf,
+};
+
+static const DisplayChangeListenerOps dcl_egl_ops = {
     .dpy_name                = "egl-headless",
     .dpy_refresh             = egl_refresh,
     .dpy_gfx_update          = egl_gfx_update,
     .dpy_gfx_switch          = egl_gfx_switch,
 
-    .dpy_gl_ctx_create       = egl_create_context,
-    .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
-    .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
-
-    .dpy_gl_scanout_disable  = egl_scanout_disable,
-    .dpy_gl_scanout_texture  = egl_scanout_texture,
-    .dpy_gl_scanout_dmabuf   = egl_scanout_dmabuf,
-    .dpy_gl_cursor_dmabuf    = egl_cursor_dmabuf,
-    .dpy_gl_cursor_position  = egl_cursor_position,
-    .dpy_gl_release_dmabuf   = egl_release_dmabuf,
     .dpy_gl_update           = egl_scanout_flush,
 };
 
@@ -187,6 +193,8 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
         exit(1);
     }
 
+    register_displayglops(&dg_egl_ops);
+
     for (idx = 0;; idx++) {
         con = qemu_console_lookup_by_index(idx);
         if (!con || !qemu_console_is_graphic(con)) {
@@ -195,8 +203,9 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
 
         edpy = g_new0(egl_dpy, 1);
         edpy->dcl.con = con;
-        edpy->dcl.ops = &egl_ops;
+        edpy->dcl.ops = &dcl_egl_ops;
         edpy->gls = qemu_gl_init_shader();
+        console_set_displayglcontext(con, edpy);
         register_displaychangelistener(&edpy->dcl);
     }
 }

+ 74 - 62
ui/egl-helpers.c

@@ -291,7 +291,7 @@ void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf)
 
 /* ---------------------------------------------------------------------- */
 
-EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
+EGLSurface qemu_egl_init_surface(EGLContext ectx, EGLNativeWindowType win)
 {
     EGLSurface esurface;
     EGLBoolean b;
@@ -315,6 +315,70 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
 
 /* ---------------------------------------------------------------------- */
 
+static int qemu_egl_init_dpy(EGLDisplay dpy, DisplayGLMode mode)
+{
+    static const EGLint conf_att_core[] = {
+        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+        EGL_RED_SIZE,   5,
+        EGL_GREEN_SIZE, 5,
+        EGL_BLUE_SIZE,  5,
+        EGL_ALPHA_SIZE, 0,
+        EGL_NONE,
+    };
+    static const EGLint conf_att_gles[] = {
+        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_RED_SIZE,   5,
+        EGL_GREEN_SIZE, 5,
+        EGL_BLUE_SIZE,  5,
+        EGL_ALPHA_SIZE, 0,
+        EGL_NONE,
+    };
+    EGLint major, minor;
+    EGLBoolean b;
+    EGLint n;
+    bool gles = (mode == DISPLAYGL_MODE_ES);
+
+    qemu_egl_display = dpy;
+
+    b = eglInitialize(qemu_egl_display, &major, &minor);
+    if (b == EGL_FALSE) {
+        error_report("egl: eglInitialize failed");
+        return -1;
+    }
+
+    b = eglBindAPI(gles ?  EGL_OPENGL_ES_API : EGL_OPENGL_API);
+    if (b == EGL_FALSE) {
+        error_report("egl: eglBindAPI failed (%s mode)",
+                     gles ? "gles" : "core");
+        return -1;
+    }
+
+    b = eglChooseConfig(qemu_egl_display,
+                        gles ? conf_att_gles : conf_att_core,
+                        &qemu_egl_config, 1, &n);
+    if (b == EGL_FALSE || n != 1) {
+        error_report("egl: eglChooseConfig failed (%s mode)",
+                     gles ? "gles" : "core");
+        return -1;
+    }
+
+    qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE;
+    return 0;
+}
+
+int qemu_egl_init_dpy_cocoa(DisplayGLMode mode)
+{
+    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (dpy == EGL_NO_DISPLAY) {
+        error_report("egl: eglGetDisplay failed");
+        return -1;
+    }
+
+    return qemu_egl_init_dpy(dpy, mode);
+}
+
 #if defined(CONFIG_X11) || defined(CONFIG_GBM)
 
 /*
@@ -345,8 +409,9 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
  * platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem
  * like mesa will be able to advertise these (even though it can do EGL 1.5).
  */
-static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
-                                       EGLenum platform)
+static int qemu_egl_init_dpy_platform(EGLNativeDisplayType native,
+                                      EGLenum platform,
+                                      DisplayGLMode mode)
 {
     EGLDisplay dpy = EGL_NO_DISPLAY;
 
@@ -363,83 +428,30 @@ static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
         /* fallback */
         dpy = eglGetDisplay(native);
     }
-    return dpy;
-}
 
-static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
-                             EGLenum platform,
-                             DisplayGLMode mode)
-{
-    static const EGLint conf_att_core[] = {
-        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-        EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
-        EGL_RED_SIZE,   5,
-        EGL_GREEN_SIZE, 5,
-        EGL_BLUE_SIZE,  5,
-        EGL_ALPHA_SIZE, 0,
-        EGL_NONE,
-    };
-    static const EGLint conf_att_gles[] = {
-        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-        EGL_RED_SIZE,   5,
-        EGL_GREEN_SIZE, 5,
-        EGL_BLUE_SIZE,  5,
-        EGL_ALPHA_SIZE, 0,
-        EGL_NONE,
-    };
-    EGLint major, minor;
-    EGLBoolean b;
-    EGLint n;
-    bool gles = (mode == DISPLAYGL_MODE_ES);
-
-    qemu_egl_display = qemu_egl_get_display(dpy, platform);
-    if (qemu_egl_display == EGL_NO_DISPLAY) {
+    if (dpy == EGL_NO_DISPLAY) {
         error_report("egl: eglGetDisplay failed");
         return -1;
     }
 
-    b = eglInitialize(qemu_egl_display, &major, &minor);
-    if (b == EGL_FALSE) {
-        error_report("egl: eglInitialize failed");
-        return -1;
-    }
-
-    b = eglBindAPI(gles ?  EGL_OPENGL_ES_API : EGL_OPENGL_API);
-    if (b == EGL_FALSE) {
-        error_report("egl: eglBindAPI failed (%s mode)",
-                     gles ? "gles" : "core");
-        return -1;
-    }
-
-    b = eglChooseConfig(qemu_egl_display,
-                        gles ? conf_att_gles : conf_att_core,
-                        &qemu_egl_config, 1, &n);
-    if (b == EGL_FALSE || n != 1) {
-        error_report("egl: eglChooseConfig failed (%s mode)",
-                     gles ? "gles" : "core");
-        return -1;
-    }
-
-    qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE;
-    return 0;
+    return qemu_egl_init_dpy(dpy, mode);
 }
 
 int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
 {
 #ifdef EGL_KHR_platform_x11
-    return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode);
+    return qemu_egl_init_dpy_platform(dpy, EGL_PLATFORM_X11_KHR, mode);
 #else
-    return qemu_egl_init_dpy(dpy, 0, mode);
+    return qemu_egl_init_dpy_platform(dpy, 0, mode);
 #endif
 }
 
 int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
 {
 #ifdef EGL_MESA_platform_gbm
-    return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode);
+    return qemu_egl_init_dpy_platform(dpy, EGL_PLATFORM_GBM_MESA, mode);
 #else
-    return qemu_egl_init_dpy(dpy, 0, mode);
+    return qemu_egl_init_dpy_platform(dpy, 0, mode);
 #endif
 }
 

+ 22 - 22
ui/gtk-egl.c

@@ -53,7 +53,7 @@ void gd_egl_init(VirtualConsole *vc)
     }
 
     vc->gfx.ectx = qemu_egl_init_ctx();
-    vc->gfx.esurface = qemu_egl_init_surface_x11
+    vc->gfx.esurface = qemu_egl_init_surface
         (vc->gfx.ectx, (EGLNativeWindowType)x11_window);
 
     assert(vc->gfx.esurface);
@@ -164,32 +164,36 @@ void gd_egl_switch(DisplayChangeListener *dcl,
     }
 }
 
-QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl,
-                                    QEMUGLParams *params)
+QEMUGLContext gd_egl_create_context(void *dg, QEMUGLParams *params)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
 
     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
                    vc->gfx.esurface, vc->gfx.ectx);
-    return qemu_egl_create_context(dcl, params);
+    return qemu_egl_create_context(dg, params);
 }
 
-void gd_egl_scanout_disable(DisplayChangeListener *dcl)
+bool gd_egl_scanout_get_enabled(void *dg)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    return ((VirtualConsole *)dg)->gfx.scanout_mode;
+}
+
+void gd_egl_scanout_disable(void *dg)
+{
+    VirtualConsole *vc = dg;
 
     vc->gfx.w = 0;
     vc->gfx.h = 0;
     gtk_egl_set_scanout_mode(vc, false);
 }
 
-void gd_egl_scanout_texture(DisplayChangeListener *dcl,
+void gd_egl_scanout_texture(void *dg,
                             uint32_t backing_id, bool backing_y_0_top,
                             uint32_t backing_width, uint32_t backing_height,
                             uint32_t x, uint32_t y,
                             uint32_t w, uint32_t h)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
 
     vc->gfx.x = x;
     vc->gfx.y = y;
@@ -205,8 +209,7 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
                          backing_id, false);
 }
 
-void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
-                           QemuDmaBuf *dmabuf)
+void gd_egl_scanout_dmabuf(void *dg, QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
     egl_dmabuf_import_texture(dmabuf);
@@ -214,18 +217,18 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
         return;
     }
 
-    gd_egl_scanout_texture(dcl, dmabuf->texture,
+    gd_egl_scanout_texture(dg, dmabuf->texture,
                            false, dmabuf->width, dmabuf->height,
                            0, 0, dmabuf->width, dmabuf->height);
 #endif
 }
 
-void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
+void gd_egl_cursor_dmabuf(void *dg,
                           QemuDmaBuf *dmabuf, bool have_hot,
                           uint32_t hot_x, uint32_t hot_y)
 {
 #ifdef CONFIG_GBM
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
 
     if (dmabuf) {
         egl_dmabuf_import_texture(dmabuf);
@@ -240,17 +243,15 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
 #endif
 }
 
-void gd_egl_cursor_position(DisplayChangeListener *dcl,
-                            uint32_t pos_x, uint32_t pos_y)
+void gd_egl_cursor_position(void *dg, uint32_t pos_x, uint32_t pos_y)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
 
     vc->gfx.cursor_x = pos_x * vc->gfx.scale_x;
     vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
 }
 
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-                           QemuDmaBuf *dmabuf)
+void gd_egl_release_dmabuf(void *dg, QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
     egl_dmabuf_release_texture(dmabuf);
@@ -304,10 +305,9 @@ void gtk_egl_init(DisplayGLMode mode)
     display_opengl = 1;
 }
 
-int gd_egl_make_current(DisplayChangeListener *dcl,
-                        QEMUGLContext ctx)
+int gd_egl_make_current(void *dg, QEMUGLContext ctx)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
 
     return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
                           vc->gfx.esurface, ctx);

+ 16 - 16
ui/gtk-gl-area.c

@@ -139,10 +139,9 @@ void gd_gl_area_switch(DisplayChangeListener *dcl,
     }
 }
 
-QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
-                                        QEMUGLParams *params)
+QEMUGLContext gd_gl_area_create_context(void *dg, QEMUGLParams *params)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
     GdkWindow *window;
     GdkGLContext *ctx;
     GError *err = NULL;
@@ -168,12 +167,17 @@ QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
     return ctx;
 }
 
-void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+void gd_gl_area_destroy_context(void *dg, QEMUGLContext ctx)
 {
     /* FIXME */
 }
 
-void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
+bool gd_gl_area_scanout_get_enabled(void *dg)
+{
+    return ((VirtualConsole *)dg)->gfx.scanout_mode;
+}
+
+void gd_gl_area_scanout_texture(void *dg,
                                 uint32_t backing_id,
                                 bool backing_y_0_top,
                                 uint32_t backing_width,
@@ -181,7 +185,7 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
                                 uint32_t x, uint32_t y,
                                 uint32_t w, uint32_t h)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
 
     vc->gfx.x = x;
     vc->gfx.y = y;
@@ -201,11 +205,9 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
                          backing_id, false);
 }
 
-void gd_gl_area_scanout_disable(DisplayChangeListener *dcl)
+void gd_gl_area_scanout_disable(void *dg)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
-
-    gtk_gl_area_set_scanout_mode(vc, false);
+    gtk_gl_area_set_scanout_mode(dg, false);
 }
 
 void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
@@ -216,11 +218,10 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
     gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
 }
 
-void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
-                               QemuDmaBuf *dmabuf)
+void gd_gl_area_scanout_dmabuf(void *dg, QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
 
     gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
     egl_dmabuf_import_texture(dmabuf);
@@ -228,7 +229,7 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
         return;
     }
 
-    gd_gl_area_scanout_texture(dcl, dmabuf->texture,
+    gd_gl_area_scanout_texture(dg, dmabuf->texture,
                                false, dmabuf->width, dmabuf->height,
                                0, 0, dmabuf->width, dmabuf->height);
 #endif
@@ -239,8 +240,7 @@ void gtk_gl_area_init(void)
     display_opengl = 1;
 }
 
-int gd_gl_area_make_current(DisplayChangeListener *dcl,
-                            QEMUGLContext ctx)
+int gd_gl_area_make_current(void *dg, QEMUGLContext ctx)
 {
     gdk_gl_context_make_current(ctx);
     return 0;

+ 41 - 21
ui/gtk.c

@@ -341,7 +341,7 @@ static void gd_update_full_redraw(VirtualConsole *vc)
     int ww, wh;
     ww = gdk_window_get_width(gtk_widget_get_window(area));
     wh = gdk_window_get_height(gtk_widget_get_window(area));
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
     if (vc->gfx.gls && gtk_use_gl_area) {
         gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
         return;
@@ -560,11 +560,11 @@ static const DisplayChangeListenerOps dcl_ops = {
 };
 
 
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
 
-static bool gd_has_dmabuf(DisplayChangeListener *dcl)
+static bool gd_has_dmabuf(void *dg)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = dg;
 
     if (gtk_use_gl_area && !gtk_widget_get_realized(vc->gfx.drawing_area)) {
         /* FIXME: Assume it will work, actual check done after realize */
@@ -577,6 +577,17 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl)
 
 /** DisplayState Callbacks (opengl version) **/
 
+static const DisplayGLOps dg_gl_area_ops = {
+    .dpy_gl_ctx_create          = gd_gl_area_create_context,
+    .dpy_gl_ctx_destroy         = gd_gl_area_destroy_context,
+    .dpy_gl_ctx_make_current    = gd_gl_area_make_current,
+    .dpy_gl_scanout_get_enabled = gd_gl_area_scanout_get_enabled,
+    .dpy_gl_scanout_texture     = gd_gl_area_scanout_texture,
+    .dpy_gl_scanout_disable     = gd_gl_area_scanout_disable,
+    .dpy_gl_scanout_dmabuf      = gd_gl_area_scanout_dmabuf,
+    .dpy_has_dmabuf             = gd_has_dmabuf,
+};
+
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
     .dpy_name             = "gtk-egl",
     .dpy_gfx_update       = gd_gl_area_update,
@@ -586,14 +597,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
     .dpy_mouse_set        = gd_mouse_set,
     .dpy_cursor_define    = gd_cursor_define,
 
-    .dpy_gl_ctx_create       = gd_gl_area_create_context,
-    .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
-    .dpy_gl_ctx_make_current = gd_gl_area_make_current,
-    .dpy_gl_scanout_texture  = gd_gl_area_scanout_texture,
-    .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
-    .dpy_gl_update           = gd_gl_area_scanout_flush,
-    .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
-    .dpy_has_dmabuf          = gd_has_dmabuf,
+    .dpy_gl_update        = gd_gl_area_scanout_flush,
 };
 
 #ifdef CONFIG_X11
@@ -607,6 +611,10 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
     .dpy_mouse_set        = gd_mouse_set,
     .dpy_cursor_define    = gd_cursor_define,
 
+    .dpy_gl_update        = gd_egl_scanout_flush,
+};
+
+static const DisplayGLOps dg_egl_ops = {
     .dpy_gl_ctx_create       = gd_egl_create_context,
     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
     .dpy_gl_ctx_make_current = gd_egl_make_current,
@@ -616,13 +624,12 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
     .dpy_gl_cursor_dmabuf    = gd_egl_cursor_dmabuf,
     .dpy_gl_cursor_position  = gd_egl_cursor_position,
     .dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
-    .dpy_gl_update           = gd_egl_scanout_flush,
     .dpy_has_dmabuf          = gd_has_dmabuf,
 };
 
 #endif
 
-#endif /* CONFIG_OPENGL */
+#endif /* defined(CONFIG_OPENGL) && defined(CONFIG_EGL) */
 
 /** QEMU Events **/
 
@@ -688,7 +695,7 @@ static void gd_set_ui_size(VirtualConsole *vc, gint width, gint height)
     dpy_set_ui_info(vc->gfx.dcl.con, &info);
 }
 
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
 
 static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
                                 void *opaque)
@@ -742,7 +749,7 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
     int ww, wh;
     int fbw, fbh;
 
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
     if (vc->gfx.gls) {
         if (gtk_use_gl_area) {
             /* invoke render callback please */
@@ -1840,7 +1847,7 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
 {
     g_signal_connect(vc->gfx.drawing_area, "draw",
                      G_CALLBACK(gd_draw_event), vc);
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
     if (gtk_use_gl_area) {
         /* wire up GtkGlArea events */
         g_signal_connect(vc->gfx.drawing_area, "render",
@@ -1954,7 +1961,7 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
     return machine_menu;
 }
 
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
 static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
 {
     gtk_gl_area_make_current(area);
@@ -1977,7 +1984,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
     vc->gfx.scale_x = 1.0;
     vc->gfx.scale_y = 1.0;
 
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
     if (display_opengl) {
         if (gtk_use_gl_area) {
             vc->gfx.drawing_area = gtk_gl_area_new();
@@ -2032,6 +2039,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
     vc->gfx.kbd = qkbd_state_init(con);
     vc->gfx.dcl.con = con;
 
+    console_set_displayglcontext(con, vc);
     register_displaychangelistener(&vc->gfx.dcl);
 
     gd_connect_vc_gfx_signals(vc);
@@ -2121,6 +2129,18 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
     separator = gtk_separator_menu_item_new();
     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
 
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
+    if (display_opengl) {
+        if (gtk_use_gl_area) {
+            register_displayglops(&dg_gl_area_ops);
+        } else {
+#ifdef CONFIG_X11
+            register_displayglops(&dg_egl_ops);
+#endif
+        }
+    }
+#endif
+
     /* gfx */
     for (vc = 0;; vc++) {
         con = qemu_console_lookup_by_index(vc);
@@ -2305,7 +2325,7 @@ static void early_gtk_display_init(DisplayOptions *opts)
 
     assert(opts->type == DISPLAY_TYPE_GTK);
     if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) {
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
 #if defined(GDK_WINDOWING_WAYLAND)
         if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
             gtk_use_gl_area = true;
@@ -2341,6 +2361,6 @@ static void register_gtk(void)
 
 type_init(register_gtk);
 
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
 module_dep("ui-opengl");
 #endif

+ 9 - 5
ui/meson.build

@@ -51,13 +51,15 @@ if config_host.has_key('CONFIG_OPENGL')
   opengl_ss = ss.source_set()
   opengl_ss.add(gbm)
   opengl_ss.add(when: [opengl, pixman, 'CONFIG_OPENGL'],
-               if_true: files('shader.c', 'console-gl.c', 'egl-helpers.c', 'egl-context.c'))
+               if_true: files('shader.c', 'console-gl.c'))
+  opengl_ss.add(when: [opengl, pixman, 'CONFIG_OPENGL', 'CONFIG_EGL'],
+               if_true: files('egl-helpers.c', 'egl-context.c'))
   ui_modules += {'opengl' : opengl_ss}
 endif
 
 if config_host.has_key('CONFIG_OPENGL') and gbm.found()
   egl_headless_ss = ss.source_set()
-  egl_headless_ss.add(when: [opengl, gbm, pixman, 'CONFIG_OPENGL'],
+  egl_headless_ss.add(when: [opengl, gbm, pixman, 'CONFIG_OPENGL', 'CONFIG_EGL'],
                       if_true: files('egl-headless.c'))
   ui_modules += {'egl-headless' : egl_headless_ss}
 endif
@@ -68,8 +70,10 @@ if gtk.found()
   gtk_ss = ss.source_set()
   gtk_ss.add(gtk, vte, pixman, files('gtk.c', 'gtk-clipboard.c'))
   gtk_ss.add(when: x11, if_true: files('x_keymap.c'))
-  gtk_ss.add(when: [opengl, 'CONFIG_OPENGL'], if_true: files('gtk-gl-area.c'))
-  gtk_ss.add(when: [x11, opengl, 'CONFIG_OPENGL'], if_true: files('gtk-egl.c'))
+  gtk_ss.add(when: [opengl, 'CONFIG_OPENGL', 'CONFIG_EGL'],
+             if_true: files('gtk-gl-area.c'))
+  gtk_ss.add(when: [x11, opengl, 'CONFIG_OPENGL', 'CONFIG_EGL'],
+             if_true: files('gtk-egl.c'))
   ui_modules += {'gtk' : gtk_ss}
 endif
 
@@ -82,7 +86,7 @@ if sdl.found()
     'sdl2-input.c',
     'sdl2.c',
   ))
-  sdl_ss.add(when: [opengl, 'CONFIG_OPENGL'], if_true: files('sdl2-gl.c'))
+  sdl_ss.add(when: [opengl, 'CONFIG_OPENGL', 'CONFIG_EGL'], if_true: files('sdl2-gl.c'))
   sdl_ss.add(when: x11, if_true: files('x_keymap.c'))
   ui_modules += {'sdl' : sdl_ss}
 endif

+ 14 - 11
ui/sdl2-gl.c

@@ -133,10 +133,9 @@ void sdl2_gl_redraw(struct sdl2_console *scon)
     }
 }
 
-QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl,
-                                     QEMUGLParams *params)
+QEMUGLContext sdl2_gl_create_context(void *dg, QEMUGLParams *params)
 {
-    struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+    struct sdl2_console *scon = dg;
     SDL_GLContext ctx;
 
     assert(scon->opengl);
@@ -168,17 +167,16 @@ QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl,
     return (QEMUGLContext)ctx;
 }
 
-void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+void sdl2_gl_destroy_context(void *dg, QEMUGLContext ctx)
 {
     SDL_GLContext sdlctx = (SDL_GLContext)ctx;
 
     SDL_GL_DeleteContext(sdlctx);
 }
 
-int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
-                                 QEMUGLContext ctx)
+int sdl2_gl_make_context_current(void *dg, QEMUGLContext ctx)
 {
-    struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+    struct sdl2_console *scon = dg;
     SDL_GLContext sdlctx = (SDL_GLContext)ctx;
 
     assert(scon->opengl);
@@ -186,9 +184,9 @@ int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
     return SDL_GL_MakeCurrent(scon->real_window, sdlctx);
 }
 
-void sdl2_gl_scanout_disable(DisplayChangeListener *dcl)
+void sdl2_gl_scanout_disable(void *dg)
 {
-    struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+    struct sdl2_console *scon = dg;
 
     assert(scon->opengl);
     scon->w = 0;
@@ -196,7 +194,12 @@ void sdl2_gl_scanout_disable(DisplayChangeListener *dcl)
     sdl2_set_scanout_mode(scon, false);
 }
 
-void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
+bool sdl2_gl_scanout_get_enabled(void *dg)
+{
+    return ((struct sdl2_console *)dg)->scanout_mode;
+}
+
+void sdl2_gl_scanout_texture(void *dg,
                              uint32_t backing_id,
                              bool backing_y_0_top,
                              uint32_t backing_width,
@@ -204,7 +207,7 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
                              uint32_t x, uint32_t y,
                              uint32_t w, uint32_t h)
 {
-    struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+    struct sdl2_console *scon = dg;
 
     assert(scon->opengl);
     scon->x = x;

+ 22 - 11
ui/sdl2.c

@@ -85,7 +85,7 @@ void sdl2_window_create(struct sdl2_console *scon)
     if (scon->hidden) {
         flags |= SDL_WINDOW_HIDDEN;
     }
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
     if (scon->opengl) {
         flags |= SDL_WINDOW_OPENGL;
     }
@@ -129,7 +129,7 @@ void sdl2_window_resize(struct sdl2_console *scon)
 static void sdl2_redraw(struct sdl2_console *scon)
 {
     if (scon->opengl) {
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
         sdl2_gl_redraw(scon);
 #endif
     } else {
@@ -768,7 +768,16 @@ static const DisplayChangeListenerOps dcl_2d_ops = {
     .dpy_cursor_define    = sdl_mouse_define,
 };
 
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
+static const DisplayGLOps dg_gl_ops = {
+    .dpy_gl_ctx_create          = sdl2_gl_create_context,
+    .dpy_gl_ctx_destroy         = sdl2_gl_destroy_context,
+    .dpy_gl_ctx_make_current    = sdl2_gl_make_context_current,
+    .dpy_gl_scanout_get_enabled = sdl2_gl_scanout_get_enabled,
+    .dpy_gl_scanout_disable     = sdl2_gl_scanout_disable,
+    .dpy_gl_scanout_texture     = sdl2_gl_scanout_texture,
+};
+
 static const DisplayChangeListenerOps dcl_gl_ops = {
     .dpy_name                = "sdl2-gl",
     .dpy_gfx_update          = sdl2_gl_update,
@@ -778,11 +787,6 @@ static const DisplayChangeListenerOps dcl_gl_ops = {
     .dpy_mouse_set           = sdl_mouse_warp,
     .dpy_cursor_define       = sdl_mouse_define,
 
-    .dpy_gl_ctx_create       = sdl2_gl_create_context,
-    .dpy_gl_ctx_destroy      = sdl2_gl_destroy_context,
-    .dpy_gl_ctx_make_current = sdl2_gl_make_context_current,
-    .dpy_gl_scanout_disable  = sdl2_gl_scanout_disable,
-    .dpy_gl_scanout_texture  = sdl2_gl_scanout_texture,
     .dpy_gl_update           = sdl2_gl_scanout_flush,
 };
 #endif
@@ -791,7 +795,7 @@ static void sdl2_display_early_init(DisplayOptions *o)
 {
     assert(o->type == DISPLAY_TYPE_SDL);
     if (o->has_gl && o->gl) {
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
         display_opengl = 1;
 #endif
     }
@@ -834,6 +838,12 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
 
     gui_fullscreen = o->has_full_screen && o->full_screen;
 
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
+    if (display_opengl) {
+        register_displayglops(&dg_gl_ops);
+    }
+#endif
+
     for (i = 0;; i++) {
         QemuConsole *con = qemu_console_lookup_by_index(i);
         if (!con) {
@@ -854,7 +864,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
         }
         sdl2_console[i].idx = i;
         sdl2_console[i].opts = o;
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
         sdl2_console[i].opengl = display_opengl;
         sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
 #else
@@ -863,6 +873,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
 #endif
         sdl2_console[i].dcl.con = con;
         sdl2_console[i].kbd = qkbd_state_init(con);
+        console_set_displayglcontext(con, sdl2_console + i);
         register_displaychangelistener(&sdl2_console[i].dcl);
 
 #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)
@@ -919,6 +930,6 @@ static void register_sdl1(void)
 
 type_init(register_sdl1);
 
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
 module_dep("ui-opengl");
 #endif

+ 11 - 3
ui/shader.c

@@ -150,11 +150,19 @@ static GLuint qemu_gl_create_compile_link_program(const GLchar *vert_src,
 QemuGLShader *qemu_gl_init_shader(void)
 {
     QemuGLShader *gls = g_new0(QemuGLShader, 1);
-
+    const char *header = epoxy_is_desktop_gl() ? "#version 140\n" : "#version 300 es\n";
+    char vert_src[256];
+    char frag_src[256];
+    char *vert_src_body = stpcpy(vert_src, header);
+    char *frag_src_body = stpcpy(frag_src, header);
+
+    strcpy(vert_src_body, texture_blit_vert_src);
+    strcpy(frag_src_body, texture_blit_frag_src);
     gls->texture_blit_prog = qemu_gl_create_compile_link_program
-        (texture_blit_vert_src, texture_blit_frag_src);
+        (vert_src, frag_src);
+    strcpy(vert_src_body, texture_blit_flip_vert_src);
     gls->texture_blit_flip_prog = qemu_gl_create_compile_link_program
-        (texture_blit_flip_vert_src, texture_blit_frag_src);
+        (vert_src, frag_src);
     if (!gls->texture_blit_prog || !gls->texture_blit_flip_prog) {
         exit(1);
     }

+ 0 - 3
ui/shader/texture-blit-flip.vert

@@ -1,6 +1,3 @@
-
-#version 300 es
-
 in vec2  in_position;
 out vec2 ex_tex_coord;
 

+ 0 - 3
ui/shader/texture-blit.frag

@@ -1,6 +1,3 @@
-
-#version 300 es
-
 uniform sampler2D image;
 in  mediump vec2 ex_tex_coord;
 out mediump vec4 out_frag_color;

+ 0 - 3
ui/shader/texture-blit.vert

@@ -1,6 +1,3 @@
-
-#version 300 es
-
 in vec2  in_position;
 out vec2 ex_tex_coord;
 

+ 19 - 17
ui/spice-display.c

@@ -909,17 +909,17 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
     }
 }
 
-static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl,
+static QEMUGLContext qemu_spice_gl_create_context(void *dg,
                                                   QEMUGLParams *params)
 {
     eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
                    qemu_egl_rn_ctx);
-    return qemu_egl_create_context(dcl, params);
+    return qemu_egl_create_context(dg, params);
 }
 
-static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
+static void qemu_spice_gl_scanout_disable(void *dg)
 {
-    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+    SimpleSpiceDisplay *ssd = dg;
 
     trace_qemu_spice_gl_scanout_disable(ssd->qxl.id);
     spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false);
@@ -928,7 +928,7 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
     ssd->have_scanout = false;
 }
 
-static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
+static void qemu_spice_gl_scanout_texture(void *dg,
                                           uint32_t tex_id,
                                           bool y_0_top,
                                           uint32_t backing_width,
@@ -936,7 +936,7 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
                                           uint32_t x, uint32_t y,
                                           uint32_t w, uint32_t h)
 {
-    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+    SimpleSpiceDisplay *ssd = dg;
     EGLint stride = 0, fourcc = 0;
     int fd = -1;
 
@@ -956,10 +956,9 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
     ssd->have_scanout = true;
 }
 
-static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl,
-                                         QemuDmaBuf *dmabuf)
+static void qemu_spice_gl_scanout_dmabuf(void *dg, QemuDmaBuf *dmabuf)
 {
-    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+    SimpleSpiceDisplay *ssd = dg;
 
     ssd->guest_dmabuf = dmabuf;
     ssd->guest_dmabuf_refresh = true;
@@ -968,11 +967,11 @@ static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl,
     ssd->have_scanout = true;
 }
 
-static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl,
+static void qemu_spice_gl_cursor_dmabuf(void *dg,
                                         QemuDmaBuf *dmabuf, bool have_hot,
                                         uint32_t hot_x, uint32_t hot_y)
 {
-    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+    SimpleSpiceDisplay *ssd = dg;
 
     ssd->have_hot = have_hot;
     ssd->hot_x = hot_x;
@@ -991,10 +990,10 @@ static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl,
     }
 }
 
-static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl,
+static void qemu_spice_gl_cursor_position(void *dg,
                                           uint32_t pos_x, uint32_t pos_y)
 {
-    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+    SimpleSpiceDisplay *ssd = dg;
 
     qemu_mutex_lock(&ssd->lock);
     ssd->ptr_x = pos_x;
@@ -1002,10 +1001,9 @@ static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl,
     qemu_mutex_unlock(&ssd->lock);
 }
 
-static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl,
-                                         QemuDmaBuf *dmabuf)
+static void qemu_spice_gl_release_dmabuf(void *dg, QemuDmaBuf *dmabuf)
 {
-    SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+    SimpleSpiceDisplay *ssd = dg;
 
     if (ssd->guest_dmabuf == dmabuf) {
         ssd->guest_dmabuf = NULL;
@@ -1105,7 +1103,10 @@ static const DisplayChangeListenerOps display_listener_gl_ops = {
     .dpy_refresh             = spice_gl_refresh,
     .dpy_mouse_set           = display_mouse_set,
     .dpy_cursor_define       = display_mouse_define,
+    .dpy_gl_update           = qemu_spice_gl_update,
+};
 
+static const DisplayGLOps display_gl_ops = {
     .dpy_gl_ctx_create       = qemu_spice_gl_create_context,
     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
     .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
@@ -1116,7 +1117,6 @@ static const DisplayChangeListenerOps display_listener_gl_ops = {
     .dpy_gl_cursor_dmabuf    = qemu_spice_gl_cursor_dmabuf,
     .dpy_gl_cursor_position  = qemu_spice_gl_cursor_position,
     .dpy_gl_release_dmabuf   = qemu_spice_gl_release_dmabuf,
-    .dpy_gl_update           = qemu_spice_gl_update,
 };
 
 #endif /* HAVE_SPICE_GL */
@@ -1137,6 +1137,8 @@ static void qemu_spice_display_init_one(QemuConsole *con)
         ssd->gls = qemu_gl_init_shader();
         ssd->have_surface = false;
         ssd->have_scanout = false;
+        console_set_displayglcontext(con, ssd);
+        register_displayglops(&display_gl_ops);
     }
 #endif
     ssd->dcl.con = con;