All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/27] Add D-Bus display backend
@ 2021-03-12 10:00 marcandre.lureau
  2021-03-12 10:00 ` [PATCH 01/27] ui: fold qemu_alloc_display in only caller marcandre.lureau
                   ` (28 more replies)
  0 siblings, 29 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Hi,

Both Spice and VNC are relatively complex and inefficient for local-only
display/console export. The goal of this display backend is to export over D-Bus
an interface close to the QEMU internal console API. Any -display or -audio
backend should be possible to implement externally that way. D-Bus is the
protocol of choice for the desktop, it has many convenient bindings for various
languages, or tools (d-feet, busctl etc). Data blob transfer is more efficient
than QMP too.

This implementation makes use of p2p connections for data streaming, and a bus
for basic introspection & interactions. It is currently Unix-only, but it should
be possible to port to other systems relatively easily (minus fd-passing, which
will have to use fallback or different methods).

This will allow to lift some task from the QEMU process (Spice or VNC aren't
trivial!). Backends can come and go. You can have several display opened (say
Boxes & virt-manager), while exporting the display over VNC for example.

An IPC interface is an easy starting point for writing backends in different
languages. I started a Gtk4 & VNC backends in Rust. Rather than rewrite existing
backends, it would be more interesting to develop new ones imho, such as a RDP
backend (based on IronRDP & freerdp). An option is also to build the QEMU
backends as D-Bus backends (maybe not too difficult).

Given the current goals, the D-Bus interface is not meant to be stable.
Clients/backends should be shipped together with QEMU. QEMU could ship its own
Gtk4 widget/library (ideally written in Rust, with an exposed C/GIR API).

The last part of the series modify vhost-user-gpu to notify directly the client
of display changes, bypassing QEMU (VGA-time will be blank, atm)

Basic testing:
$ qemu-system- ... -display dbus

And after git clone https://gitlab.com/marcandre.lureau/qemu-display.git
$ cargo run --bin qemu-gtk4

Better to use with gl=on and virgl. You may also export audio with "-audiodev
dbus" (see related patch).

Marc-André Lureau (27):
  ui: fold qemu_alloc_display in only caller
  vhost-user-gpu: glFlush before notifying clients
  vhost-user-gpu: fix vugbm_device_init fallback
  vhost-user-gpu: fix cursor move/update
  ui: factor out qemu_console_set_display_gl_ctx()
  ui: associate GL context outside of display listener registration
  ui: make gl_block use a counter
  ui: add a gl-unblock warning timer
  ui: simplify gl unblock & flush
  ui: dispatch GL events to all listeners
  ui: split the GL context in a different object
  ui: move qemu_spice_fill_device_address to ui/util.c
  console: save current scanout details
  ui: add a D-Bus display backend
  audio: add dbusaudio backend
  vhost-user-gpu: add vg_send_disable_scanout()
  vhost-user-gpu: add vg_send_scanout_dmabuf()
  vhost-user-gpu: add vg_send_dmabuf_update()
  vhost-user-gpu: add vg_send_scanout()
  vhost-user-gpu: add vg_send_cursor_update()
  vhost-user-gpu: add vg_send_cursor_pos()
  vhost-user-gpu: add vg_send_update()
  vhost-user: add VHOST_USER_GPU_QEMU_DBUS_LISTENER
  ui: add GraphicHwOps.register_dbus_listener()
  vhost-user-gpu: implement register_dbus_listener()
  vhost-user-gpu: check the PIXMAN format is supported
  vhost-user-gpu: implement GPU_QEMU_DBUS_LISTENER

 docs/interop/vhost-user.rst               |  10 +
 meson.build                               |   5 +
 qapi/audio.json                           |   3 +-
 qapi/ui.json                              |  27 +-
 audio/audio_int.h                         |   7 +
 audio/audio_template.h                    |   2 +
 contrib/vhost-user-gpu/vugbm.h            |   2 +-
 contrib/vhost-user-gpu/vugpu.h            |  37 +-
 include/hw/virtio/vhost-backend.h         |   2 +
 include/hw/virtio/virtio-gpu.h            |   1 +
 include/qemu/dbus.h                       |  20 +
 include/ui/console.h                      |  68 ++-
 include/ui/egl-context.h                  |   6 +-
 include/ui/gtk.h                          |  11 +-
 include/ui/sdl2.h                         |   7 +-
 include/ui/spice-display.h                |   5 +-
 subprojects/libvhost-user/libvhost-user.h |   5 +
 ui/dbus.h                                 |  69 +++
 audio/audio.c                             |   1 +
 audio/dbusaudio.c                         | 649 ++++++++++++++++++++++
 contrib/vhost-user-gpu/vhost-user-gpu.c   | 580 +++++++++++++++----
 contrib/vhost-user-gpu/virgl.c            |  45 +-
 contrib/vhost-user-gpu/vugbm.c            |  44 +-
 hw/display/qxl.c                          |   5 +-
 hw/display/vhost-user-gpu.c               |  35 +-
 hw/display/virtio-gpu-base.c              |  19 +-
 hw/display/virtio-vga.c                   |  13 +-
 hw/virtio/vhost-user.c                    |  23 +
 ui/console.c                              | 312 +++++++----
 ui/dbus-console.c                         | 460 +++++++++++++++
 ui/dbus-error.c                           |  45 ++
 ui/dbus-listener.c                        | 480 ++++++++++++++++
 ui/dbus.c                                 | 290 ++++++++++
 ui/egl-context.c                          |   6 +-
 ui/egl-headless.c                         |  20 +-
 ui/gtk-egl.c                              |  11 +-
 ui/gtk-gl-area.c                          |   9 +-
 ui/gtk.c                                  |  25 +-
 ui/sdl2-gl.c                              |  12 +-
 ui/sdl2.c                                 |  14 +-
 ui/spice-core.c                           |  50 --
 ui/spice-display.c                        |  25 +-
 ui/util.c                                 |  75 +++
 util/module.c                             |   3 +
 audio/meson.build                         |   3 +-
 audio/trace-events                        |   5 +
 contrib/vhost-user-gpu/meson.build        |   6 +-
 qemu-options.hx                           |  11 +
 ui/dbus-display1.xml                      | 205 +++++++
 ui/meson.build                            |  18 +
 ui/trace-events                           |  11 +
 51 files changed, 3386 insertions(+), 411 deletions(-)
 create mode 100644 ui/dbus.h
 create mode 100644 audio/dbusaudio.c
 create mode 100644 ui/dbus-console.c
 create mode 100644 ui/dbus-error.c
 create mode 100644 ui/dbus-listener.c
 create mode 100644 ui/dbus.c
 create mode 100644 ui/util.c
 create mode 100644 ui/dbus-display1.xml

-- 
2.29.0




^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH 01/27] ui: fold qemu_alloc_display in only caller
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 02/27] vhost-user-gpu: glFlush before notifying clients marcandre.lureau
                   ` (27 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

A minor code simplification.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/console.c | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/ui/console.c b/ui/console.c
index 171a7bf14b..bab32723b5 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1386,26 +1386,18 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
     return s;
 }
 
-static void qemu_alloc_display(DisplaySurface *surface, int width, int height)
+DisplaySurface *qemu_create_displaysurface(int width, int height)
 {
-    qemu_pixman_image_unref(surface->image);
-    surface->image = NULL;
+    DisplaySurface *surface = g_new0(DisplaySurface, 1);
 
+    trace_displaysurface_create(surface, width, height);
     surface->format = PIXMAN_x8r8g8b8;
     surface->image = pixman_image_create_bits(surface->format,
                                               width, height,
                                               NULL, width * 4);
     assert(surface->image != NULL);
-
     surface->flags = QEMU_ALLOCATED_FLAG;
-}
 
-DisplaySurface *qemu_create_displaysurface(int width, int height)
-{
-    DisplaySurface *surface = g_new0(DisplaySurface, 1);
-
-    trace_displaysurface_create(surface, width, height);
-    qemu_alloc_display(surface, width, height);
     return surface;
 }
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 02/27] vhost-user-gpu: glFlush before notifying clients
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
  2021-03-12 10:00 ` [PATCH 01/27] ui: fold qemu_alloc_display in only caller marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 03/27] vhost-user-gpu: fix vugbm_device_init fallback marcandre.lureau
                   ` (26 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

For similar reasons as commit 3af1671852 ("spice: flush on GL update
before notifying client"), vhost-user-gpu must ensure the GL state is
flushed before sharing its rendering result.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/virgl.c     | 3 +++
 contrib/vhost-user-gpu/meson.build | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c
index 8bb3c563d9..9e6660c7ab 100644
--- a/contrib/vhost-user-gpu/virgl.c
+++ b/contrib/vhost-user-gpu/virgl.c
@@ -16,6 +16,8 @@
 #include <virglrenderer.h>
 #include "virgl.h"
 
+#include <epoxy/gl.h>
+
 void
 vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id,
                             gpointer data)
@@ -372,6 +374,7 @@ virgl_cmd_resource_flush(VuGpu *g,
 
     VUGPU_FILL_CMD(rf);
 
+    glFlush();
     if (!rf.resource_id) {
         g_debug("bad resource id for flush..?");
         return;
diff --git a/contrib/vhost-user-gpu/meson.build b/contrib/vhost-user-gpu/meson.build
index 2fc2320b52..0ce1515a10 100644
--- a/contrib/vhost-user-gpu/meson.build
+++ b/contrib/vhost-user-gpu/meson.build
@@ -2,7 +2,7 @@ if 'CONFIG_TOOLS' in config_host and 'CONFIG_VIRGL' in config_host \
     and 'CONFIG_GBM' in config_host and 'CONFIG_LINUX' in config_host \
     and pixman.found()
   executable('vhost-user-gpu', files('vhost-user-gpu.c', 'virgl.c', 'vugbm.c'),
-             dependencies: [qemuutil, pixman, gbm, virgl, vhost_user],
+             dependencies: [qemuutil, pixman, gbm, virgl, vhost_user, opengl],
              install: true,
              install_dir: get_option('libexecdir'))
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 03/27] vhost-user-gpu: fix vugbm_device_init fallback
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
  2021-03-12 10:00 ` [PATCH 01/27] ui: fold qemu_alloc_display in only caller marcandre.lureau
  2021-03-12 10:00 ` [PATCH 02/27] vhost-user-gpu: glFlush before notifying clients marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 04/27] vhost-user-gpu: fix cursor move/update marcandre.lureau
                   ` (25 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

vugbm implements GBM device wrapping, udmabuf and memory fallback.
However, the fallback/detection logic is flawed, as if "/dev/udmabuf"
failed to be opened, it will not initialize vugbm and crash later.

Rework the vugbm_device_init() logic to initialize correctly in all
cases.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugbm.h          |  2 +-
 contrib/vhost-user-gpu/vhost-user-gpu.c |  6 +---
 contrib/vhost-user-gpu/vugbm.c          | 44 +++++++++++--------------
 3 files changed, 22 insertions(+), 30 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugbm.h b/contrib/vhost-user-gpu/vugbm.h
index 66f1520764..82bc4934e1 100644
--- a/contrib/vhost-user-gpu/vugbm.h
+++ b/contrib/vhost-user-gpu/vugbm.h
@@ -54,7 +54,7 @@ struct vugbm_buffer {
     uint32_t format;
 };
 
-bool vugbm_device_init(struct vugbm_device *dev, int fd);
+void vugbm_device_init(struct vugbm_device *dev, int fd);
 void vugbm_device_destroy(struct vugbm_device *dev);
 
 bool vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev,
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index b27990ffdb..ef40fbccbb 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -1186,11 +1186,7 @@ main(int argc, char *argv[])
         exit(EXIT_FAILURE);
     }
 
-    if (g.drm_rnode_fd >= 0) {
-        if (!vugbm_device_init(&g.gdev, g.drm_rnode_fd)) {
-            g_warning("Failed to init DRM device, using fallback path");
-        }
-    }
+    vugbm_device_init(&g.gdev, g.drm_rnode_fd);
 
     if ((!!opt_socket_path + (opt_fdnum != -1)) != 1) {
         g_printerr("Please specify either --fd or --socket-path\n");
diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c
index f5304ada2f..fb15d0372c 100644
--- a/contrib/vhost-user-gpu/vugbm.c
+++ b/contrib/vhost-user-gpu/vugbm.c
@@ -199,55 +199,51 @@ vugbm_device_destroy(struct vugbm_device *dev)
     dev->device_destroy(dev);
 }
 
-bool
+void
 vugbm_device_init(struct vugbm_device *dev, int fd)
 {
-    dev->fd = fd;
+    assert(!dev->inited);
 
 #ifdef CONFIG_GBM
-    dev->dev = gbm_create_device(fd);
-#endif
-
-    if (0) {
-        /* nothing */
+    if (fd >= 0) {
+        dev->dev = gbm_create_device(fd);
     }
-#ifdef CONFIG_GBM
-    else if (dev->dev != NULL) {
+    if (dev->dev != NULL) {
+        dev->fd = fd;
         dev->alloc_bo = alloc_bo;
         dev->free_bo = free_bo;
         dev->get_fd = get_fd;
         dev->map_bo = map_bo;
         dev->unmap_bo = unmap_bo;
         dev->device_destroy = device_destroy;
+        dev->inited = true;
     }
 #endif
 #ifdef CONFIG_MEMFD
-    else if (g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) {
+    if (!dev->inited && g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) {
         dev->fd = open("/dev/udmabuf", O_RDWR);
-        if (dev->fd < 0) {
-            return false;
+        if (dev->fd >= 0) {
+            g_debug("Using experimental udmabuf backend");
+            dev->alloc_bo = udmabuf_alloc_bo;
+            dev->free_bo = udmabuf_free_bo;
+            dev->get_fd = udmabuf_get_fd;
+            dev->map_bo = udmabuf_map_bo;
+            dev->unmap_bo = udmabuf_unmap_bo;
+            dev->device_destroy = udmabuf_device_destroy;
+            dev->inited = true;
         }
-        g_debug("Using experimental udmabuf backend");
-        dev->alloc_bo = udmabuf_alloc_bo;
-        dev->free_bo = udmabuf_free_bo;
-        dev->get_fd = udmabuf_get_fd;
-        dev->map_bo = udmabuf_map_bo;
-        dev->unmap_bo = udmabuf_unmap_bo;
-        dev->device_destroy = udmabuf_device_destroy;
     }
 #endif
-    else {
+    if (!dev->inited) {
         g_debug("Using mem fallback");
         dev->alloc_bo = mem_alloc_bo;
         dev->free_bo = mem_free_bo;
         dev->map_bo = mem_map_bo;
         dev->unmap_bo = mem_unmap_bo;
         dev->device_destroy = mem_device_destroy;
-        return false;
+        dev->inited = true;
     }
-
-    dev->inited = true;
-    return true;
+    assert(dev->inited);
 }
 
 static bool
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 04/27] vhost-user-gpu: fix cursor move/update
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (2 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 03/27] vhost-user-gpu: fix vugbm_device_init fallback marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 05/27] ui: factor out qemu_console_set_display_gl_ctx() marcandre.lureau
                   ` (24 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

"move" is incorrectly initialized.

Fix it by using a switch statement and also treating unknown commands
with a fallback.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vhost-user-gpu.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index ef40fbccbb..f73f292c9f 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -892,11 +892,8 @@ update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data)
 static void
 vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
 {
-    bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR;
-
-    g_debug("%s move:%d\n", G_STRFUNC, move);
-
-    if (move) {
+    switch (cursor->hdr.type) {
+    case VIRTIO_GPU_CMD_MOVE_CURSOR: {
         VhostUserGpuMsg msg = {
             .request = cursor->resource_id ?
                 VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE,
@@ -907,8 +904,11 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
                 .y = cursor->pos.y,
             }
         };
+        g_debug("%s: move", G_STRFUNC);
         vg_send_msg(g, &msg, -1);
-    } else {
+        break;
+    }
+    case VIRTIO_GPU_CMD_UPDATE_CURSOR: {
         VhostUserGpuMsg msg = {
             .request = VHOST_USER_GPU_CURSOR_UPDATE,
             .size = sizeof(VhostUserGpuCursorUpdate),
@@ -922,6 +922,7 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
                 .hot_y = cursor->hot_y,
             }
         };
+        g_debug("%s: update", G_STRFUNC);
         if (g->virgl) {
             vg_virgl_update_cursor_data(g, cursor->resource_id,
                                         msg.payload.cursor_update.data);
@@ -930,6 +931,11 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
                                       msg.payload.cursor_update.data);
         }
         vg_send_msg(g, &msg, -1);
+        break;
+    }
+    default:
+        g_debug("%s: unknown cmd %d", G_STRFUNC, cursor->hdr.type);
+        break;
     }
 }
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 05/27] ui: factor out qemu_console_set_display_gl_ctx()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (3 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 04/27] vhost-user-gpu: fix cursor move/update marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 15:34   ` Philippe Mathieu-Daudé
  2021-03-12 10:00 ` [PATCH 06/27] ui: associate GL context outside of display listener registration marcandre.lureau
                   ` (23 subsequent siblings)
  28 siblings, 1 reply; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

The next patch will make use of this function to dissociate
DisplayChangeListener from GL context.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h |  2 ++
 ui/console.c         | 21 +++++++++++++--------
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index c960b7066c..9391fb052b 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -413,6 +413,8 @@ void graphic_hw_gl_flushed(QemuConsole *con);
 
 void qemu_console_early_init(void);
 
+void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayChangeListener *dcl);
+
 QemuConsole *qemu_console_lookup_by_index(unsigned int index);
 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
diff --git a/ui/console.c b/ui/console.c
index bab32723b5..83475bd8c3 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1502,6 +1502,18 @@ static bool dpy_compatible_with(QemuConsole *con,
     return true;
 }
 
+void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayChangeListener *dcl)
+{
+    /* 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);
+        exit(1);
+    }
+    dcl->con->gl = dcl;
+}
+
 void register_displaychangelistener(DisplayChangeListener *dcl)
 {
     static const char nodev[] =
@@ -1513,14 +1525,7 @@ 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);
-            exit(1);
-        }
-        dcl->con->gl = dcl;
+        qemu_console_set_display_gl_ctx(dcl->con, dcl);
     }
 
     if (dcl->con && !dpy_compatible_with(dcl->con, dcl, &err)) {
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 06/27] ui: associate GL context outside of display listener registration
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (4 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 05/27] ui: factor out qemu_console_set_display_gl_ctx() marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 07/27] ui: make gl_block use a counter marcandre.lureau
                   ` (22 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/console.c       | 7 +++++--
 ui/egl-headless.c  | 1 +
 ui/gtk.c           | 3 +++
 ui/sdl2.c          | 3 +++
 ui/spice-display.c | 3 +++
 5 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/ui/console.c b/ui/console.c
index 83475bd8c3..53eba2019e 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1524,8 +1524,11 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
 
     assert(!dcl->ds);
 
-    if (dcl->ops->dpy_gl_ctx_create) {
-        qemu_console_set_display_gl_ctx(dcl->con, dcl);
+    if (dcl->con && dcl->con->gl &&
+        dcl->con->gl != dcl) {
+        error_report("Display %s is incompatible with the GL context",
+                     dcl->ops->dpy_name);
+        exit(1);
     }
 
     if (dcl->con && !dpy_compatible_with(dcl->con, dcl, &err)) {
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index da377a74af..773520bc17 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -197,6 +197,7 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
         edpy->dcl.con = con;
         edpy->dcl.ops = &egl_ops;
         edpy->gls = qemu_gl_init_shader();
+        qemu_console_set_display_gl_ctx(con, &edpy->dcl);
         register_displaychangelistener(&edpy->dcl);
     }
 }
diff --git a/ui/gtk.c b/ui/gtk.c
index 3edaf041de..d8b3784bf0 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -2082,6 +2082,9 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
     vc->gfx.kbd = qkbd_state_init(con);
     vc->gfx.dcl.con = con;
 
+    if (display_opengl) {
+        qemu_console_set_display_gl_ctx(con, &vc->gfx.dcl);
+    }
     register_displaychangelistener(&vc->gfx.dcl);
 
     gd_connect_vc_gfx_signals(vc);
diff --git a/ui/sdl2.c b/ui/sdl2.c
index a203cb0239..0593a333f5 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -863,6 +863,9 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
 #endif
         sdl2_console[i].dcl.con = con;
         sdl2_console[i].kbd = qkbd_state_init(con);
+        if (display_opengl) {
+            qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dcl);
+        }
         register_displaychangelistener(&sdl2_console[i].dcl);
 
 #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)
diff --git a/ui/spice-display.c b/ui/spice-display.c
index d22781a23d..f637fd1bc6 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -1152,6 +1152,9 @@ static void qemu_spice_display_init_one(QemuConsole *con)
 
     qemu_spice_create_host_memslot(ssd);
 
+    if (spice_opengl) {
+        qemu_console_set_display_gl_ctx(con, &ssd->dcl);
+    }
     register_displaychangelistener(&ssd->dcl);
 }
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 07/27] ui: make gl_block use a counter
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (5 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 06/27] ui: associate GL context outside of display listener registration marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:12   ` Philippe Mathieu-Daudé
  2021-03-12 10:00 ` [PATCH 08/27] ui: add a gl-unblock warning timer marcandre.lureau
                   ` (21 subsequent siblings)
  28 siblings, 1 reply; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Track multiple callers blocking requests.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/console.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/ui/console.c b/ui/console.c
index 53eba2019e..fedb9d8b13 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -128,7 +128,7 @@ struct QemuConsole {
     DisplaySurface *surface;
     int dcls;
     DisplayChangeListener *gl;
-    bool gl_block;
+    int gl_block;
     int window_id;
 
     /* Graphic console state.  */
@@ -288,10 +288,19 @@ void graphic_hw_gl_block(QemuConsole *con, bool block)
 {
     assert(con != NULL);
 
-    con->gl_block = block;
-    if (con->hw_ops->gl_block) {
-        con->hw_ops->gl_block(con->hw, block);
+    if (block) {
+        con->gl_block++;
+    } else {
+        con->gl_block--;
+    }
+    assert(con->gl_block >= 0);
+    if (!con->hw_ops->gl_block) {
+        return;
+    }
+    if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) {
+        return;
     }
+    con->hw_ops->gl_block(con->hw, block);
 }
 
 void graphic_hw_gl_flushed(QemuConsole *con)
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 08/27] ui: add a gl-unblock warning timer
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (6 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 07/27] ui: make gl_block use a counter marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 09/27] ui: simplify gl unblock & flush marcandre.lureau
                   ` (20 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Similar to the one that exists for Spice, so we can investigate if
something is locked.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/console.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/ui/console.c b/ui/console.c
index fedb9d8b13..808f82fa64 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -129,6 +129,7 @@ struct QemuConsole {
     int dcls;
     DisplayChangeListener *gl;
     int gl_block;
+    QEMUTimer *gl_unblock_timer;
     int window_id;
 
     /* Graphic console state.  */
@@ -284,8 +285,14 @@ void graphic_hw_update(QemuConsole *con)
     }
 }
 
+static void graphic_hw_gl_unblock_timer(void *opaque)
+{
+    warn_report("console: no gl-unblock within one second");
+}
+
 void graphic_hw_gl_block(QemuConsole *con, bool block)
 {
+    uint64_t timeout;
     assert(con != NULL);
 
     if (block) {
@@ -301,6 +308,14 @@ void graphic_hw_gl_block(QemuConsole *con, bool block)
         return;
     }
     con->hw_ops->gl_block(con->hw, block);
+
+    if (block) {
+        timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+        timeout += 1000; /* one sec */
+        timer_mod(con->gl_unblock_timer, timeout);
+    } else {
+        timer_del(con->gl_unblock_timer);
+    }
 }
 
 void graphic_hw_gl_flushed(QemuConsole *con)
@@ -2025,6 +2040,8 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
 
     surface = qemu_create_placeholder_surface(width, height, noinit);
     dpy_gfx_replace_surface(s, surface);
+    s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+                                       graphic_hw_gl_unblock_timer, s);
     return s;
 }
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 09/27] ui: simplify gl unblock & flush
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (7 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 08/27] ui: add a gl-unblock warning timer marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 10/27] ui: dispatch GL events to all listeners marcandre.lureau
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

GraphicHw.gl_flushed was introduced to notify the
device (vhost-user-gpu) that the GL resources (the display scanout) are
no longer needed.

It was decoupled from QEMU own gl-blocking mechanism, but that
difference isn't really helping. Instead, we can reuse QEMU gl-blocking
and notify virtio_gpu_gl_flueshd() when unblocking (to unlock
vhost-user-gpu).

An extra block/unblock is added arount dpy_gl_update() so existing
backends that don't block will have the flush event handled. It will
also help when there are no backends associated.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h         |  2 --
 hw/display/vhost-user-gpu.c  |  2 +-
 hw/display/virtio-gpu-base.c |  5 ++++-
 hw/display/virtio-vga.c      | 11 -----------
 ui/console.c                 | 12 +++---------
 ui/gtk-egl.c                 |  1 -
 ui/gtk-gl-area.c             |  1 -
 ui/sdl2-gl.c                 |  2 --
 ui/spice-display.c           |  1 -
 9 files changed, 8 insertions(+), 29 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 9391fb052b..a8c2b9f479 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -393,7 +393,6 @@ typedef struct GraphicHwOps {
     void (*update_interval)(void *opaque, uint64_t interval);
     int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info);
     void (*gl_block)(void *opaque, bool block);
-    void (*gl_flushed)(void *opaque);
 } GraphicHwOps;
 
 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
@@ -409,7 +408,6 @@ void graphic_hw_update_done(QemuConsole *con);
 void graphic_hw_invalidate(QemuConsole *con);
 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
 void graphic_hw_gl_block(QemuConsole *con, bool block);
-void graphic_hw_gl_flushed(QemuConsole *con);
 
 void qemu_console_early_init(void);
 
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
index a01f9315e1..a2a011e9cc 100644
--- a/hw/display/vhost-user-gpu.c
+++ b/hw/display/vhost-user-gpu.c
@@ -254,8 +254,8 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
             vhost_user_gpu_unblock(g);
             break;
         }
-        dpy_gl_update(con, m->x, m->y, m->width, m->height);
         g->backend_blocked = true;
+        dpy_gl_update(con, m->x, m->y, m->width, m->height);
         break;
     }
     case VHOST_USER_GPU_UPDATE: {
diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index 25f8920fdb..ee2753001a 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -118,6 +118,10 @@ virtio_gpu_gl_block(void *opaque, bool block)
         g->renderer_blocked--;
     }
     assert(g->renderer_blocked >= 0);
+
+    if (!block && g->renderer_blocked == 0) {
+        virtio_gpu_gl_flushed(g);
+    }
 }
 
 static int
@@ -144,7 +148,6 @@ static const GraphicHwOps virtio_gpu_ops = {
     .text_update = virtio_gpu_text_update,
     .ui_info = virtio_gpu_ui_info,
     .gl_block = virtio_gpu_gl_block,
-    .gl_flushed = virtio_gpu_gl_flushed,
 };
 
 bool
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index d3c6404061..b071909b68 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -68,16 +68,6 @@ static void virtio_vga_base_gl_block(void *opaque, bool block)
     }
 }
 
-static void virtio_vga_base_gl_flushed(void *opaque)
-{
-    VirtIOVGABase *vvga = opaque;
-    VirtIOGPUBase *g = vvga->vgpu;
-
-    if (g->hw_ops->gl_flushed) {
-        g->hw_ops->gl_flushed(g);
-    }
-}
-
 static int virtio_vga_base_get_flags(void *opaque)
 {
     VirtIOVGABase *vvga = opaque;
@@ -93,7 +83,6 @@ static const GraphicHwOps virtio_vga_base_ops = {
     .text_update = virtio_vga_base_text_update,
     .ui_info = virtio_vga_base_ui_info,
     .gl_block = virtio_vga_base_gl_block,
-    .gl_flushed = virtio_vga_base_gl_flushed,
 };
 
 static const VMStateDescription vmstate_virtio_vga_base = {
diff --git a/ui/console.c b/ui/console.c
index 808f82fa64..18311707bd 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -318,15 +318,6 @@ void graphic_hw_gl_block(QemuConsole *con, bool block)
     }
 }
 
-void graphic_hw_gl_flushed(QemuConsole *con)
-{
-    assert(con != NULL);
-
-    if (con->hw_ops->gl_flushed) {
-        con->hw_ops->gl_flushed(con->hw);
-    }
-}
-
 int qemu_console_get_window_id(QemuConsole *con)
 {
     return con->window_id;
@@ -1953,7 +1944,10 @@ void dpy_gl_update(QemuConsole *con,
                    uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
     assert(con->gl);
+
+    graphic_hw_gl_block(con, true);
     con->gl->ops->dpy_gl_update(con->gl, x, y, w, h);
+    graphic_hw_gl_block(con, false);
 }
 
 /***********************************************************/
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2a2e6d3a17..48603abfbd 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -94,7 +94,6 @@ void gd_egl_draw(VirtualConsole *vc)
     }
 
     glFlush();
-    graphic_hw_gl_flushed(vc->gfx.dcl.con);
 }
 
 void gd_egl_update(DisplayChangeListener *dcl,
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index dd5783fec7..e895938a96 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -72,7 +72,6 @@ void gd_gl_area_draw(VirtualConsole *vc)
     }
 
     glFlush();
-    graphic_hw_gl_flushed(vc->gfx.dcl.con);
 }
 
 void gd_gl_area_update(DisplayChangeListener *dcl,
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index a21d2deed9..5b950fbbea 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -58,7 +58,6 @@ static void sdl2_gl_render_surface(struct sdl2_console *scon)
 
     surface_gl_render_texture(scon->gls, scon->surface);
     SDL_GL_SwapWindow(scon->real_window);
-    graphic_hw_gl_flushed(scon->dcl.con);
 }
 
 void sdl2_gl_update(DisplayChangeListener *dcl,
@@ -241,5 +240,4 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
     egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top);
 
     SDL_GL_SwapWindow(scon->real_window);
-    graphic_hw_gl_flushed(dcl->con);
 }
diff --git a/ui/spice-display.c b/ui/spice-display.c
index f637fd1bc6..24f5cc30f1 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -826,7 +826,6 @@ static void qemu_spice_gl_unblock_bh(void *opaque)
     SimpleSpiceDisplay *ssd = opaque;
 
     qemu_spice_gl_block(ssd, false);
-    graphic_hw_gl_flushed(ssd->dcl.con);
 }
 
 static void qemu_spice_gl_block_timer(void *opaque)
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 10/27] ui: dispatch GL events to all listeners
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (8 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 09/27] ui: simplify gl unblock & flush marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 11/27] ui: split the GL context in a different object marcandre.lureau
                   ` (18 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

For now, only one listener can receive GL events. Let's dispatch to all
listeners. (preliminary check ensure there is a single listener now
during regitration, and in next patches, compatible listeners only)

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/console.c | 58 +++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 42 insertions(+), 16 deletions(-)

diff --git a/ui/console.c b/ui/console.c
index 18311707bd..a0910671f7 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1883,8 +1883,12 @@ int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
 
 void dpy_gl_scanout_disable(QemuConsole *con)
 {
-    assert(con->gl);
-    con->gl->ops->dpy_gl_scanout_disable(con->gl);
+    DisplayState *s = con->ds;
+    DisplayChangeListener *dcl;
+
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        dcl->ops->dpy_gl_scanout_disable(dcl);
+    }
 }
 
 void dpy_gl_scanout_texture(QemuConsole *con,
@@ -1895,58 +1899,80 @@ 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,
+    DisplayState *s = con->ds;
+    DisplayChangeListener *dcl;
+
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        dcl->ops->dpy_gl_scanout_texture(dcl, 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);
+    DisplayState *s = con->ds;
+    DisplayChangeListener *dcl;
+
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
+    }
 }
 
 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
                           bool have_hot, uint32_t hot_x, uint32_t hot_y)
 {
-    assert(con->gl);
+    DisplayState *s = con->ds;
+    DisplayChangeListener *dcl;
 
-    if (con->gl->ops->dpy_gl_cursor_dmabuf) {
-        con->gl->ops->dpy_gl_cursor_dmabuf(con->gl, dmabuf,
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->ops->dpy_gl_cursor_dmabuf) {
+            dcl->ops->dpy_gl_cursor_dmabuf(dcl, 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);
+    DisplayState *s = con->ds;
+    DisplayChangeListener *dcl;
 
-    if (con->gl->ops->dpy_gl_cursor_position) {
-        con->gl->ops->dpy_gl_cursor_position(con->gl, pos_x, pos_y);
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->ops->dpy_gl_cursor_position) {
+            dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y);
+        }
     }
 }
 
 void dpy_gl_release_dmabuf(QemuConsole *con,
                           QemuDmaBuf *dmabuf)
 {
-    assert(con->gl);
+    DisplayState *s = con->ds;
+    DisplayChangeListener *dcl;
 
-    if (con->gl->ops->dpy_gl_release_dmabuf) {
-        con->gl->ops->dpy_gl_release_dmabuf(con->gl, dmabuf);
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->ops->dpy_gl_release_dmabuf) {
+            dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf);
+        }
     }
 }
 
 void dpy_gl_update(QemuConsole *con,
                    uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
+    DisplayState *s = con->ds;
+    DisplayChangeListener *dcl;
+
     assert(con->gl);
 
     graphic_hw_gl_block(con, true);
-    con->gl->ops->dpy_gl_update(con->gl, x, y, w, h);
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        dcl->ops->dpy_gl_update(dcl, x, y, w, h);
+    }
     graphic_hw_gl_block(con, false);
 }
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 11/27] ui: split the GL context in a different object
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (9 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 10/27] ui: dispatch GL events to all listeners marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 12/27] ui: move qemu_spice_fill_device_address to ui/util.c marcandre.lureau
                   ` (17 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This will allow to have one GL context but a variable number of
listeners.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h       | 31 ++++++++++++++++++++-----------
 include/ui/egl-context.h   |  6 +++---
 include/ui/gtk.h           | 11 ++++++-----
 include/ui/sdl2.h          |  7 ++++---
 include/ui/spice-display.h |  1 +
 ui/console.c               | 24 +++++++++++++++---------
 ui/egl-context.c           |  6 +++---
 ui/egl-headless.c          | 21 ++++++++++++++-------
 ui/gtk-egl.c               | 10 +++++-----
 ui/gtk-gl-area.c           |  8 ++++----
 ui/gtk.c                   | 24 ++++++++++++++++--------
 ui/sdl2-gl.c               | 10 +++++-----
 ui/sdl2.c                  | 13 +++++++++----
 ui/spice-display.c         | 18 +++++++++++-------
 14 files changed, 116 insertions(+), 74 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index a8c2b9f479..b271bb1c51 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -171,6 +171,7 @@ typedef struct QemuDmaBuf {
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
+typedef struct DisplayGLCtx DisplayGLCtx;
 
 typedef struct DisplayChangeListenerOps {
     const char *dpy_name;
@@ -205,16 +206,6 @@ typedef struct DisplayChangeListenerOps {
     void (*dpy_cursor_define)(DisplayChangeListener *dcl,
                               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);
-
     /* required if GL */
     void (*dpy_gl_scanout_disable)(DisplayChangeListener *dcl);
     /* required if GL */
@@ -255,6 +246,24 @@ struct DisplayChangeListener {
     QLIST_ENTRY(DisplayChangeListener) next;
 };
 
+typedef struct DisplayGLCtxOps {
+    /* We only check if the GLCtx is compatible with a DCL via ops. A natural
+     * evolution of this would be a callback to check some runtime requirements
+     * and allow various DCL kinds. */
+    const DisplayChangeListenerOps *compatible_dcl;
+
+    QEMUGLContext (*dpy_gl_ctx_create)(DisplayGLCtx *dgc,
+                                       QEMUGLParams *params);
+    void (*dpy_gl_ctx_destroy)(DisplayGLCtx *dgc,
+                               QEMUGLContext ctx);
+    int (*dpy_gl_ctx_make_current)(DisplayGLCtx *dgc,
+                                   QEMUGLContext ctx);
+} DisplayGLCtxOps;
+
+struct DisplayGLCtx {
+    const DisplayGLCtxOps *ops;
+};
+
 DisplayState *init_displaystate(void);
 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
                                                 pixman_format_code_t format,
@@ -411,7 +420,7 @@ void graphic_hw_gl_block(QemuConsole *con, bool block);
 
 void qemu_console_early_init(void);
 
-void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayChangeListener *dcl);
+void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *ctx);
 
 QemuConsole *qemu_console_lookup_by_index(unsigned int index);
 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
diff --git a/include/ui/egl-context.h b/include/ui/egl-context.h
index 9374fe41e3..c2761d747a 100644
--- a/include/ui/egl-context.h
+++ b/include/ui/egl-context.h
@@ -4,10 +4,10 @@
 #include "ui/console.h"
 #include "ui/egl-helpers.h"
 
-QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
+QEMUGLContext qemu_egl_create_context(DisplayGLCtx *dgc,
                                       QEMUGLParams *params);
-void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx);
-int qemu_egl_make_context_current(DisplayChangeListener *dcl,
+void qemu_egl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx);
+int qemu_egl_make_context_current(DisplayGLCtx *dgc,
                                   QEMUGLContext ctx);
 
 #endif /* EGL_CONTEXT_H */
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 5ae0ad60a6..802c619a39 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -28,6 +28,7 @@ typedef struct GtkDisplayState GtkDisplayState;
 
 typedef struct VirtualGfxConsole {
     GtkWidget *drawing_area;
+    DisplayGLCtx dgc;
     DisplayChangeListener dcl;
     QKbdState *kbd;
     DisplaySurface *ds;
@@ -97,7 +98,7 @@ 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,
+QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
                                     QEMUGLParams *params);
 void gd_egl_scanout_disable(DisplayChangeListener *dcl);
 void gd_egl_scanout_texture(DisplayChangeListener *dcl,
@@ -119,7 +120,7 @@ void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
 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,
+int gd_egl_make_current(DisplayGLCtx *dgc,
                         QEMUGLContext ctx);
 
 /* ui/gtk-gl-area.c */
@@ -130,9 +131,9 @@ 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,
+QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc,
                                         QEMUGLParams *params);
-void gd_gl_area_destroy_context(DisplayChangeListener *dcl,
+void gd_gl_area_destroy_context(DisplayGLCtx *dgc,
                                 QEMUGLContext ctx);
 void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
                                QemuDmaBuf *dmabuf);
@@ -147,7 +148,7 @@ void gd_gl_area_scanout_disable(DisplayChangeListener *dcl);
 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,
+int gd_gl_area_make_current(DisplayGLCtx *dgc,
                             QEMUGLContext ctx);
 
 #endif /* UI_GTK_H */
diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h
index f85c117a78..71bcf7ebda 100644
--- a/include/ui/sdl2.h
+++ b/include/ui/sdl2.h
@@ -16,6 +16,7 @@
 #endif
 
 struct sdl2_console {
+    DisplayGLCtx dgc;
     DisplayChangeListener dcl;
     DisplaySurface *surface;
     DisplayOptions *opts;
@@ -65,10 +66,10 @@ 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,
+QEMUGLContext sdl2_gl_create_context(DisplayGLCtx *dgc,
                                      QEMUGLParams *params);
-void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx);
-int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
+void sdl2_gl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx);
+int sdl2_gl_make_context_current(DisplayGLCtx *dgc,
                                  QEMUGLContext ctx);
 
 void sdl2_gl_scanout_disable(DisplayChangeListener *dcl);
diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index ed298d58f0..a2fbf62c52 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -86,6 +86,7 @@ typedef struct SimpleSpiceCursor SimpleSpiceCursor;
 
 struct SimpleSpiceDisplay {
     DisplaySurface *ds;
+    DisplayGLCtx dgc;
     DisplayChangeListener dcl;
     void *buf;
     int bufsize;
diff --git a/ui/console.c b/ui/console.c
index a0910671f7..07357fd328 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -127,7 +127,7 @@ struct QemuConsole {
     DisplayState *ds;
     DisplaySurface *surface;
     int dcls;
-    DisplayChangeListener *gl;
+    DisplayGLCtx *gl;
     int gl_block;
     QEMUTimer *gl_unblock_timer;
     int window_id;
@@ -1517,16 +1517,23 @@ static bool dpy_compatible_with(QemuConsole *con,
     return true;
 }
 
-void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayChangeListener *dcl)
+void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
 {
     /* 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);
+    assert(con);
+    if (con->gl) {
+        error_report("The console already has an OpenGL context.");
         exit(1);
     }
-    dcl->con->gl = dcl;
+    con->gl = gl;
+}
+
+static bool dpy_gl_compatible_with(QemuConsole *con, DisplayChangeListener *dcl)
+{
+    if (!con->gl)
+        return true;
+
+    return con->gl->ops->compatible_dcl == dcl->ops;
 }
 
 void register_displaychangelistener(DisplayChangeListener *dcl)
@@ -1539,8 +1546,7 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
 
     assert(!dcl->ds);
 
-    if (dcl->con && dcl->con->gl &&
-        dcl->con->gl != dcl) {
+    if (dcl->con && !dpy_gl_compatible_with(dcl->con, dcl)) {
         error_report("Display %s is incompatible with the GL context",
                      dcl->ops->dpy_name);
         exit(1);
diff --git a/ui/egl-context.c b/ui/egl-context.c
index 368ffa49d8..eb5f520fc4 100644
--- a/ui/egl-context.c
+++ b/ui/egl-context.c
@@ -1,7 +1,7 @@
 #include "qemu/osdep.h"
 #include "ui/egl-context.h"
 
-QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
+QEMUGLContext qemu_egl_create_context(DisplayGLCtx *dgc,
                                       QEMUGLParams *params)
 {
    EGLContext ctx;
@@ -24,12 +24,12 @@ QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
    return ctx;
 }
 
-void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+void qemu_egl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
 {
     eglDestroyContext(qemu_egl_display, ctx);
 }
 
-int qemu_egl_make_context_current(DisplayChangeListener *dcl,
+int qemu_egl_make_context_current(DisplayGLCtx *dgc,
                                   QEMUGLContext ctx)
 {
    return eglMakeCurrent(qemu_egl_display,
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index 773520bc17..f492591d4e 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -38,12 +38,12 @@ static void egl_gfx_switch(DisplayChangeListener *dcl,
     edpy->ds = new_surface;
 }
 
-static QEMUGLContext egl_create_context(DisplayChangeListener *dcl,
+static QEMUGLContext egl_create_context(DisplayGLCtx *dgc,
                                         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(dgc, params);
 }
 
 static void egl_scanout_disable(DisplayChangeListener *dcl)
@@ -157,10 +157,6 @@ static const DisplayChangeListenerOps egl_ops = {
     .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,
@@ -170,6 +166,13 @@ static const DisplayChangeListenerOps egl_ops = {
     .dpy_gl_update           = egl_scanout_flush,
 };
 
+static const DisplayGLCtxOps eglctx_ops = {
+    .compatible_dcl          = &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,
+};
+
 static void early_egl_headless_init(DisplayOptions *opts)
 {
     display_opengl = 1;
@@ -188,6 +191,8 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
     }
 
     for (idx = 0;; idx++) {
+        DisplayGLCtx *ctx;
+
         con = qemu_console_lookup_by_index(idx);
         if (!con || !qemu_console_is_graphic(con)) {
             break;
@@ -197,7 +202,9 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
         edpy->dcl.con = con;
         edpy->dcl.ops = &egl_ops;
         edpy->gls = qemu_gl_init_shader();
-        qemu_console_set_display_gl_ctx(con, &edpy->dcl);
+        ctx = g_new0(DisplayGLCtx, 1);
+        ctx->ops = &eglctx_ops;
+        qemu_console_set_display_gl_ctx(con, ctx);
         register_displaychangelistener(&edpy->dcl);
     }
 }
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 48603abfbd..199717495d 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -163,14 +163,14 @@ void gd_egl_switch(DisplayChangeListener *dcl,
     }
 }
 
-QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl,
+QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
                                     QEMUGLParams *params)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
 
     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(dgc, params);
 }
 
 void gd_egl_scanout_disable(DisplayChangeListener *dcl)
@@ -303,10 +303,10 @@ void gtk_egl_init(DisplayGLMode mode)
     display_opengl = 1;
 }
 
-int gd_egl_make_current(DisplayChangeListener *dcl,
+int gd_egl_make_current(DisplayGLCtx *dgc,
                         QEMUGLContext ctx)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
 
     return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
                           vc->gfx.esurface, ctx);
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index e895938a96..f0d00d000d 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -138,10 +138,10 @@ void gd_gl_area_switch(DisplayChangeListener *dcl,
     }
 }
 
-QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
+QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc,
                                         QEMUGLParams *params)
 {
-    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
     GdkWindow *window;
     GdkGLContext *ctx;
     GError *err = NULL;
@@ -167,7 +167,7 @@ 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(DisplayGLCtx *dgc, QEMUGLContext ctx)
 {
     /* FIXME */
 }
@@ -238,7 +238,7 @@ void gtk_gl_area_init(void)
     display_opengl = 1;
 }
 
-int gd_gl_area_make_current(DisplayChangeListener *dcl,
+int gd_gl_area_make_current(DisplayGLCtx *dgc,
                             QEMUGLContext ctx)
 {
     gdk_gl_context_make_current(ctx);
diff --git a/ui/gtk.c b/ui/gtk.c
index d8b3784bf0..63ce4fd980 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -643,9 +643,6 @@ 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,
@@ -653,8 +650,14 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
     .dpy_has_dmabuf          = gd_has_dmabuf,
 };
 
-#ifdef CONFIG_X11
+static const DisplayGLCtxOps gl_area_ctx_ops = {
+    .compatible_dcl          = &dcl_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,
+};
 
+#ifdef CONFIG_X11
 static const DisplayChangeListenerOps dcl_egl_ops = {
     .dpy_name             = "gtk-egl",
     .dpy_gfx_update       = gd_egl_update,
@@ -664,9 +667,6 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
     .dpy_mouse_set        = gd_mouse_set,
     .dpy_cursor_define    = gd_cursor_define,
 
-    .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,
     .dpy_gl_scanout_disable  = gd_egl_scanout_disable,
     .dpy_gl_scanout_texture  = gd_egl_scanout_texture,
     .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
@@ -677,6 +677,12 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
     .dpy_has_dmabuf          = gd_has_dmabuf,
 };
 
+static const DisplayGLCtxOps egl_ctx_ops = {
+    .compatible_dcl          = &dcl_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,
+};
 #endif
 
 #endif /* CONFIG_OPENGL */
@@ -2034,6 +2040,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
             g_signal_connect(vc->gfx.drawing_area, "realize",
                              G_CALLBACK(gl_area_realize), vc);
             vc->gfx.dcl.ops = &dcl_gl_area_ops;
+            vc->gfx.dgc.ops = &gl_area_ctx_ops;
         } else {
 #ifdef CONFIG_X11
             vc->gfx.drawing_area = gtk_drawing_area_new();
@@ -2048,6 +2055,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
             gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
 #pragma GCC diagnostic pop
             vc->gfx.dcl.ops = &dcl_egl_ops;
+            vc->gfx.dgc.ops = &egl_ctx_ops;
             vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
 #else
             abort();
@@ -2083,7 +2091,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
     vc->gfx.dcl.con = con;
 
     if (display_opengl) {
-        qemu_console_set_display_gl_ctx(con, &vc->gfx.dcl);
+        qemu_console_set_display_gl_ctx(con, &vc->gfx.dgc);
     }
     register_displaychangelistener(&vc->gfx.dcl);
 
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index 5b950fbbea..39cab8cde7 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -132,10 +132,10 @@ void sdl2_gl_redraw(struct sdl2_console *scon)
     }
 }
 
-QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl,
+QEMUGLContext sdl2_gl_create_context(DisplayGLCtx *dgc,
                                      QEMUGLParams *params)
 {
-    struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+    struct sdl2_console *scon = container_of(dgc, struct sdl2_console, dgc);
     SDL_GLContext ctx;
 
     assert(scon->opengl);
@@ -167,17 +167,17 @@ QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl,
     return (QEMUGLContext)ctx;
 }
 
-void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+void sdl2_gl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
 {
     SDL_GLContext sdlctx = (SDL_GLContext)ctx;
 
     SDL_GL_DeleteContext(sdlctx);
 }
 
-int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
+int sdl2_gl_make_context_current(DisplayGLCtx *dgc,
                                  QEMUGLContext ctx)
 {
-    struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+    struct sdl2_console *scon = container_of(dgc, struct sdl2_console, dgc);
     SDL_GLContext sdlctx = (SDL_GLContext)ctx;
 
     assert(scon->opengl);
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 0593a333f5..97ba283c49 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -778,13 +778,17 @@ 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,
 };
+
+static const DisplayGLCtxOps gl_ctx_ops = {
+    .compatible_dcl          = &dcl_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,
+};
 #endif
 
 static void sdl2_display_early_init(DisplayOptions *o)
@@ -857,6 +861,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
 #ifdef CONFIG_OPENGL
         sdl2_console[i].opengl = display_opengl;
         sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
+        sdl2_console[i].dgc.ops = display_opengl ? &gl_ctx_ops : NULL;
 #else
         sdl2_console[i].opengl = 0;
         sdl2_console[i].dcl.ops = &dcl_2d_ops;
@@ -864,7 +869,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
         sdl2_console[i].dcl.con = con;
         sdl2_console[i].kbd = qkbd_state_init(con);
         if (display_opengl) {
-            qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dcl);
+            qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dgc);
         }
         register_displaychangelistener(&sdl2_console[i].dcl);
 
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 24f5cc30f1..0b787d12df 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -904,12 +904,12 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
     }
 }
 
-static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl,
+static QEMUGLContext qemu_spice_gl_create_context(DisplayGLCtx *dgc,
                                                   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(dgc, params);
 }
 
 static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
@@ -1101,10 +1101,6 @@ static const DisplayChangeListenerOps display_listener_gl_ops = {
     .dpy_mouse_set           = display_mouse_set,
     .dpy_cursor_define       = display_mouse_define,
 
-    .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,
-
     .dpy_gl_scanout_disable  = qemu_spice_gl_scanout_disable,
     .dpy_gl_scanout_texture  = qemu_spice_gl_scanout_texture,
     .dpy_gl_scanout_dmabuf   = qemu_spice_gl_scanout_dmabuf,
@@ -1114,6 +1110,13 @@ static const DisplayChangeListenerOps display_listener_gl_ops = {
     .dpy_gl_update           = qemu_spice_gl_update,
 };
 
+static const DisplayGLCtxOps gl_ctx_ops = {
+    .compatible_dcl          = &display_listener_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,
+};
+
 #endif /* HAVE_SPICE_GL */
 
 static void qemu_spice_display_init_one(QemuConsole *con)
@@ -1126,6 +1129,7 @@ static void qemu_spice_display_init_one(QemuConsole *con)
 #ifdef HAVE_SPICE_GL
     if (spice_opengl) {
         ssd->dcl.ops = &display_listener_gl_ops;
+        ssd->dgc.ops = &gl_ctx_ops;
         ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd);
         ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
                                              qemu_spice_gl_block_timer, ssd);
@@ -1152,7 +1156,7 @@ static void qemu_spice_display_init_one(QemuConsole *con)
     qemu_spice_create_host_memslot(ssd);
 
     if (spice_opengl) {
-        qemu_console_set_display_gl_ctx(con, &ssd->dcl);
+        qemu_console_set_display_gl_ctx(con, &ssd->dgc);
     }
     register_displaychangelistener(&ssd->dcl);
 }
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 12/27] ui: move qemu_spice_fill_device_address to ui/util.c
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (10 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 11/27] ui: split the GL context in a different object marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 13/27] console: save current scanout details marcandre.lureau
                   ` (16 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Other backends can use it.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h       |  6 +++
 include/ui/spice-display.h |  4 --
 hw/display/qxl.c           |  5 ++-
 ui/spice-core.c            | 50 -------------------------
 ui/spice-display.c         |  5 ++-
 ui/util.c                  | 75 ++++++++++++++++++++++++++++++++++++++
 ui/meson.build             |  1 +
 7 files changed, 90 insertions(+), 56 deletions(-)
 create mode 100644 ui/util.c

diff --git a/include/ui/console.h b/include/ui/console.h
index b271bb1c51..793f4c09ee 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -489,4 +489,10 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp);
 /* input.c */
 int index_from_key(const char *key, size_t key_length);
 
+/* util.c */
+bool qemu_console_fill_device_address(QemuConsole *con,
+                                      char *device_address,
+                                      size_t size,
+                                      Error **errp);
+
 #endif
diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index a2fbf62c52..e271e011da 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -184,8 +184,4 @@ void qemu_spice_display_start(void);
 void qemu_spice_display_stop(void);
 int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd);
 
-bool qemu_spice_fill_device_address(QemuConsole *con,
-                                    char *device_address,
-                                    size_t size);
-
 #endif
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 6784d32920..ac56aeeff4 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -2202,12 +2202,15 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
     }
 
 #if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */
+    Error *err = NULL;
     char device_address[256] = "";
-    if (qemu_spice_fill_device_address(qxl->vga.con, device_address, 256)) {
+    if (qemu_console_fill_device_address(qxl->vga.con, device_address, 256, &err)) {
         spice_qxl_set_device_info(&qxl->ssd.qxl,
                                   device_address,
                                   0,
                                   qxl->max_outputs);
+    } else {
+        error_report_err(err);
     }
 #endif
 
diff --git a/ui/spice-core.c b/ui/spice-core.c
index beee932f55..2c21d923c8 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -861,56 +861,6 @@ bool qemu_spice_have_display_interface(QemuConsole *con)
     return false;
 }
 
-/*
- * Recursively (in reverse order) appends addresses of PCI devices as it moves
- * up in the PCI hierarchy.
- *
- * @returns true on success, false when the buffer wasn't large enough
- */
-static bool append_pci_address(char *buf, size_t buf_size, const PCIDevice *pci)
-{
-    PCIBus *bus = pci_get_bus(pci);
-    /*
-     * equivalent to if (!pci_bus_is_root(bus)), but the function is not built
-     * with PCI_CONFIG=n, avoid using an #ifdef by checking directly
-     */
-    if (bus->parent_dev != NULL) {
-        append_pci_address(buf, buf_size, bus->parent_dev);
-    }
-
-    size_t len = strlen(buf);
-    ssize_t written = snprintf(buf + len, buf_size - len, "/%02x.%x",
-        PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn));
-
-    return written > 0 && written < buf_size - len;
-}
-
-bool qemu_spice_fill_device_address(QemuConsole *con,
-                                    char *device_address,
-                                    size_t size)
-{
-    DeviceState *dev = DEVICE(object_property_get_link(OBJECT(con),
-                                                       "device",
-                                                       &error_abort));
-    PCIDevice *pci = (PCIDevice *) object_dynamic_cast(OBJECT(dev),
-                                                       TYPE_PCI_DEVICE);
-
-    if (pci == NULL) {
-        warn_report("Setting device address of a display device to SPICE: "
-                    "Not a PCI device.");
-        return false;
-    }
-
-    strncpy(device_address, "pci/0000", size);
-    if (!append_pci_address(device_address, size, pci)) {
-        warn_report("Setting device address of a display device to SPICE: "
-            "Too many PCI devices in the chain.");
-        return false;
-    }
-
-    return true;
-}
-
 int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con)
 {
     if (g_slist_find(spice_consoles, con)) {
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 0b787d12df..278a11b1d5 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -1144,12 +1144,15 @@ static void qemu_spice_display_init_one(QemuConsole *con)
     qemu_spice_add_display_interface(&ssd->qxl, con);
 
 #if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */
+    Error *err = NULL;
     char device_address[256] = "";
-    if (qemu_spice_fill_device_address(con, device_address, 256)) {
+    if (qemu_console_fill_device_address(con, device_address, 256, &err)) {
         spice_qxl_set_device_info(&ssd->qxl,
                                   device_address,
                                   qemu_console_get_head(con),
                                   1);
+    } else {
+        error_report_err(err);
     }
 #endif
 
diff --git a/ui/util.c b/ui/util.c
new file mode 100644
index 0000000000..7e8fc1ea53
--- /dev/null
+++ b/ui/util.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "qapi/error.h"
+#include "ui/console.h"
+
+/*
+ * Recursively (in reverse order) appends addresses of PCI devices as it moves
+ * up in the PCI hierarchy.
+ *
+ * @returns true on success, false when the buffer wasn't large enough
+ */
+static bool append_pci_address(char *buf, size_t buf_size, const PCIDevice *pci)
+{
+    PCIBus *bus = pci_get_bus(pci);
+    /*
+     * equivalent to if (!pci_bus_is_root(bus)), but the function is not built
+     * with PCI_CONFIG=n, avoid using an #ifdef by checking directly
+     */
+    if (bus->parent_dev != NULL) {
+        append_pci_address(buf, buf_size, bus->parent_dev);
+    }
+
+    size_t len = strlen(buf);
+    ssize_t written = snprintf(buf + len, buf_size - len, "/%02x.%x",
+        PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn));
+
+    return written > 0 && written < buf_size - len;
+}
+
+bool qemu_console_fill_device_address(QemuConsole *con,
+                                      char *device_address,
+                                      size_t size,
+                                      Error **errp)
+{
+    ERRP_GUARD();
+    DeviceState *dev = DEVICE(object_property_get_link(OBJECT(con),
+                                                       "device",
+                                                       &error_abort));
+    PCIDevice *pci = (PCIDevice *) object_dynamic_cast(OBJECT(dev),
+                                                       TYPE_PCI_DEVICE);
+
+    if (pci == NULL) {
+        error_setg(errp, "Setting device address of a display device: "
+                   "Not a PCI device.");
+        return false;
+    }
+
+    strncpy(device_address, "pci/0000", size);
+    if (!append_pci_address(device_address, size, pci)) {
+        error_setg(errp, "Setting device address of a display device: "
+                   "Too many PCI devices in the chain.");
+        return false;
+    }
+
+    return true;
+}
diff --git a/ui/meson.build b/ui/meson.build
index e8d3ff41b9..97cdd58856 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -11,6 +11,7 @@ softmmu_ss.add(files(
   'kbd-state.c',
   'keymaps.c',
   'qemu-pixman.c',
+  'util.c',
 ))
 softmmu_ss.add([spice_headers, files('spice-module.c')])
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 13/27] console: save current scanout details
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (11 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 12/27] ui: move qemu_spice_fill_device_address to ui/util.c marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 14/27] ui: add a D-Bus display backend marcandre.lureau
                   ` (15 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Add a new DisplayScanout structure to save the current scanout details.
This allows to attach later UI backends and set the scanout.

Introduce displaychangelistener_display_console() helper function to
handle the dpy_gfx_switch/gl_scanout() & dpy_gfx_update() calls.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h |  27 ++++++++
 ui/console.c         | 161 ++++++++++++++++++++++++++++---------------
 2 files changed, 134 insertions(+), 54 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 793f4c09ee..31141955d9 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -108,6 +108,17 @@ struct QemuConsoleClass {
 #define QEMU_ALLOCATED_FLAG     0x01
 #define QEMU_PLACEHOLDER_FLAG   0x02
 
+typedef struct ScanoutTexture {
+    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 width;
+    uint32_t height;
+} ScanoutTexture;
+
 typedef struct DisplaySurface {
     pixman_format_code_t format;
     pixman_image_t *image;
@@ -170,6 +181,22 @@ typedef struct QemuDmaBuf {
     bool      y0_top;
 } QemuDmaBuf;
 
+enum display_scanout {
+    SCANOUT_NONE,
+    SCANOUT_SURFACE,
+    SCANOUT_TEXTURE,
+    SCANOUT_DMABUF,
+};
+
+typedef struct DisplayScanout {
+    enum display_scanout kind;
+    union {
+        /* DisplaySurface *surface; is kept in QemuConsole */
+        ScanoutTexture texture;
+        QemuDmaBuf *dmabuf;
+    };
+} DisplayScanout;
+
 typedef struct DisplayState DisplayState;
 typedef struct DisplayGLCtx DisplayGLCtx;
 
diff --git a/ui/console.c b/ui/console.c
index 07357fd328..3c3be032ad 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -126,6 +126,7 @@ struct QemuConsole {
     console_type_t console_type;
     DisplayState *ds;
     DisplaySurface *surface;
+    DisplayScanout scanout;
     int dcls;
     DisplayGLCtx *gl;
     int gl_block;
@@ -197,6 +198,7 @@ static void dpy_refresh(DisplayState *s);
 static DisplayState *get_alloc_displaystate(void);
 static void text_console_update_cursor_timer(void);
 static void text_console_update_cursor(void *opaque);
+static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
 
 static void gui_update(void *opaque)
 {
@@ -532,6 +534,8 @@ static void text_console_resize(QemuConsole *s)
     TextCell *cells, *c, *c1;
     int w1, x, y, last_width;
 
+    assert(s->scanout.kind == SCANOUT_SURFACE);
+
     last_width = s->width;
     s->width = surface_width(s->surface) / FONT_WIDTH;
     s->height = surface_height(s->surface) / FONT_HEIGHT;
@@ -1103,6 +1107,45 @@ static void console_putchar(QemuConsole *s, int ch)
     }
 }
 
+static void displaychangelistener_display_console(DisplayChangeListener *dcl,
+                                                  QemuConsole *con)
+{
+    static const char nodev[] =
+        "This VM has no graphic display device.";
+    static DisplaySurface *dummy;
+
+    if (!con) {
+        if (!dcl->ops->dpy_gfx_switch) {
+            return;
+        }
+        if (!dummy) {
+            dummy = qemu_create_placeholder_surface(640, 480, nodev);
+        }
+        dcl->ops->dpy_gfx_switch(dcl, dummy);
+        return;
+    }
+
+    if (con->scanout.kind == SCANOUT_DMABUF && displaychangelistener_has_dmabuf(dcl)) {
+        dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf);
+    } else if (con->scanout.kind == SCANOUT_TEXTURE && dcl->ops->dpy_gl_scanout_texture) {
+        dcl->ops->dpy_gl_scanout_texture(dcl,
+                                         con->scanout.texture.backing_id,
+                                         con->scanout.texture.backing_y_0_top,
+                                         con->scanout.texture.backing_width,
+                                         con->scanout.texture.backing_height,
+                                         con->scanout.texture.x,
+                                         con->scanout.texture.y,
+                                         con->scanout.texture.width,
+                                         con->scanout.texture.height);
+    } else if (con->scanout.kind == SCANOUT_SURFACE && dcl->ops->dpy_gfx_switch) {
+        dcl->ops->dpy_gfx_switch(dcl, con->surface);
+    }
+
+    dcl->ops->dpy_gfx_update(dcl, 0, 0,
+                             qemu_console_get_width(con, 0),
+                             qemu_console_get_height(con, 0));
+}
+
 void console_select(unsigned int index)
 {
     DisplayChangeListener *dcl;
@@ -1119,13 +1162,7 @@ void console_select(unsigned int index)
                 if (dcl->con != NULL) {
                     continue;
                 }
-                if (dcl->ops->dpy_gfx_switch) {
-                    dcl->ops->dpy_gfx_switch(dcl, s->surface);
-                }
-            }
-            if (s->surface) {
-                dpy_gfx_update(s, 0, 0, surface_width(s->surface),
-                               surface_height(s->surface));
+                displaychangelistener_display_console(dcl, s);
             }
         }
         if (ds->have_text) {
@@ -1538,9 +1575,6 @@ static bool dpy_gl_compatible_with(QemuConsole *con, DisplayChangeListener *dcl)
 
 void register_displaychangelistener(DisplayChangeListener *dcl)
 {
-    static const char nodev[] =
-        "This VM has no graphic display device.";
-    static DisplaySurface *dummy;
     QemuConsole *con;
     Error *err = NULL;
 
@@ -1567,16 +1601,7 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
     } else {
         con = active_console;
     }
-    if (dcl->ops->dpy_gfx_switch) {
-        if (con) {
-            dcl->ops->dpy_gfx_switch(dcl, con->surface);
-        } else {
-            if (!dummy) {
-                dummy = qemu_create_placeholder_surface(640, 480, nodev);
-            }
-            dcl->ops->dpy_gfx_switch(dcl, dummy);
-        }
-    }
+    displaychangelistener_display_console(dcl, con);
     text_console_update_cursor(NULL);
 }
 
@@ -1656,13 +1681,9 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
-    int width = w;
-    int height = h;
+    int width = qemu_console_get_width(con, x + w);
+    int height = qemu_console_get_height(con, y + h);
 
-    if (con->surface) {
-        width = surface_width(con->surface);
-        height = surface_height(con->surface);
-    }
     x = MAX(x, 0);
     y = MAX(y, 0);
     x = MIN(x, width);
@@ -1685,12 +1706,10 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
 
 void dpy_gfx_update_full(QemuConsole *con)
 {
-    if (!con->surface) {
-        return;
-    }
-    dpy_gfx_update(con, 0, 0,
-                   surface_width(con->surface),
-                   surface_height(con->surface));
+    int w = qemu_console_get_width(con, 0);
+    int h = qemu_console_get_height(con, 0);
+
+    dpy_gfx_update(con, 0, 0, w, h);
 }
 
 void dpy_gfx_replace_surface(QemuConsole *con,
@@ -1717,6 +1736,7 @@ void dpy_gfx_replace_surface(QemuConsole *con,
 
     assert(old_surface != surface);
 
+    con->scanout.kind = SCANOUT_SURFACE;
     con->surface = surface;
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (con != (dcl->con ? dcl->con : active_console)) {
@@ -1892,6 +1912,9 @@ void dpy_gl_scanout_disable(QemuConsole *con)
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
 
+    if (con->scanout.kind != SCANOUT_SURFACE) {
+        con->scanout.kind = SCANOUT_NONE;
+    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         dcl->ops->dpy_gl_scanout_disable(dcl);
     }
@@ -1908,6 +1931,10 @@ void dpy_gl_scanout_texture(QemuConsole *con,
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
 
+    con->scanout.kind = SCANOUT_TEXTURE;
+    con->scanout.texture = (ScanoutTexture) {
+        backing_id, backing_y_0_top, backing_width, backing_height, x, y, width, height
+    };
     QLIST_FOREACH(dcl, &s->listeners, next) {
         dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
                                          backing_y_0_top,
@@ -1922,6 +1949,8 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con,
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
 
+    con->scanout.kind = SCANOUT_DMABUF;
+    con->scanout.dmabuf = dmabuf;
     QLIST_FOREACH(dcl, &s->listeners, next) {
         dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
     }
@@ -2048,10 +2077,8 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
     s = qemu_console_lookup_unused();
     if (s) {
         trace_console_gfx_reuse(s->index);
-        if (s->surface) {
-            width = surface_width(s->surface);
-            height = surface_height(s->surface);
-        }
+        width = qemu_console_get_width(s, 0);
+        height = qemu_console_get_height(s, 0);
     } else {
         trace_console_gfx_new();
         s = new_console(ds, GRAPHIC_CONSOLE, head);
@@ -2080,13 +2107,8 @@ void graphic_console_close(QemuConsole *con)
     static const char unplugged[] =
         "Guest display has been unplugged";
     DisplaySurface *surface;
-    int width = 640;
-    int height = 480;
-
-    if (con->surface) {
-        width = surface_width(con->surface);
-        height = surface_height(con->surface);
-    }
+    int width = qemu_console_get_width(con, 640);
+    int height = qemu_console_get_height(con, 480);
 
     trace_console_gfx_close(con->index);
     object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
@@ -2238,7 +2260,19 @@ int qemu_console_get_width(QemuConsole *con, int fallback)
     if (con == NULL) {
         con = active_console;
     }
-    return con ? surface_width(con->surface) : fallback;
+    if (con == NULL) {
+        return fallback;
+    }
+    switch (con->scanout.kind) {
+    case SCANOUT_DMABUF:
+        return con->scanout.dmabuf->width;
+    case SCANOUT_TEXTURE:
+        return con->scanout.texture.width;
+    case SCANOUT_SURFACE:
+        return surface_width(con->surface);
+    default:
+        return fallback;
+    }
 }
 
 int qemu_console_get_height(QemuConsole *con, int fallback)
@@ -2246,7 +2280,19 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
     if (con == NULL) {
         con = active_console;
     }
-    return con ? surface_height(con->surface) : fallback;
+    if (con == NULL) {
+        return fallback;
+    }
+    switch (con->scanout.kind) {
+    case SCANOUT_DMABUF:
+        return con->scanout.dmabuf->height;
+    case SCANOUT_TEXTURE:
+        return con->scanout.texture.height;
+    case SCANOUT_SURFACE:
+        return surface_height(con->surface);
+    default:
+        return fallback;
+    }
 }
 
 static void vc_chr_set_echo(Chardev *chr, bool echo)
@@ -2306,12 +2352,13 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds)
     s->total_height = DEFAULT_BACKSCROLL;
     s->x = 0;
     s->y = 0;
-    if (!s->surface) {
-        if (active_console && active_console->surface) {
-            g_width = surface_width(active_console->surface);
-            g_height = surface_height(active_console->surface);
+    if (s->scanout.kind != SCANOUT_SURFACE) {
+        if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) {
+            g_width = qemu_console_get_width(active_console, g_width);
+            g_height = qemu_console_get_height(active_console, g_height);
         }
         s->surface = qemu_create_displaysurface(g_width, g_height);
+        s->scanout.kind = SCANOUT_SURFACE;
     }
 
     s->hw_ops = &text_console_ops;
@@ -2370,6 +2417,7 @@ static void vc_chr_open(Chardev *chr,
         s = new_console(NULL, TEXT_CONSOLE, 0);
     } else {
         s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0);
+        s->scanout.kind = SCANOUT_SURFACE;
         s->surface = qemu_create_displaysurface(width, height);
     }
 
@@ -2393,13 +2441,13 @@ static void vc_chr_open(Chardev *chr,
 
 void qemu_console_resize(QemuConsole *s, int width, int height)
 {
-    DisplaySurface *surface;
+    DisplaySurface *surface = qemu_console_surface(s);
 
     assert(s->console_type == GRAPHIC_CONSOLE);
 
-    if (s->surface && (s->surface->flags & QEMU_ALLOCATED_FLAG) &&
-        pixman_image_get_width(s->surface->image) == width &&
-        pixman_image_get_height(s->surface->image) == height) {
+    if (surface && (surface->flags & QEMU_ALLOCATED_FLAG) &&
+        pixman_image_get_width(surface->image) == width &&
+        pixman_image_get_height(surface->image) == height) {
         return;
     }
 
@@ -2409,7 +2457,12 @@ void qemu_console_resize(QemuConsole *s, int width, int height)
 
 DisplaySurface *qemu_console_surface(QemuConsole *console)
 {
-    return console->surface;
+    switch (console->scanout.kind) {
+    case SCANOUT_SURFACE:
+        return console->surface;
+    default:
+        return NULL;
+    }
 }
 
 PixelFormat qemu_default_pixelformat(int bpp)
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 14/27] ui: add a D-Bus display backend
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (12 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 13/27] console: save current scanout details marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 11:03   ` Gerd Hoffmann
  2021-03-12 10:00 ` [PATCH 15/27] audio: add dbusaudio backend marcandre.lureau
                   ` (14 subsequent siblings)
  28 siblings, 1 reply; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

The "dbus" ui/display backend exports the QEMU consoles over a currently
unstable D-Bus protocol defined in ui/dbus-display1.xml (it is subject
to changes at each releases, so only client shipped with the same QEMU
release will be fully compatible).

By default, the connection is done on the session bus, but you can
specify a different bus with the "addr" option.

The backend takes the "org.qemu" service name, while still allowing
further instances to queue on the same name (so you can lookup all the
available instances too). It accepts any number of clients at this
point, although this is expected to evolve with options to restrict
clients, or only accept p2p via fd passing.

The interface is intentionally very close to the internal QEMU API,
and can be introspected or interacted with busctl/dfeet etc:

$ ./qemu-system-x86_64 -name MyVM -display dbus
$ busctl --user introspect org.qemu /org/qemu/Display1/Console_0

org.qemu.Display1.Console           interface -         -               -
.RegisterListener                   method    h         -               -
.SetUIInfo                          method    qqiiuu    -               -
.DeviceAddress                      property  s         "pci/0000/01.0" emits-change
.Head                               property  u         0               emits-change
.Height                             property  u         480             emits-change
.Label                              property  s         "VGA"           emits-change
.Type                               property  s         "Graphic"       emits-change
.Width                              property  u         640             emits-change
org.qemu.Display1.Keyboard          interface -         -               -
.Press                              method    u         -               -
.Release                            method    u         -               -
.Modifiers                          property  u         0               emits-change writable
org.qemu.Display1.Mouse             interface -         -               -
.Press                              method    u         -               -
.Release                            method    u         -               -
.SetAbsPosition                     method    uu        -               -

The "RegisterListener" method takes a FD as argument and expect the
client to implement the org.qemu.Display1.Listener D-Bus p2p interface
over it. This allows to notify of display updates, sending big chunk of
data if necessary without going through the bus. However, DMABUF updates
are preferred.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 meson.build          |   5 +
 qapi/ui.json         |  24 ++-
 include/qemu/dbus.h  |  20 ++
 ui/dbus.h            |  68 ++++++
 ui/dbus-console.c    | 457 ++++++++++++++++++++++++++++++++++++++++
 ui/dbus-error.c      |  45 ++++
 ui/dbus-listener.c   | 480 +++++++++++++++++++++++++++++++++++++++++++
 ui/dbus.c            | 255 +++++++++++++++++++++++
 util/module.c        |   2 +
 qemu-options.hx      |   8 +
 ui/dbus-display1.xml | 123 +++++++++++
 ui/meson.build       |  17 ++
 ui/trace-events      |  11 +
 13 files changed, 1513 insertions(+), 2 deletions(-)
 create mode 100644 ui/dbus.h
 create mode 100644 ui/dbus-console.c
 create mode 100644 ui/dbus-error.c
 create mode 100644 ui/dbus-listener.c
 create mode 100644 ui/dbus.c
 create mode 100644 ui/dbus-display1.xml

diff --git a/meson.build b/meson.build
index a7d2dd429d..b6f680bd34 100644
--- a/meson.build
+++ b/meson.build
@@ -1861,6 +1861,11 @@ if 'CONFIG_VHOST_USER' in config_host
   vhost_user = libvhost_user.get_variable('vhost_user_dep')
 endif
 
+# FIXME enable_modules shouldn't be necessary, but: https://github.com/mesonbuild/meson/issues/8333
+if enable_modules and config_host.has_key('CONFIG_GIO') and config_host.has_key('GDBUS_CODEGEN')
+  config_host += {'CONFIG_UI_DBUS': 'y'}
+endif
+
 subdir('qapi')
 subdir('qobject')
 subdir('stubs')
diff --git a/qapi/ui.json b/qapi/ui.json
index d08d72b439..bdfab800c0 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1068,6 +1068,23 @@
 { 'struct'  : 'DisplayEGLHeadless',
   'data'    : { '*rendernode' : 'str' } }
 
+##
+# @DisplayDBus:
+#
+# DBus display options.
+#
+# @addr: The D-Bus bus address (default to the session bus).
+#
+# @rendernode: Which DRM render node should be used. Default is the first
+#              available node on the host.
+#
+# Since: X.X
+#
+##
+{ 'struct'  : 'DisplayDBus',
+  'data'    : { '*rendernode' : 'str',
+                '*addr': 'str' } }
+
  ##
  # @DisplayGLMode:
  #
@@ -1133,13 +1150,15 @@
 #             application to connect to it. The server will redirect
 #             the serial console and QEMU monitors. (Since 4.0)
 #
+# @dbus: Start a D-Bus service for the display. (Since X.X)
+#
 # Since: 2.12
 #
 ##
 { 'enum'    : 'DisplayType',
   'data'    : [ 'default', 'none', 'gtk', 'sdl',
                 'egl-headless', 'curses', 'cocoa',
-                'spice-app'] }
+                'spice-app', 'dbus'] }
 
 ##
 # @DisplayOptions:
@@ -1165,7 +1184,8 @@
   'discriminator' : 'type',
   'data'    : { 'gtk'            : 'DisplayGTK',
                 'curses'         : 'DisplayCurses',
-                'egl-headless'   : 'DisplayEGLHeadless'} }
+                'egl-headless'   : 'DisplayEGLHeadless',
+                'dbus'           : 'DisplayDBus'} }
 
 ##
 # @query-display-options:
diff --git a/include/qemu/dbus.h b/include/qemu/dbus.h
index 9d591f9ee4..c2d7f55379 100644
--- a/include/qemu/dbus.h
+++ b/include/qemu/dbus.h
@@ -12,6 +12,26 @@
 
 #include <gio/gio.h>
 
+/* glib/gio 2.68 */
+#define DBUS_METHOD_INVOCATION_HANDLED TRUE
+#define DBUS_METHOD_INVOCATION_UNHANDLED FALSE
+
+/* in msec */
+#define DBUS_DEFAULT_TIMEOUT 1000
+
+#define DBUS_DISPLAY1_ROOT "/org/qemu/Display1"
+
+#define DBUS_DISPLAY_ERROR (dbus_display_error_quark ())
+GQuark dbus_display_error_quark (void);
+
+typedef enum
+{
+    DBUS_DISPLAY_ERROR_FAILED,
+    DBUS_DISPLAY_ERROR_INVALID,
+    DBUS_DISPLAY_ERROR_UNSUPPORTED,
+    DBUS_DISPLAY_N_ERRORS,
+} DBusDisplayError;
+
 GStrv qemu_dbus_get_queued_owners(GDBusConnection *connection,
                                   const char *name,
                                   Error **errp);
diff --git a/ui/dbus.h b/ui/dbus.h
new file mode 100644
index 0000000000..f554084a27
--- /dev/null
+++ b/ui/dbus.h
@@ -0,0 +1,68 @@
+/*
+ * QEMU DBus display
+ *
+ * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef UI_DBUS_H_
+#define UI_DBUS_H_
+
+#include "qemu/dbus.h"
+#include "qom/object.h"
+#include "ui/console.h"
+
+#include "dbus-display1.h"
+
+struct DBusDisplay {
+    Object parent;
+
+    DisplayGLMode gl_mode;
+    char *dbus_addr;
+    DisplayGLCtx glctx;
+
+    GDBusConnection *bus;
+    GDBusObjectManagerServer *server;
+    DBusDisplayDisplay1VM *iface;
+    GPtrArray *consoles;
+};
+
+#define TYPE_DBUS_DISPLAY "dbus-display"
+OBJECT_DECLARE_SIMPLE_TYPE(DBusDisplay, DBUS_DISPLAY)
+
+#define DBUS_DISPLAY_TYPE_CONSOLE dbus_display_console_get_type()
+G_DECLARE_FINAL_TYPE(DBusDisplayConsole, dbus_display_console, DBUS_DISPLAY, CONSOLE, GDBusObjectSkeleton)
+
+DBusDisplayConsole *dbus_display_console_new(DBusDisplay *display, QemuConsole *con);
+int dbus_display_console_get_index(DBusDisplayConsole *self);
+
+#define DBUS_DISPLAY_TYPE_LISTENER dbus_display_listener_get_type()
+G_DECLARE_FINAL_TYPE(DBusDisplayListener, dbus_display_listener, DBUS_DISPLAY, LISTENER, GObject)
+
+DBusDisplayListener *dbus_display_listener_new(const char *bus_name,
+                                               GDBusConnection *conn,
+                                               DBusDisplayConsole *console);
+
+DBusDisplayConsole *dbus_display_listener_get_console(DBusDisplayListener *self);
+const char *dbus_display_listener_get_bus_name(DBusDisplayListener *self);
+
+extern const DisplayChangeListenerOps dbus_gl_dcl_ops;
+extern const DisplayChangeListenerOps dbus_dcl_ops;
+
+#endif /* UI_DBUS_H_ */
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
new file mode 100644
index 0000000000..3487a8f5cb
--- /dev/null
+++ b/ui/dbus-console.c
@@ -0,0 +1,457 @@
+/*
+ * QEMU DBus display console
+ *
+ * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "ui/input.h"
+#include "ui/kbd-state.h"
+#include "trace.h"
+
+#include <gio/gunixfdlist.h>
+
+#include "dbus.h"
+
+struct _DBusDisplayConsole {
+    GDBusObjectSkeleton parent_instance;
+    DisplayChangeListener dcl;
+
+    DBusDisplay *display;
+    QemuConsole *con;
+    GHashTable *listeners;
+    DBusDisplayDisplay1Console *iface;
+
+    DBusDisplayDisplay1Keyboard *iface_kbd;
+    QKbdState *kbd;
+
+    DBusDisplayDisplay1Mouse *iface_mouse;
+    gboolean last_set;
+    guint last_x;
+    guint last_y;
+};
+
+G_DEFINE_TYPE(DBusDisplayConsole, dbus_display_console, G_TYPE_DBUS_OBJECT_SKELETON)
+
+static void
+dbus_display_console_set_size(DBusDisplayConsole *self, uint32_t width, uint32_t height)
+{
+    g_object_set(self->iface,
+                 "width", width,
+                 "height", height,
+                 NULL);
+}
+
+static void
+dbus_gfx_switch(DisplayChangeListener *dcl,
+                struct DisplaySurface *new_surface)
+{
+    DBusDisplayConsole *self = container_of(dcl, DBusDisplayConsole, dcl);
+
+    dbus_display_console_set_size(self,
+                                  surface_width(new_surface),
+                                  surface_height(new_surface));
+}
+
+static void
+dbus_gfx_update(DisplayChangeListener *dcl,
+                int x, int y, int w, int h)
+{
+}
+
+static void
+dbus_gl_scanout_disable(DisplayChangeListener *dcl)
+{
+}
+
+static void
+dbus_gl_scanout_texture(DisplayChangeListener *dcl,
+                        uint32_t tex_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)
+{
+    DBusDisplayConsole *self = container_of(dcl, DBusDisplayConsole, dcl);
+
+    dbus_display_console_set_size(self, w, h);
+}
+
+static void
+dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl,
+                       QemuDmaBuf *dmabuf)
+{
+    DBusDisplayConsole *self = container_of(dcl, DBusDisplayConsole, dcl);
+
+    dbus_display_console_set_size(self,
+                                  dmabuf->width,
+                                  dmabuf->height);
+}
+
+static void
+dbus_gl_scanout_update(DisplayChangeListener *dcl,
+                       uint32_t x, uint32_t y,
+                       uint32_t w, uint32_t h)
+{
+}
+
+static const DisplayChangeListenerOps dbus_console_dcl_ops = {
+    .dpy_name                = "dbus-console",
+    .dpy_gfx_switch          = dbus_gfx_switch,
+    .dpy_gfx_update          = dbus_gfx_update,
+    .dpy_gl_scanout_disable  = dbus_gl_scanout_disable,
+    .dpy_gl_scanout_texture  = dbus_gl_scanout_texture,
+    .dpy_gl_scanout_dmabuf   = dbus_gl_scanout_dmabuf,
+    .dpy_gl_update           = dbus_gl_scanout_update,
+};
+
+static void
+dbus_display_console_init(DBusDisplayConsole *object)
+{
+    DBusDisplayConsole *self = DBUS_DISPLAY_CONSOLE(object);
+
+    self->listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                            NULL, g_object_unref);
+    self->dcl.ops = &dbus_console_dcl_ops;
+}
+
+static void
+dbus_display_console_dispose(GObject *object)
+{
+    DBusDisplayConsole *self = DBUS_DISPLAY_CONSOLE(object);
+
+    unregister_displaychangelistener(&self->dcl);
+    g_clear_object(&self->iface_kbd);
+    g_clear_object(&self->iface);
+    g_clear_pointer(&self->listeners, g_hash_table_unref);
+    g_clear_pointer(&self->kbd, qkbd_state_free);
+
+    G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object);
+}
+
+static void
+dbus_display_console_class_init(DBusDisplayConsoleClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+    gobject_class->dispose = dbus_display_console_dispose;
+}
+
+static void
+listener_vanished_cb(DBusDisplayListener *listener)
+{
+    DBusDisplayConsole *self = dbus_display_listener_get_console(listener);
+    const char *name = dbus_display_listener_get_bus_name(listener);
+
+    trace_dbus_listener_vanished(name);
+
+    g_hash_table_remove(self->listeners, name);
+    qkbd_state_lift_all_keys(self->kbd);
+}
+
+static gboolean
+dbus_console_set_ui_info(DBusDisplayConsole *self,
+                         GDBusMethodInvocation *invocation,
+                         guint16 arg_width_mm,
+                         guint16 arg_height_mm,
+                         gint arg_xoff,
+                         gint arg_yoff,
+                         guint arg_width,
+                         guint arg_height)
+{
+    QemuUIInfo info = {
+        .width_mm = arg_width_mm,
+        .height_mm = arg_height_mm,
+        .xoff = arg_xoff,
+        .yoff = arg_yoff,
+        .width = arg_width,
+        .height = arg_height,
+    };
+
+    if (!dpy_ui_info_supported(self->con)) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_UNSUPPORTED,
+                                              "SetUIInfo is not supported by guest");
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    dpy_set_ui_info(self->con, &info);
+    dbus_display_display1_console_complete_set_uiinfo(self->iface, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+dbus_console_register_listener(DBusDisplayConsole *self,
+                               GDBusMethodInvocation *invocation,
+                               GUnixFDList *fd_list,
+                               GVariant *arg_listener)
+{
+    const char *sender = g_dbus_method_invocation_get_sender(invocation);
+    GDBusConnection *listener_conn;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GSocket) socket = NULL;
+    g_autoptr(GSocketConnection) socket_conn = NULL;
+    g_autofree char *guid = g_dbus_generate_guid();
+    DBusDisplayListener *listener;
+    int fd;
+
+    if (g_hash_table_contains(self->listeners, sender)) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_INVALID,
+                                              "`%s` is already registered!",
+                                              sender);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
+    if (err) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_FAILED,
+                                              "Couldn't get peer fd: %s", err->message);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    socket = g_socket_new_from_fd(fd, &err);
+    if (err) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_FAILED,
+                                              "Couldn't make a socket: %s", err->message);
+        close(fd);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+    socket_conn = g_socket_connection_factory_create_connection(socket);
+    /* return now: easier for the other end, as it may handle priv dbus synchronously */
+    dbus_display_display1_console_complete_register_listener(self->iface, invocation, NULL);
+
+    listener_conn = g_dbus_connection_new_sync(G_IO_STREAM(socket_conn),
+                                               guid,
+                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
+                                               NULL, NULL, &err);
+    if (err) {
+        error_report("Failed to setup peer connection: %s", err->message);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    listener = dbus_display_listener_new(sender, listener_conn, self);
+    if (!listener) {
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    g_hash_table_insert(self->listeners,
+                        (gpointer)dbus_display_listener_get_bus_name(listener),
+                        listener);
+    g_object_connect(listener_conn,
+                     "swapped-signal::closed", listener_vanished_cb, listener,
+                     NULL);
+
+    trace_dbus_registered_listener(sender);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+dbus_kbd_press(DBusDisplayConsole *self,
+               GDBusMethodInvocation *invocation,
+               guint arg_keycode)
+{
+    QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode);
+
+    trace_dbus_kbd_press(arg_keycode);
+
+    qkbd_state_key_event(self->kbd, qcode, true);
+
+    dbus_display_display1_keyboard_complete_press(self->iface_kbd, invocation);
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+dbus_kbd_release(DBusDisplayConsole *self,
+                 GDBusMethodInvocation *invocation,
+                 guint arg_keycode)
+{
+    QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode);
+
+    trace_dbus_kbd_release(arg_keycode);
+
+    qkbd_state_key_event(self->kbd, qcode, false);
+
+    dbus_display_display1_keyboard_complete_release(self->iface_kbd, invocation);
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+dbus_kbd_modifiers_changed(DBusDisplayConsole *self,
+                           GParamSpec *pspec)
+{
+    guint modifiers = dbus_display_display1_keyboard_get_modifiers(self->iface_kbd);
+
+    trace_dbus_kbd_modifiers_changed(modifiers);
+}
+
+static void
+dbus_kbd_qemu_leds_updated(void *data, int ledstate)
+{
+    DBusDisplayConsole *self = DBUS_DISPLAY_CONSOLE(data);
+
+    // FIXME: what about self->kbd state?
+    dbus_display_display1_keyboard_set_modifiers(self->iface_kbd, ledstate);
+}
+
+static gboolean
+dbus_mouse_set_pos(DBusDisplayConsole *self,
+                   GDBusMethodInvocation *invocation,
+                   guint x, guint y)
+{
+    int width, height;
+
+    trace_dbus_mouse_set_pos(x, y);
+
+    width = qemu_console_get_width(self->con, 0);
+    height = qemu_console_get_height(self->con, 0);
+    if (qemu_input_is_absolute()) {
+        if (x >= width || y >= height) {
+            g_dbus_method_invocation_return_error(invocation,
+                                                  DBUS_DISPLAY_ERROR,
+                                                  DBUS_DISPLAY_ERROR_INVALID,
+                                                  "Invalid mouse position");
+            return DBUS_METHOD_INVOCATION_HANDLED;
+        }
+        qemu_input_queue_abs(self->con, INPUT_AXIS_X, x, 0, width);
+        qemu_input_queue_abs(self->con, INPUT_AXIS_Y, y, 0, height);
+        qemu_input_event_sync();
+    } else if (self->last_set) {
+        qemu_input_queue_rel(self->con, INPUT_AXIS_X, x - self->last_x);
+        qemu_input_queue_rel(self->con, INPUT_AXIS_Y, y - self->last_y);
+        qemu_input_event_sync();
+    }
+
+    self->last_x = x;
+    self->last_y = y;
+    self->last_set = TRUE;
+
+    dbus_display_display1_mouse_complete_set_abs_position(self->iface_mouse, invocation);
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+dbus_mouse_press(DBusDisplayConsole *self,
+                 GDBusMethodInvocation *invocation,
+                 guint button)
+{
+    trace_dbus_mouse_press(button);
+
+    qemu_input_queue_btn(self->con, button, true);
+    qemu_input_event_sync();
+
+    dbus_display_display1_mouse_complete_press(self->iface_mouse, invocation);
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+dbus_mouse_release(DBusDisplayConsole *self,
+                   GDBusMethodInvocation *invocation,
+                   guint button)
+{
+    trace_dbus_mouse_release(button);
+
+    qemu_input_queue_btn(self->con, button, false);
+    qemu_input_event_sync();
+
+    dbus_display_display1_mouse_complete_release(self->iface_mouse, invocation);
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+int dbus_display_console_get_index(DBusDisplayConsole *self)
+{
+    return qemu_console_get_index(self->con);
+}
+
+DBusDisplayConsole *
+dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
+{
+    g_autofree char *path = NULL;
+    g_autofree char *label = NULL;
+    char device_addr[256] = "";
+    DBusDisplayConsole *self;
+    int idx;
+
+    assert(display);
+    assert(con);
+
+    label = qemu_console_get_label(con);
+    idx = qemu_console_get_index(con);
+    path = g_strdup_printf(DBUS_DISPLAY1_ROOT "/Console_%d", idx);
+    self = g_object_new(DBUS_DISPLAY_TYPE_CONSOLE,
+                        "g-object-path", path,
+                        NULL);
+    self->display = display;
+    self->con = con;
+    /* we should handle errors, and skip non graphics? */
+    qemu_console_fill_device_address(con, device_addr, sizeof(device_addr), NULL);
+
+    self->iface = dbus_display_display1_console_skeleton_new();
+    g_object_set(self->iface,
+        "label", label,
+        "type", qemu_console_is_graphic(con) ? "Graphic" : "Text",
+        "head", qemu_console_get_head(con),
+        "width", qemu_console_get_width(con, 0),
+        "height", qemu_console_get_height(con, 0),
+        "device-address", device_addr,
+        NULL);
+    g_object_connect(self->iface,
+        "swapped-signal::handle-register-listener", dbus_console_register_listener, self,
+        "swapped-signal::handle-set-uiinfo", dbus_console_set_ui_info, self,
+        NULL);
+    g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(self),
+        G_DBUS_INTERFACE_SKELETON(self->iface));
+
+    self->kbd = qkbd_state_init(con);
+    self->iface_kbd = dbus_display_display1_keyboard_skeleton_new();
+    qemu_add_led_event_handler(dbus_kbd_qemu_leds_updated, self);
+    g_object_connect(self->iface_kbd,
+        "swapped-signal::handle-press", dbus_kbd_press, self,
+        "swapped-signal::handle-release", dbus_kbd_release, self,
+        "swapped-signal::notify::modifiers", dbus_kbd_modifiers_changed, self,
+        NULL);
+    g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(self),
+        G_DBUS_INTERFACE_SKELETON(self->iface_kbd));
+
+    self->iface_mouse = dbus_display_display1_mouse_skeleton_new();
+    g_object_connect(self->iface_mouse,
+        "swapped-signal::handle-set-abs-position", dbus_mouse_set_pos, self,
+        "swapped-signal::handle-press", dbus_mouse_press, self,
+        "swapped-signal::handle-release", dbus_mouse_release, self,
+        NULL);
+    g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(self),
+        G_DBUS_INTERFACE_SKELETON(self->iface_mouse));
+
+    register_displaychangelistener(&self->dcl);
+    return self;
+}
diff --git a/ui/dbus-error.c b/ui/dbus-error.c
new file mode 100644
index 0000000000..9575bb6aa9
--- /dev/null
+++ b/ui/dbus-error.c
@@ -0,0 +1,45 @@
+/*
+ * QEMU DBus display errors
+ *
+ * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "dbus.h"
+
+static const GDBusErrorEntry dbus_display_error_entries[] =
+{
+  { DBUS_DISPLAY_ERROR_FAILED, "org.qemu.Display1.Error.Failed" },
+  { DBUS_DISPLAY_ERROR_INVALID, "org.qemu.Display1.Error.Invalid" },
+  { DBUS_DISPLAY_ERROR_UNSUPPORTED, "org.qemu.Display1.Error.Unsupported" },
+};
+
+G_STATIC_ASSERT(G_N_ELEMENTS(dbus_display_error_entries) == DBUS_DISPLAY_N_ERRORS);
+
+GQuark
+dbus_display_error_quark(void)
+{
+  static volatile gsize quark_volatile = 0;
+  g_dbus_error_register_error_domain("dbus-display-error-quark",
+                                     &quark_volatile,
+                                     dbus_display_error_entries,
+                                     G_N_ELEMENTS(dbus_display_error_entries));
+  return (GQuark)quark_volatile;
+}
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
new file mode 100644
index 0000000000..2ef8e0e921
--- /dev/null
+++ b/ui/dbus-listener.c
@@ -0,0 +1,480 @@
+/*
+ * QEMU DBus display console
+ *
+ * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "sysemu/sysemu.h"
+#include "dbus.h"
+#include <gio/gunixfdlist.h>
+
+#include "ui/shader.h"
+#include "ui/egl-helpers.h"
+#include "ui/egl-context.h"
+#include "trace.h"
+
+struct _DBusDisplayListener
+{
+    GObject parent;
+
+    char *bus_name;
+    DBusDisplayConsole *console;
+    GDBusConnection *conn;
+
+    DBusDisplayDisplay1Listener *proxy;
+
+    DisplayChangeListener dcl;
+    DisplaySurface *ds;
+    QemuGLShader *gls;
+    int gl_updates;
+};
+
+G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
+
+static void dbus_update_gl_cb(GObject *source_object,
+                           GAsyncResult *res,
+                           gpointer user_data)
+{
+    g_autoptr(GError) err = NULL;
+    DBusDisplayListener *self = user_data;
+
+    if (!dbus_display_display1_listener_call_update_dmabuf_finish(self->proxy, res, &err)) {
+        error_report("Failed to call update: %s", err->message);
+    }
+
+    graphic_hw_gl_block(self->dcl.con, false);
+    g_object_unref(self);
+}
+
+static void dbus_call_update_gl(DBusDisplayListener *self,
+                                int x, int y, int w, int h)
+{
+    graphic_hw_gl_block(self->dcl.con, true);
+    glFlush();
+    dbus_display_display1_listener_call_update_dmabuf(self->proxy,
+        x, y, w, h,
+        G_DBUS_CALL_FLAGS_NONE,
+        DBUS_DEFAULT_TIMEOUT, NULL,
+        dbus_update_gl_cb,
+        g_object_ref(self));
+}
+
+static void dbus_scanout_disable(DisplayChangeListener *dcl)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+
+    self->ds = NULL;
+    dbus_display_display1_listener_call_disable(
+        self->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
+                                QemuDmaBuf *dmabuf)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GUnixFDList) fd_list = NULL;
+
+    fd_list = g_unix_fd_list_new();
+    if (g_unix_fd_list_append(fd_list, dmabuf->fd, &err) != 0) {
+        error_report("Failed to setup dmabuf fdlist: %s", err->message);
+        return;
+    }
+
+    dbus_display_display1_listener_call_scanout_dmabuf(
+        self->proxy,
+        g_variant_new_handle(0),
+        dmabuf->width,
+        dmabuf->height,
+        dmabuf->stride,
+        dmabuf->fourcc,
+        dmabuf->modifier,
+        dmabuf->y0_top,
+        G_DBUS_CALL_FLAGS_NONE,
+        -1,
+        fd_list,
+        NULL, NULL, NULL);
+}
+
+static void dbus_scanout_texture(DisplayChangeListener *dcl,
+                                 uint32_t tex_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)
+{
+    QemuDmaBuf dmabuf = {
+        .width = backing_width,
+        .height = backing_height,
+        .y0_top = backing_y_0_top,
+    };
+
+    assert(tex_id);
+    dmabuf.fd = egl_get_fd_for_texture(tex_id, (EGLint *)&dmabuf.stride,
+                                       (EGLint *)&dmabuf.fourcc, &dmabuf.modifier);
+    if (dmabuf.fd < 0) {
+        error_report("%s: failed to get fd for texture", __func__);
+        return;
+    }
+
+    dbus_scanout_dmabuf(dcl, &dmabuf);
+    close(dmabuf.fd);
+}
+
+static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
+                               QemuDmaBuf *dmabuf, bool have_hot,
+                               uint32_t hot_x, uint32_t hot_y)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+    DisplaySurface *ds;
+    GVariant *v_data = NULL;
+    egl_fb cursor_fb;
+
+    if (!dmabuf) {
+        dbus_display_display1_listener_call_mouse_set(
+            self->proxy, 0, 0, false, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        return;
+    }
+
+    egl_dmabuf_import_texture(dmabuf);
+    if (!dmabuf->texture) {
+        return;
+    }
+    egl_fb_setup_for_tex(&cursor_fb, dmabuf->width, dmabuf->height,
+                         dmabuf->texture, false);
+    ds = qemu_create_displaysurface(dmabuf->width, dmabuf->height);
+    egl_fb_read(ds, &cursor_fb);
+
+    v_data = g_variant_new_from_data(
+        G_VARIANT_TYPE("ay"),
+        surface_data(ds),
+        surface_width(ds) * surface_height(ds) * 4,
+        TRUE,
+        (GDestroyNotify)qemu_free_displaysurface,
+        ds);
+    dbus_display_display1_listener_call_cursor_define(
+        self->proxy,
+        surface_width(ds),
+        surface_height(ds),
+        hot_x,
+        hot_y,
+        v_data,
+        G_DBUS_CALL_FLAGS_NONE,
+        -1,
+        NULL,
+        NULL,
+        NULL);
+}
+
+static void dbus_cursor_position(DisplayChangeListener *dcl,
+                                 uint32_t pos_x, uint32_t pos_y)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+
+    dbus_display_display1_listener_call_mouse_set(
+        self->proxy, pos_x, pos_y, true, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void dbus_release_dmabuf(DisplayChangeListener *dcl,
+                                QemuDmaBuf *dmabuf)
+{
+    dbus_scanout_disable(dcl);
+}
+
+static void dbus_scanout_update(DisplayChangeListener *dcl,
+                                uint32_t x, uint32_t y,
+                                uint32_t w, uint32_t h)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+
+    dbus_call_update_gl(self, x, y, w, h);
+}
+
+static void dbus_gl_refresh(DisplayChangeListener *dcl)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+
+    if (!self->ds || qemu_console_is_gl_blocked(self->dcl.con)) {
+        return;
+    }
+
+    graphic_hw_update(dcl->con);
+    if (self->gl_updates) {
+        dbus_call_update_gl(self, 0, 0,
+                            surface_width(self->ds), surface_height(self->ds));
+        self->gl_updates = 0;
+    }
+}
+
+static void dbus_refresh(DisplayChangeListener *dcl)
+{
+    graphic_hw_update(dcl->con);
+}
+
+static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
+                               int x, int y, int w, int h)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+
+    if (self->ds) {
+        surface_gl_update_texture(self->gls, self->ds, x, y, w, h);
+    }
+
+    self->gl_updates++;
+}
+
+static void dbus_gfx_update(DisplayChangeListener *dcl,
+                            int x, int y, int w, int h)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+    pixman_image_t *img;
+    GVariant *v_data;
+    size_t stride;
+
+    assert(self->ds);
+    stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(self->ds)), 8);
+
+    trace_dbus_update(x, y, w, h);
+
+    /* make a copy, since gvariant only handles linear data */
+    img = pixman_image_create_bits(surface_format(self->ds),
+                                   w, h, NULL, stride);
+    pixman_image_composite(PIXMAN_OP_SRC, self->ds->image, NULL, img,
+                           x, y, 0, 0, 0, 0, w, h);
+
+    v_data = g_variant_new_from_data(
+        G_VARIANT_TYPE("ay"),
+        pixman_image_get_data(img),
+        pixman_image_get_stride(img) * h,
+        TRUE,
+        (GDestroyNotify)pixman_image_unref,
+        img);
+    dbus_display_display1_listener_call_update(self->proxy,
+        x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img),
+        v_data,
+        G_DBUS_CALL_FLAGS_NONE,
+        DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+}
+
+static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
+                               struct DisplaySurface *new_surface)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+
+    if (self->ds) {
+        surface_gl_destroy_texture(self->gls, self->ds);
+    }
+    self->ds = new_surface;
+    if (self->ds) {
+        int width = surface_width(self->ds);
+        int height = surface_height(self->ds);
+
+        surface_gl_create_texture(self->gls, self->ds);
+        dbus_scanout_texture(&self->dcl, self->ds->texture, true,
+                             width, height, 0, 0, width, height);
+    }
+}
+
+static void dbus_gfx_switch(DisplayChangeListener *dcl,
+                            struct DisplaySurface *new_surface)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+    GVariant *v_data = NULL;
+
+    self->ds = new_surface;
+    if (!self->ds) {
+        /* why not call disable instead? */
+        return;
+    }
+
+    v_data = g_variant_new_from_data(
+        G_VARIANT_TYPE("ay"),
+        surface_data(self->ds),
+        surface_stride(self->ds) * surface_height(self->ds),
+        TRUE,
+        (GDestroyNotify)pixman_image_unref,
+        pixman_image_ref(self->ds->image));
+    dbus_display_display1_listener_call_scanout(self->proxy,
+        surface_width(self->ds),
+        surface_height(self->ds),
+        surface_stride(self->ds),
+        surface_format(self->ds),
+        v_data,
+        G_DBUS_CALL_FLAGS_NONE,
+        DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+}
+
+static void dbus_mouse_set(DisplayChangeListener *dcl,
+                           int x, int y, int on)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+
+    dbus_display_display1_listener_call_mouse_set(
+        self->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void dbus_cursor_define(DisplayChangeListener *dcl,
+                               QEMUCursor *c)
+{
+    DBusDisplayListener *self = container_of(dcl, DBusDisplayListener, dcl);
+    GVariant *v_data = NULL;
+
+    cursor_get(c);
+    v_data = g_variant_new_from_data(
+        G_VARIANT_TYPE("ay"),
+        c->data,
+        c->width * c->height * 4,
+        TRUE,
+        (GDestroyNotify)cursor_put,
+        c);
+
+    dbus_display_display1_listener_call_cursor_define(
+        self->proxy,
+        c->width,
+        c->height,
+        c->hot_x,
+        c->hot_y,
+        v_data,
+        G_DBUS_CALL_FLAGS_NONE,
+        -1,
+        NULL,
+        NULL,
+        NULL);
+}
+
+const DisplayChangeListenerOps dbus_gl_dcl_ops = {
+    .dpy_name                = "dbus-gl",
+    .dpy_gfx_update          = dbus_gl_gfx_update,
+    .dpy_gfx_switch          = dbus_gl_gfx_switch,
+    .dpy_gfx_check_format    = console_gl_check_format,
+    .dpy_refresh             = dbus_gl_refresh,
+    .dpy_mouse_set           = dbus_mouse_set,
+    .dpy_cursor_define       = dbus_cursor_define,
+
+    .dpy_gl_scanout_disable  = dbus_scanout_disable,
+    .dpy_gl_scanout_texture  = dbus_scanout_texture,
+    .dpy_gl_scanout_dmabuf   = dbus_scanout_dmabuf,
+    .dpy_gl_cursor_dmabuf    = dbus_cursor_dmabuf,
+    .dpy_gl_cursor_position  = dbus_cursor_position,
+    .dpy_gl_release_dmabuf   = dbus_release_dmabuf,
+    .dpy_gl_update           = dbus_scanout_update,
+};
+
+const DisplayChangeListenerOps dbus_dcl_ops = {
+    .dpy_name                = "dbus",
+    .dpy_gfx_update          = dbus_gfx_update,
+    .dpy_gfx_switch          = dbus_gfx_switch,
+    .dpy_refresh             = dbus_refresh,
+    .dpy_mouse_set           = dbus_mouse_set,
+    .dpy_cursor_define       = dbus_cursor_define,
+};
+
+static void
+dbus_display_listener_dispose(GObject *object)
+{
+    DBusDisplayListener *self = DBUS_DISPLAY_LISTENER(object);
+
+    unregister_displaychangelistener(&self->dcl);
+    g_clear_object(&self->conn);
+    g_clear_pointer(&self->bus_name, g_free);
+    g_clear_object(&self->proxy);
+    g_clear_pointer(&self->gls, qemu_gl_fini_shader);
+
+    G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
+}
+
+static void
+dbus_display_listener_constructed(GObject *object)
+{
+    DBusDisplayListener *self = DBUS_DISPLAY_LISTENER(object);
+
+    if (display_opengl) {
+        self->gls = qemu_gl_init_shader();
+        self->dcl.ops = &dbus_gl_dcl_ops;
+    } else {
+        self->dcl.ops = &dbus_dcl_ops;
+    }
+
+    G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object);
+}
+
+static void
+dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+    object_class->dispose = dbus_display_listener_dispose;
+    object_class->constructed = dbus_display_listener_constructed;
+}
+
+static void
+dbus_display_listener_init(DBusDisplayListener *self)
+{
+}
+
+const char *
+dbus_display_listener_get_bus_name(DBusDisplayListener *self)
+{
+    return self->bus_name;
+}
+
+DBusDisplayConsole *
+dbus_display_listener_get_console(DBusDisplayListener *self)
+{
+    return self->console;
+}
+
+DBusDisplayListener *
+dbus_display_listener_new(const char *bus_name,
+                          GDBusConnection *conn,
+                          DBusDisplayConsole *console)
+{
+    DBusDisplayListener *self;
+    QemuConsole *con;
+    g_autoptr(GError) err = NULL;
+
+    self = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL);
+    self->proxy =
+        dbus_display_display1_listener_proxy_new_sync(conn,
+            G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+            NULL,
+            "/org/qemu/Display1/Listener",
+            NULL,
+            &err);
+    if (!self->proxy) {
+        error_report("Failed to setup proxy: %s", err->message);
+        g_object_unref(conn);
+        g_object_unref(self);
+        return NULL;
+    }
+
+    self->bus_name = g_strdup(bus_name);
+    self->conn = conn;
+    self->console = console;
+
+    con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
+    assert(con);
+    self->dcl.con = con;
+    register_displaychangelistener(&self->dcl);
+
+    return self;
+}
diff --git a/ui/dbus.c b/ui/dbus.c
new file mode 100644
index 0000000000..089a92cedf
--- /dev/null
+++ b/ui/dbus.c
@@ -0,0 +1,255 @@
+/*
+ * QEMU DBus display
+ *
+ * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu/dbus.h"
+#include "qemu/option.h"
+#include "qom/object_interfaces.h"
+#include "sysemu/sysemu.h"
+#include "ui/egl-helpers.h"
+#include "ui/egl-context.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+#include "dbus.h"
+
+static QEMUGLContext dbus_create_context(DisplayGLCtx *dgc,
+                                         QEMUGLParams *params)
+{
+    eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                   qemu_egl_rn_ctx);
+    return qemu_egl_create_context(dgc, params);
+}
+
+static const DisplayGLCtxOps dbus_gl_ops = {
+    .compatible_dcl          = &dbus_gl_dcl_ops,
+    .dpy_gl_ctx_create       = dbus_create_context,
+    .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
+    .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
+};
+
+static void
+dbus_display_init(Object *o)
+{
+    DBusDisplay *self = DBUS_DISPLAY(o);
+    g_autoptr(GDBusObjectSkeleton) vm = NULL;
+
+    self->glctx.ops = &dbus_gl_ops;
+    self->iface = dbus_display_display1_vm_skeleton_new();
+    self->consoles = g_ptr_array_new_with_free_func(g_object_unref);
+
+    self->server = g_dbus_object_manager_server_new(DBUS_DISPLAY1_ROOT);
+
+    vm = g_dbus_object_skeleton_new(DBUS_DISPLAY1_ROOT "/VM");
+    g_dbus_object_skeleton_add_interface(vm, G_DBUS_INTERFACE_SKELETON(self->iface));
+    g_dbus_object_manager_server_export(self->server, vm);
+}
+
+static void
+dbus_display_finalize(Object *o)
+{
+    DBusDisplay *self = DBUS_DISPLAY(o);
+
+    g_clear_object(&self->server);
+    g_clear_pointer(&self->consoles, g_ptr_array_unref);
+    g_clear_object(&self->bus);
+    g_clear_object(&self->iface);
+    g_free(self->dbus_addr);
+}
+
+static bool
+dbus_display_add_console(DBusDisplay *self, int idx, Error **errp)
+{
+    QemuConsole *con;
+    DBusDisplayConsole *dbus_console;
+
+    con = qemu_console_lookup_by_index(idx);
+    assert(con);
+
+    if (qemu_console_is_graphic(con) &&
+        self->gl_mode != DISPLAYGL_MODE_OFF) {
+        qemu_console_set_display_gl_ctx(con, &self->glctx);
+    }
+
+    dbus_console = dbus_display_console_new(self, con);
+    g_ptr_array_insert(self->consoles, idx, dbus_console);
+    g_dbus_object_manager_server_export(self->server,
+                                        G_DBUS_OBJECT_SKELETON(dbus_console));
+    return true;
+}
+
+static void
+dbus_display_complete(UserCreatable *uc, Error **errp)
+{
+    DBusDisplay *self = DBUS_DISPLAY(uc);
+    g_autoptr(GError) err = NULL;
+    g_autofree char *uuid = qemu_uuid_unparse_strdup(&qemu_uuid);
+    g_autoptr(GArray) consoles = NULL;
+    GVariant *console_ids;
+    int idx;
+
+    if (!object_resolve_path_type("", TYPE_DBUS_DISPLAY, NULL)) {
+        error_setg(errp, "There is already an instance of %s",
+                   TYPE_DBUS_DISPLAY);
+        return;
+    }
+
+    if (self->dbus_addr && *self->dbus_addr) {
+        self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
+                        G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+                        G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
+                        NULL, NULL, &err);
+    } else {
+        self->bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &err);
+    }
+    if (err) {
+        error_setg(errp, "failed to connect to DBus: %s", err->message);
+        return;
+    }
+
+
+    consoles = g_array_new(FALSE, FALSE, sizeof(guint32));
+    for (idx = 0;; idx++) {
+        if (!qemu_console_lookup_by_index(idx)) {
+            break;
+        }
+        if (!dbus_display_add_console(self, idx, errp)) {
+            return;
+        }
+        g_array_append_val(consoles, idx);
+    }
+
+    console_ids = g_variant_new_from_data(G_VARIANT_TYPE("au"),
+                                          consoles->data, consoles->len * sizeof(guint32), TRUE,
+                                          (GDestroyNotify)g_array_unref, consoles);
+    g_steal_pointer(&consoles);
+    g_object_set(self->iface,
+                 "name", qemu_name ?: "QEMU " QEMU_VERSION,
+                 "uuid", uuid,
+                 "console-ids", console_ids,
+                 NULL);
+
+    g_dbus_object_manager_server_set_connection(self->server, self->bus);
+    g_bus_own_name_on_connection(self->bus, "org.qemu", G_BUS_NAME_OWNER_FLAGS_NONE,
+                                 NULL, NULL, NULL, NULL);
+}
+
+static char *
+get_dbus_addr(Object *o, Error **errp)
+{
+    DBusDisplay *self = DBUS_DISPLAY(o);
+
+    return g_strdup(self->dbus_addr);
+}
+
+static void
+set_dbus_addr(Object *o, const char *str, Error **errp)
+{
+    DBusDisplay *self = DBUS_DISPLAY(o);
+
+    g_free(self->dbus_addr);
+    self->dbus_addr = g_strdup(str);
+}
+
+static int
+get_gl_mode(Object *o, Error **errp)
+{
+    DBusDisplay *self = DBUS_DISPLAY(o);
+
+    return self->gl_mode;
+}
+
+static void
+set_gl_mode(Object *o, int val, Error **errp)
+{
+    DBusDisplay *self = DBUS_DISPLAY(o);
+
+    self->gl_mode = val;
+}
+
+static void
+dbus_display_class_init(ObjectClass *oc, void *data)
+{
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+    ucc->complete = dbus_display_complete;
+    object_class_property_add_str(oc, "addr", get_dbus_addr, set_dbus_addr);
+    object_class_property_add_enum(oc, "gl-mode",
+                                   "DisplayGLMode", &DisplayGLMode_lookup,
+                                   get_gl_mode, set_gl_mode);
+}
+
+static void
+early_dbus_init(DisplayOptions *opts)
+{
+    DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_OFF;
+
+    if (mode != DISPLAYGL_MODE_OFF) {
+        if (egl_rendernode_init(opts->u.dbus.rendernode, mode) < 0) {
+            error_report("dbus: render node init failed");
+            exit(1);
+        }
+
+        display_opengl = 1;
+    }
+}
+
+static void
+dbus_init(DisplayState *ds, DisplayOptions *opts)
+{
+    DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_OFF;
+
+    object_new_with_props(TYPE_DBUS_DISPLAY,
+                          object_get_objects_root(),
+                          "dbus-display", &error_fatal,
+                          "addr", opts->u.dbus.addr ?: "",
+                          "gl-mode", DisplayGLMode_str(mode),
+                          NULL);
+}
+
+static const TypeInfo dbus_display_info = {
+    .name = TYPE_DBUS_DISPLAY,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(DBusDisplay),
+    .instance_init = dbus_display_init,
+    .instance_finalize = dbus_display_finalize,
+    .class_init = dbus_display_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+static QemuDisplay qemu_display_dbus = {
+    .type       = DISPLAY_TYPE_DBUS,
+    .early_init = early_dbus_init,
+    .init       = dbus_init,
+};
+
+static void register_dbus(void)
+{
+    type_register_static(&dbus_display_info);
+    qemu_display_register(&qemu_display_dbus);
+}
+
+type_init(register_dbus);
diff --git a/util/module.c b/util/module.c
index c65060c167..f228067d0d 100644
--- a/util/module.c
+++ b/util/module.c
@@ -183,6 +183,7 @@ static const struct {
     { "ui-spice-app",   "chardev-spice" },
 
 #ifdef CONFIG_OPENGL
+    { "ui-dbus",         "ui-opengl"    },
     { "ui-egl-headless", "ui-opengl"    },
     { "ui-gtk",          "ui-opengl"    },
     { "ui-sdl",          "ui-opengl"    },
@@ -294,6 +295,7 @@ static struct {
     const char *prefix;
     const char *module;
 } const qom_modules[] = {
+    { "dbus-display",          "backends-", "dbus"            },
     { "ccid-card-passthru",    "hw-", "usb-smartcard"         },
     { "ccid-card-emulated",    "hw-", "usb-smartcard"         },
     { "usb-redir",             "hw-", "usb-redirect"          },
diff --git a/qemu-options.hx b/qemu-options.hx
index 90801286c6..9b42f15f53 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1765,6 +1765,10 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
 #endif
 #if defined(CONFIG_OPENGL)
     "-display egl-headless[,rendernode=<file>]\n"
+#endif
+#if defined(CONFIG_UI_DBUS)
+    "-display dbus[,addr=<dbusaddr>]\n"
+    "             [,gl=on|core|es|off][,rendernode=<file>]\n"
 #endif
     "-display none\n"
     "                select display backend type\n"
@@ -1827,6 +1831,10 @@ SRST
         Start QEMU as a Spice server and launch the default Spice client
         application. The Spice server will redirect the serial consoles
         and QEMU monitors. (Since 4.0)
+
+    ``dbus``
+        Start a D-Bus service for the display. (Since X.X)
+
 ERST
 
 DEF("nographic", 0, QEMU_OPTION_nographic,
diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml
new file mode 100644
index 0000000000..df2ffcea07
--- /dev/null
+++ b/ui/dbus-display1.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<node>
+  <!-- on /org/qemu/Display1/VM -->
+  <interface name="org.qemu.Display1.VM">
+    <property name="Name" type="s" access="read"/>
+    <property name="UUID" type="s" access="read"/>
+    <property name="ConsoleIDs" type="au" access="read"/>
+  </interface>
+
+  <!-- on /org/qemu/Display1/Console_N -->
+  <interface name="org.qemu.Display1.Console">
+    <method name="RegisterListener">
+      <arg type="h" name="listener" direction="in"/>
+    </method>
+    <method name="SetUIInfo">
+      <arg name="width_mm" type="q" direction="in"/>
+      <arg name="height_mm" type="q" direction="in"/>
+      <arg name="xoff" type="i" direction="in"/>
+      <arg name="yoff" type="i" direction="in"/>
+      <arg name="width" type="u" direction="in"/>
+      <arg name="height" type="u" direction="in"/>
+    </method>
+    <property name="Label" type="s" access="read"/>
+    <property name="Head" type="u" access="read"/>
+    <property name="Type" type="s" access="read"/>
+    <property name="Width" type="u" access="read"/>
+    <property name="Height" type="u" access="read"/>
+    <property name="DeviceAddress" type="s" access="read"/>
+  </interface>
+
+  <interface name="org.qemu.Display1.Keyboard">
+    <method name="Press">
+      <arg type="u" name="keycode" direction="in"/>
+    </method>
+    <method name="Release">
+      <arg type="u" name="keycode" direction="in"/>
+    </method>
+    <!--
+       scroll = 1 << 0
+       num    = 1 << 1
+       caps   = 1 << 2
+    -->
+    <property name="Modifiers" type="u" access="readwrite"/>
+  </interface>
+
+  <interface name="org.qemu.Display1.Mouse">
+    <!--
+       left = 0
+       middle = 1
+       right = 2
+       wheel-up = 3
+       wheel-down = 4
+       side = 5
+       extra = 6
+    -->
+    <method name="Press">
+      <arg type="u" name="button" direction="in"/>
+    </method>
+    <method name="Release">
+      <arg type="u" name="button" direction="in"/>
+    </method>
+    <method name="SetAbsPosition">
+      <arg type="u" name="x" direction="in"/>
+      <arg type="u" name="y" direction="in"/>
+    </method>
+  </interface>
+
+  <!-- on client /org/qemu/Display1/Listener -->
+  <interface name="org.qemu.Display1.Listener">
+    <method name="Scanout">
+      <arg type="u" name="width" direction="in"/>
+      <arg type="u" name="height" direction="in"/>
+      <arg type="u" name="stride" direction="in"/>
+      <arg type="u" name="pixman_format" direction="in"/>
+      <arg type="ay" name="data" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+    <method name="Update">
+      <arg type="i" name="x" direction="in"/>
+      <arg type="i" name="y" direction="in"/>
+      <arg type="i" name="width" direction="in"/>
+      <arg type="i" name="height" direction="in"/>
+      <arg type="u" name="stride" direction="in"/>
+      <arg type="u" name="pixman_format" direction="in"/>
+      <arg type="ay" name="data" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+    <method name="ScanoutDMABUF">
+      <arg type="h" name="dmabuf" direction="in"/>
+      <arg type="u" name="width" direction="in"/>
+      <arg type="u" name="height" direction="in"/>
+      <arg type="u" name="stride" direction="in"/>
+      <arg type="u" name="fourcc" direction="in"/>
+      <!-- xywh? -->
+      <arg type="t" name="modifier" direction="in"/>
+      <arg type="b" name="y0_top" direction="in"/>
+    </method>
+    <method name="UpdateDMABUF">
+      <arg type="i" name="x" direction="in"/>
+      <arg type="i" name="y" direction="in"/>
+      <arg type="i" name="width" direction="in"/>
+      <arg type="i" name="height" direction="in"/>
+    </method>
+    <method name="Disable">
+    </method>
+    <method name="MouseSet">
+      <arg type="i" name="x" direction="in"/>
+      <arg type="i" name="y" direction="in"/>
+      <arg type="i" name="on" direction="in"/>
+    </method>
+    <method name="CursorDefine">
+      <arg type="i" name="width" direction="in"/>
+      <arg type="i" name="height" direction="in"/>
+      <arg type="i" name="hot_x" direction="in"/>
+      <arg type="i" name="hot_y" direction="in"/>
+      <arg type="ay" name="data" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+  </interface>
+</node>
diff --git a/ui/meson.build b/ui/meson.build
index 97cdd58856..fe75c40f38 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -59,6 +59,23 @@ if config_host.has_key('CONFIG_OPENGL') and gbm.found()
   ui_modules += {'egl-headless' : egl_headless_ss}
 endif
 
+if 'CONFIG_UI_DBUS' in config_host
+  dbus_ss = ss.source_set()
+  dbus_display1 = custom_target('dbus-display gdbus-codegen',
+                                output: ['dbus-display1.h', 'dbus-display1.c'],
+                                input: files('dbus-display1.xml'),
+                                command: [config_host['GDBUS_CODEGEN'],
+                                          '@INPUT@',
+                                          '--glib-min-required', '2.64',
+                                          '--output-directory', meson.current_build_dir(),
+                                          '--interface-prefix', 'org.qemu.',
+                                          '--c-namespace', 'DBusDisplay',
+                                          '--generate-c-code', '@BASENAME@']).to_list()
+  dbus_ss.add(when: [gio, pixman, opengl, 'CONFIG_GIO'],
+              if_true: files('dbus.c', 'dbus-console.c', 'dbus-listener.c', 'dbus-error.c') + dbus_display1)
+  ui_modules += {'dbus' : dbus_ss}
+endif
+
 if gtk.found()
   softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c'))
 
diff --git a/ui/trace-events b/ui/trace-events
index 0ffcdb4408..a821f51dc3 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -108,3 +108,14 @@ xkeymap_extension(const char *name) "extension '%s'"
 xkeymap_vendor(const char *name) "vendor '%s'"
 xkeymap_keycodes(const char *name) "keycodes '%s'"
 xkeymap_keymap(const char *name) "keymap '%s'"
+
+# dbus.c
+dbus_registered_listener(const char *bus_name) "peer %s"
+dbus_listener_vanished(const char *bus_name) "peer %s"
+dbus_kbd_press(unsigned int keycode) "keycode %u"
+dbus_kbd_release(unsigned int keycode) "keycode %u"
+dbus_kbd_modifiers_changed(unsigned int modifiers) "modifiers %u"
+dbus_mouse_press(unsigned int button) "button %u"
+dbus_mouse_release(unsigned int button) "button %u"
+dbus_mouse_set_pos(unsigned int x, unsigned int y) "x=%u, y=%u"
+dbus_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d"
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 15/27] audio: add dbusaudio backend
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (13 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 14/27] ui: add a D-Bus display backend marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 16/27] vhost-user-gpu: add vg_send_disable_scanout() marcandre.lureau
                   ` (13 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Add a new -audio backend that accepts DBus clients/listeners to handle
playback & recording, to be exported via the -display dbus.

Example usage:
-audiodev dbus,in.mixing-engine=off,out.mixing-engine=off,id=dbus
-display dbus,audiodev=dbus

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi/audio.json        |   3 +-
 qapi/ui.json           |   7 +-
 audio/audio_int.h      |   7 +
 audio/audio_template.h |   2 +
 ui/dbus.h              |   1 +
 audio/audio.c          |   1 +
 audio/dbusaudio.c      | 649 +++++++++++++++++++++++++++++++++++++++++
 ui/dbus.c              |  35 +++
 util/module.c          |   1 +
 audio/meson.build      |   3 +-
 audio/trace-events     |   5 +
 qemu-options.hx        |   3 +
 ui/dbus-display1.xml   |  82 ++++++
 13 files changed, 795 insertions(+), 4 deletions(-)
 create mode 100644 audio/dbusaudio.c

diff --git a/qapi/audio.json b/qapi/audio.json
index 9cba0df8a4..693e327c6b 100644
--- a/qapi/audio.json
+++ b/qapi/audio.json
@@ -386,7 +386,7 @@
 # Since: 4.0
 ##
 { 'enum': 'AudiodevDriver',
-  'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'jack', 'oss', 'pa',
+  'data': [ 'none', 'alsa', 'coreaudio', 'dbus', 'dsound', 'jack', 'oss', 'pa',
             'sdl', 'spice', 'wav' ] }
 
 ##
@@ -412,6 +412,7 @@
     'none':      'AudiodevGenericOptions',
     'alsa':      'AudiodevAlsaOptions',
     'coreaudio': 'AudiodevCoreaudioOptions',
+    'dbus':      'AudiodevGenericOptions',
     'dsound':    'AudiodevDsoundOptions',
     'jack':      'AudiodevJackOptions',
     'oss':       'AudiodevOssOptions',
diff --git a/qapi/ui.json b/qapi/ui.json
index bdfab800c0..0344f75f69 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1078,12 +1078,15 @@
 # @rendernode: Which DRM render node should be used. Default is the first
 #              available node on the host.
 #
+# @audiodev: Use the specified DBus audiodev to export audio.
+#
 # Since: X.X
 #
 ##
 { 'struct'  : 'DisplayDBus',
-  'data'    : { '*rendernode' : 'str',
-                '*addr': 'str' } }
+  'data'    : { '*rendernode': 'str',
+                '*addr': 'str',
+                '*audiodev': 'str' } }
 
  ##
  # @DisplayGLMode:
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 06f0913835..294c7f2342 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -31,6 +31,10 @@
 #endif
 #include "mixeng.h"
 
+#ifdef CONFIG_GIO
+#include <gio/gio.h>
+#endif
+
 struct audio_pcm_ops;
 
 struct audio_callback {
@@ -140,6 +144,9 @@ struct audio_driver {
     const char *descr;
     void *(*init) (Audiodev *);
     void (*fini) (void *);
+#ifdef CONFIG_GIO
+    void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager);
+#endif
     struct audio_pcm_ops *pcm_ops;
     int can_be_default;
     int max_voices_out;
diff --git a/audio/audio_template.h b/audio/audio_template.h
index c6714946aa..d2d348638b 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -327,6 +327,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
     case AUDIODEV_DRIVER_COREAUDIO:
         return qapi_AudiodevCoreaudioPerDirectionOptions_base(
             dev->u.coreaudio.TYPE);
+    case AUDIODEV_DRIVER_DBUS:
+        return dev->u.dbus.TYPE;
     case AUDIODEV_DRIVER_DSOUND:
         return dev->u.dsound.TYPE;
     case AUDIODEV_DRIVER_JACK:
diff --git a/ui/dbus.h b/ui/dbus.h
index f554084a27..10a019564f 100644
--- a/ui/dbus.h
+++ b/ui/dbus.h
@@ -35,6 +35,7 @@ struct DBusDisplay {
 
     DisplayGLMode gl_mode;
     char *dbus_addr;
+    char *audiodev;
     DisplayGLCtx glctx;
 
     GDBusConnection *bus;
diff --git a/audio/audio.c b/audio/audio.c
index 6734c8af70..fbcf676e78 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1989,6 +1989,7 @@ void audio_create_pdos(Audiodev *dev)
         CASE(NONE, none, );
         CASE(ALSA, alsa, Alsa);
         CASE(COREAUDIO, coreaudio, Coreaudio);
+        CASE(DBUS, dbus, );
         CASE(DSOUND, dsound, );
         CASE(JACK, jack, Jack);
         CASE(OSS, oss, Oss);
diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c
new file mode 100644
index 0000000000..4f1e51d951
--- /dev/null
+++ b/audio/dbusaudio.c
@@ -0,0 +1,649 @@
+/*
+ * QEMU DBus audio
+ *
+ * Copyright (c) 2021 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/host-utils.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/dbus.h"
+
+#include <gio/gunixfdlist.h>
+#include "ui/dbus-display1.h"
+
+#define AUDIO_CAP "dbus"
+#include "audio.h"
+#include "audio_int.h"
+#include "trace.h"
+
+#define DBUS_DISPLAY1_AUDIO DBUS_DISPLAY1_ROOT "/Audio"
+
+#define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */
+
+typedef struct DBusAudio {
+    GDBusObjectManagerServer *server;
+    GDBusObjectSkeleton *audio;
+    DBusDisplayDisplay1Audio *iface;
+    GHashTable *out_listeners;
+    GHashTable *in_listeners;
+} DBusAudio;
+
+typedef struct DBusVoiceOut {
+    HWVoiceOut hw;
+    bool enabled;
+    RateCtl rate;
+
+    void *buf;
+    size_t buf_pos;
+    size_t buf_size;
+
+    bool has_volume;
+    Volume volume;
+} DBusVoiceOut;
+
+typedef struct DBusVoiceIn {
+    HWVoiceIn hw;
+    bool enabled;
+    RateCtl rate;
+
+    bool has_volume;
+    Volume volume;
+} DBusVoiceIn;
+
+static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
+{
+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
+
+    if (!vo->buf) {
+        vo->buf_size = hw->samples * hw->info.bytes_per_frame;
+        vo->buf = g_malloc(vo->buf_size);
+        vo->buf_pos = 0;
+    }
+
+    *size = MIN(vo->buf_size - vo->buf_pos, *size);
+    *size = audio_rate_get_bytes(&hw->info, &vo->rate, *size);
+
+    return vo->buf + vo->buf_pos;
+
+}
+
+static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioOutListener *listener = NULL;
+    g_autoptr(GBytes) bytes = NULL;
+    g_autoptr(GVariant) v_data = NULL;
+
+    assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);
+    vo->buf_pos += size;
+
+    trace_dbus_put_buffer_out(size);
+
+    if (vo->buf_pos < vo->buf_size) {
+        return size;
+    }
+
+    bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);
+    v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
+    g_variant_ref_sink(v_data);
+
+    g_hash_table_iter_init(&iter, da->out_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_display_display1_audio_out_listener_call_write(
+            listener,
+            (uintptr_t)hw,
+            v_data,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
+
+    return size;
+}
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define AUDIO_HOST_BE TRUE
+#else
+#define AUDIO_HOST_BE FALSE
+#endif
+
+static void
+dbus_init_out_listener(DBusDisplayDisplay1AudioOutListener *listener, HWVoiceOut *hw)
+{
+    dbus_display_display1_audio_out_listener_call_init(
+        listener,
+        (uintptr_t)hw,
+        hw->info.bits,
+        hw->info.is_signed,
+        hw->info.is_float,
+        hw->info.freq,
+        hw->info.nchannels,
+        hw->info.bytes_per_frame,
+        hw->info.bytes_per_second,
+        hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static int
+dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioOutListener *listener = NULL;
+
+    audio_pcm_init_info(&hw->info, as);
+    hw->samples = DBUS_AUDIO_NSAMPLES;
+    audio_rate_start(&vo->rate);
+
+    g_hash_table_iter_init(&iter, da->out_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_init_out_listener(listener, hw);
+    }
+    return 0;
+}
+
+static void
+dbus_fini_out(HWVoiceOut *hw)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioOutListener *listener = NULL;
+
+    g_hash_table_iter_init(&iter, da->out_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_display_display1_audio_out_listener_call_fini(
+            listener,
+            (uintptr_t)hw,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
+
+    g_clear_pointer(&vo->buf, g_free);
+}
+
+static void
+dbus_enable_out(HWVoiceOut *hw, bool enable)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioOutListener *listener = NULL;
+
+    vo->enabled = enable;
+    if (enable) {
+        audio_rate_start(&vo->rate);
+    }
+
+    g_hash_table_iter_init(&iter, da->out_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_display_display1_audio_out_listener_call_set_enabled(
+            listener, (uintptr_t)hw, enable,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
+}
+
+static void
+dbus_volume_out_listener(HWVoiceOut *hw,
+                         DBusDisplayDisplay1AudioOutListener *listener)
+{
+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
+    Volume *vol = &vo->volume;
+    g_autoptr(GBytes) bytes = NULL;
+    GVariant *v_vol = NULL;
+
+    if (!vo->has_volume) {
+        return;
+    }
+
+    assert(vol->channels < sizeof(vol->vol));
+    bytes = g_bytes_new(vol->vol, vol->channels);
+    v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
+    dbus_display_display1_audio_out_listener_call_set_volume(
+        listener, (uintptr_t)hw, vol->mute, v_vol,
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void
+dbus_volume_out(HWVoiceOut *hw, Volume *vol)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioOutListener *listener = NULL;
+
+    vo->has_volume = true;
+    vo->volume = *vol;
+
+    g_hash_table_iter_init(&iter, da->out_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_volume_out_listener(hw, listener);
+    }
+}
+
+static void
+dbus_init_in_listener(DBusDisplayDisplay1AudioInListener *listener, HWVoiceIn *hw)
+{
+    dbus_display_display1_audio_in_listener_call_init(
+        listener,
+        (uintptr_t)hw,
+        hw->info.bits,
+        hw->info.is_signed,
+        hw->info.is_float,
+        hw->info.freq,
+        hw->info.nchannels,
+        hw->info.bytes_per_frame,
+        hw->info.bytes_per_second,
+        hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static int
+dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioInListener *listener = NULL;
+
+    audio_pcm_init_info(&hw->info, as);
+    hw->samples = DBUS_AUDIO_NSAMPLES;
+    audio_rate_start(&vo->rate);
+
+    g_hash_table_iter_init(&iter, da->in_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_init_in_listener(listener, hw);
+    }
+    return 0;
+}
+
+static void
+dbus_fini_in(HWVoiceIn *hw)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioInListener *listener = NULL;
+
+    g_hash_table_iter_init(&iter, da->in_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_display_display1_audio_in_listener_call_fini(
+            listener,
+            (uintptr_t)hw,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
+}
+
+static void
+dbus_volume_in_listener(HWVoiceIn *hw,
+                         DBusDisplayDisplay1AudioInListener *listener)
+{
+    DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
+    Volume *vol = &vo->volume;
+    g_autoptr(GBytes) bytes = NULL;
+    GVariant *v_vol = NULL;
+
+    if (!vo->has_volume) {
+        return;
+    }
+
+    assert(vol->channels < sizeof(vol->vol));
+    bytes = g_bytes_new(vol->vol, vol->channels);
+    v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
+    dbus_display_display1_audio_in_listener_call_set_volume(
+        listener, (uintptr_t)hw, vol->mute, v_vol,
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void
+dbus_volume_in(HWVoiceIn *hw, Volume *vol)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioInListener *listener = NULL;
+
+    vo->has_volume = true;
+    vo->volume = *vol;
+
+    g_hash_table_iter_init(&iter, da->in_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_volume_in_listener(hw, listener);
+    }
+}
+
+static size_t
+dbus_read(HWVoiceIn *hw, void *buf, size_t size)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioInListener *listener = NULL;
+
+    trace_dbus_read(size);
+
+    /* size = audio_rate_get_bytes(&hw->info, &vo->rate, size); */
+
+    g_hash_table_iter_init(&iter, da->in_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        g_autoptr(GVariant) v_data = NULL;
+        const char *data;
+        gsize n = 0;
+
+        if (dbus_display_display1_audio_in_listener_call_read_sync(
+                listener,
+                (uintptr_t)hw,
+                size,
+                G_DBUS_CALL_FLAGS_NONE, -1,
+                &v_data, NULL, NULL)) {
+            data = g_variant_get_fixed_array(v_data, &n, 1);
+            g_warn_if_fail(n <= size);
+            size = MIN(n, size);
+            memcpy(buf, data, size);
+            break;
+        }
+    }
+
+    return size;
+}
+
+static void
+dbus_enable_in(HWVoiceIn *hw, bool enable)
+{
+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
+    DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
+    GHashTableIter iter;
+    DBusDisplayDisplay1AudioInListener *listener = NULL;
+
+    vo->enabled = enable;
+    if (enable) {
+        audio_rate_start(&vo->rate);
+    }
+
+    g_hash_table_iter_init(&iter, da->in_listeners);
+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
+        dbus_display_display1_audio_in_listener_call_set_enabled(
+            listener, (uintptr_t)hw, enable,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
+}
+
+static void *
+dbus_audio_init(Audiodev *dev)
+{
+    DBusAudio *self = g_new0(DBusAudio, 1);
+
+    self->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                g_free, g_object_unref);
+    self->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, g_object_unref);
+    return self;
+}
+
+static void
+dbus_audio_fini(void *opaque)
+{
+    DBusAudio *self = opaque;
+
+    if (self->server) {
+        g_dbus_object_manager_server_unexport(self->server, DBUS_DISPLAY1_AUDIO);
+    }
+    g_clear_object(&self->audio);
+    g_clear_object(&self->iface);
+    g_clear_pointer(&self->in_listeners, g_hash_table_unref);
+    g_clear_pointer(&self->out_listeners, g_hash_table_unref);
+    g_clear_object(&self->server);
+    g_free(self);
+}
+
+static void
+listener_out_vanished_cb(GDBusConnection *connection,
+                         gboolean remote_peer_vanished,
+                         GError *error,
+                         DBusAudio *self)
+{
+    char *name = g_object_get_data(G_OBJECT(connection), "name");
+
+    g_hash_table_remove(self->out_listeners, name);
+}
+
+static void
+listener_in_vanished_cb(GDBusConnection *connection,
+                        gboolean remote_peer_vanished,
+                        GError *error,
+                        DBusAudio *self)
+{
+    char *name = g_object_get_data(G_OBJECT(connection), "name");
+
+    g_hash_table_remove(self->in_listeners, name);
+}
+
+static gboolean
+dbus_audio_register_listener(AudioState *s,
+                             GDBusMethodInvocation *invocation,
+                             GUnixFDList *fd_list,
+                             GVariant *arg_listener,
+                             bool out)
+{
+    DBusAudio *self = s->drv_opaque;
+    const char *sender = g_dbus_method_invocation_get_sender(invocation);
+    g_autoptr(GDBusConnection) listener_conn = NULL;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GSocket) socket = NULL;
+    g_autoptr(GSocketConnection) socket_conn = NULL;
+    g_autofree char *guid = g_dbus_generate_guid();
+    GHashTable *listeners = out ? self->out_listeners : self->in_listeners;
+    GObject *listener;
+    int fd;
+
+    trace_dbus_register(sender, out ? "out" : "in");
+
+    if (g_hash_table_contains(listeners, sender)) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_INVALID,
+                                              "`%s` is already registered!",
+                                              sender);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
+    if (err) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_FAILED,
+                                              "Couldn't get peer fd: %s",
+                                              err->message);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    socket = g_socket_new_from_fd(fd, &err);
+    if (err) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_FAILED,
+                                              "Couldn't make a socket: %s",
+                                              err->message);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+    socket_conn = g_socket_connection_factory_create_connection(socket);
+    if (out) {
+        dbus_display_display1_audio_complete_register_out_listener(
+            self->iface, invocation, NULL);
+    } else {
+        dbus_display_display1_audio_complete_register_in_listener(
+            self->iface, invocation, NULL);
+    }
+
+    listener_conn =
+        g_dbus_connection_new_sync(
+            G_IO_STREAM(socket_conn),
+            guid,
+            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
+            NULL, NULL, &err);
+    if (err) {
+        error_report("Failed to setup peer connection: %s", err->message);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    listener = out ?
+        G_OBJECT(dbus_display_display1_audio_out_listener_proxy_new_sync(
+            listener_conn,
+            G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+            NULL,
+            "/org/qemu/Display1/AudioOutListener",
+            NULL,
+            &err)) :
+        G_OBJECT(dbus_display_display1_audio_in_listener_proxy_new_sync(
+            listener_conn,
+            G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+            NULL,
+            "/org/qemu/Display1/AudioInListener",
+            NULL,
+            &err));
+    if (!listener) {
+        error_report("Failed to setup proxy: %s", err->message);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    if (out) {
+        HWVoiceOut *hw;
+
+        QLIST_FOREACH(hw, &s->hw_head_out, entries) {
+            DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
+            DBusDisplayDisplay1AudioOutListener *l =
+                DBUS_DISPLAY_DISPLAY1_AUDIO_OUT_LISTENER(listener);
+
+            dbus_init_out_listener(l, hw);
+            dbus_display_display1_audio_out_listener_call_set_enabled(
+                l, (uintptr_t)hw, vo->enabled,
+                G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        }
+    } else {
+        HWVoiceIn *hw;
+
+        QLIST_FOREACH(hw, &s->hw_head_in, entries) {
+            DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
+            DBusDisplayDisplay1AudioInListener *l =
+                DBUS_DISPLAY_DISPLAY1_AUDIO_IN_LISTENER(listener);
+
+            dbus_init_in_listener(DBUS_DISPLAY_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);
+            dbus_display_display1_audio_in_listener_call_set_enabled(
+                l, (uintptr_t)hw, vo->enabled,
+                G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        }
+    }
+
+    g_object_set_data_full(G_OBJECT(listener_conn), "name",
+                           g_strdup(sender), g_free);
+    g_hash_table_insert(listeners, g_strdup(sender), listener);
+    g_object_connect(listener_conn,
+                     "signal::closed",
+                     out ? listener_out_vanished_cb : listener_in_vanished_cb,
+                     self,
+                     NULL);
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+dbus_audio_register_out_listener(AudioState *s,
+                                 GDBusMethodInvocation *invocation,
+                                 GUnixFDList *fd_list,
+                                 GVariant *arg_listener)
+{
+    return dbus_audio_register_listener(s, invocation,
+                                        fd_list, arg_listener, true);
+
+}
+
+static gboolean
+dbus_audio_register_in_listener(AudioState *s,
+                                GDBusMethodInvocation *invocation,
+                                GUnixFDList *fd_list,
+                                GVariant *arg_listener)
+{
+    return dbus_audio_register_listener(s, invocation,
+                                        fd_list, arg_listener, false);
+}
+
+static void
+dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server)
+{
+    DBusAudio *self = s->drv_opaque;
+
+    g_assert(self);
+    g_assert(!self->server);
+
+    self->server = g_object_ref(server);
+
+    self->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO);
+    self->iface = g_object_new(DBUS_DISPLAY_TYPE_DISPLAY1_AUDIO_SKELETON, NULL);
+    g_object_connect(self->iface,
+                     "swapped-signal::handle-register-in-listener",
+                     dbus_audio_register_in_listener, s,
+                     "swapped-signal::handle-register-out-listener",
+                     dbus_audio_register_out_listener, s,
+                     NULL);
+
+    g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(self->audio),
+                                         G_DBUS_INTERFACE_SKELETON(self->iface));
+    g_dbus_object_manager_server_export(self->server, self->audio);
+}
+
+static struct audio_pcm_ops dbus_pcm_ops = {
+    .init_out = dbus_init_out,
+    .fini_out = dbus_fini_out,
+    .write    = audio_generic_write,
+    .get_buffer_out = dbus_get_buffer_out,
+    .put_buffer_out = dbus_put_buffer_out,
+    .enable_out = dbus_enable_out,
+    .volume_out = dbus_volume_out,
+
+    .init_in  = dbus_init_in,
+    .fini_in  = dbus_fini_in,
+    .read     = dbus_read,
+    .run_buffer_in = audio_generic_run_buffer_in,
+    .enable_in = dbus_enable_in,
+    .volume_in = dbus_volume_in,
+};
+
+static struct audio_driver dbus_audio_driver = {
+    .name            = "dbus",
+    .descr           = "Timer based audio exposed with DBus interface",
+    .init            = dbus_audio_init,
+    .fini            = dbus_audio_fini,
+    .set_dbus_server = dbus_audio_set_server,
+    .pcm_ops         = &dbus_pcm_ops,
+    .can_be_default  = 1,
+    .max_voices_out  = INT_MAX,
+    .max_voices_in   = INT_MAX,
+    .voice_size_out  = sizeof(DBusVoiceOut),
+    .voice_size_in   = sizeof(DBusVoiceIn)
+};
+
+static void register_audio_dbus(void)
+{
+    audio_driver_register(&dbus_audio_driver);
+}
+type_init(register_audio_dbus);
diff --git a/ui/dbus.c b/ui/dbus.c
index 089a92cedf..cc0d0665f0 100644
--- a/ui/dbus.c
+++ b/ui/dbus.c
@@ -28,6 +28,8 @@
 #include "sysemu/sysemu.h"
 #include "ui/egl-helpers.h"
 #include "ui/egl-context.h"
+#include "audio/audio.h"
+#include "audio/audio_int.h"
 #include "qapi/error.h"
 #include "trace.h"
 
@@ -75,6 +77,7 @@ dbus_display_finalize(Object *o)
     g_clear_object(&self->bus);
     g_clear_object(&self->iface);
     g_free(self->dbus_addr);
+    g_free(self->audiodev);
 }
 
 static bool
@@ -127,6 +130,18 @@ dbus_display_complete(UserCreatable *uc, Error **errp)
         return;
     }
 
+    if (self->audiodev && *self->audiodev) {
+        AudioState *audio_state = audio_state_by_name(self->audiodev);
+        if (!audio_state) {
+            error_setg(errp, "Audiodev '%s' not found", self->audiodev);
+            return;
+        }
+        if (!g_str_equal(audio_state->drv->name, "dbus")) {
+            error_setg(errp, "Audiodev '%s' is not compatible with DBus", self->audiodev);
+            return;
+        }
+        audio_state->drv->set_dbus_server(audio_state, self->server);
+    }
 
     consoles = g_array_new(FALSE, FALSE, sizeof(guint32));
     for (idx = 0;; idx++) {
@@ -171,6 +186,24 @@ set_dbus_addr(Object *o, const char *str, Error **errp)
     self->dbus_addr = g_strdup(str);
 }
 
+static char *
+get_audiodev(Object *o, Error **errp)
+{
+    DBusDisplay *self = DBUS_DISPLAY(o);
+
+    return g_strdup(self->audiodev);
+}
+
+static void
+set_audiodev(Object *o, const char *str, Error **errp)
+{
+    DBusDisplay *self = DBUS_DISPLAY(o);
+
+    g_free(self->audiodev);
+    self->audiodev = g_strdup(str);
+}
+
+
 static int
 get_gl_mode(Object *o, Error **errp)
 {
@@ -194,6 +227,7 @@ dbus_display_class_init(ObjectClass *oc, void *data)
 
     ucc->complete = dbus_display_complete;
     object_class_property_add_str(oc, "addr", get_dbus_addr, set_dbus_addr);
+    object_class_property_add_str(oc, "audiodev", get_audiodev, set_audiodev);
     object_class_property_add_enum(oc, "gl-mode",
                                    "DisplayGLMode", &DisplayGLMode_lookup,
                                    get_gl_mode, set_gl_mode);
@@ -223,6 +257,7 @@ dbus_init(DisplayState *ds, DisplayOptions *opts)
                           object_get_objects_root(),
                           "dbus-display", &error_fatal,
                           "addr", opts->u.dbus.addr ?: "",
+                          "audiodev", opts->u.dbus.audiodev ?: "",
                           "gl-mode", DisplayGLMode_str(mode),
                           NULL);
 }
diff --git a/util/module.c b/util/module.c
index f228067d0d..858f13e86f 100644
--- a/util/module.c
+++ b/util/module.c
@@ -176,6 +176,7 @@ static const struct {
     const char *name;
     const char *dep;
 } module_deps[] = {
+    { "audio-dbus",     "ui-dbus" },
     { "audio-spice",    "ui-spice-core" },
     { "chardev-spice",  "ui-spice-core" },
     { "hw-display-qxl", "ui-spice-core" },
diff --git a/audio/meson.build b/audio/meson.build
index 7d53b0f920..5eca175779 100644
--- a/audio/meson.build
+++ b/audio/meson.build
@@ -18,7 +18,8 @@ foreach m : [
   ['CONFIG_AUDIO_PA', 'pa', pulse, 'paaudio.c'],
   ['CONFIG_AUDIO_SDL', 'sdl', sdl, 'sdlaudio.c'],
   ['CONFIG_AUDIO_JACK', 'jack', jack, 'jackaudio.c'],
-  ['CONFIG_SPICE', 'spice', spice, 'spiceaudio.c']
+  ['CONFIG_SPICE', 'spice', spice, 'spiceaudio.c'],
+  ['CONFIG_UI_DBUS', 'dbus', gio, 'dbusaudio.c'],
 ]
   if config_host.has_key(m[0])
     module_ss = ss.source_set()
diff --git a/audio/trace-events b/audio/trace-events
index 6aec535763..02d6d70ed3 100644
--- a/audio/trace-events
+++ b/audio/trace-events
@@ -13,6 +13,11 @@ alsa_resume_out(void) "Resuming suspended output stream"
 # ossaudio.c
 oss_version(int version) "OSS version = 0x%x"
 
+# dbusaudio.c
+dbus_register(const char *s, const char *dir) "sender = %s, dir = %s"
+dbus_put_buffer_out(size_t len) "len = %zu"
+dbus_read(size_t len) "len = %zu"
+
 # audio.c
 audio_timer_start(int interval) "interval %d ms"
 audio_timer_stop(void) ""
diff --git a/qemu-options.hx b/qemu-options.hx
index 9b42f15f53..826dfc503d 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -616,6 +616,9 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
 #endif
 #ifdef CONFIG_SPICE
     "-audiodev spice,id=id[,prop[=value][,...]]\n"
+#endif
+#ifdef CONFIG_UI_DBUS
+    "-audiodev dbus,id=id[,prop[=value][,...]]\n"
 #endif
     "-audiodev wav,id=id[,prop[=value][,...]]\n"
     "                path= path of wav file to record\n",
diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml
index df2ffcea07..61afbed727 100644
--- a/ui/dbus-display1.xml
+++ b/ui/dbus-display1.xml
@@ -120,4 +120,86 @@
       </arg>
     </method>
   </interface>
+
+  <!-- on /org/qemu/Display1/Audio -->
+  <interface name="org.qemu.Display1.Audio">
+    <method name="RegisterOutListener">
+      <arg type="h" name="listener" direction="in"/>
+    </method>
+    <method name="RegisterInListener">
+      <arg type="h" name="listener" direction="in"/>
+    </method>
+  </interface>
+
+  <!-- on client /org/qemu/Display1/AudioOutListener -->
+  <interface name="org.qemu.Display1.AudioOutListener">
+    <method name="Init">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="bits" type="y" direction="in"/>
+      <arg name="is_signed" type="b" direction="in"/>
+      <arg name="is_float" type="b" direction="in"/>
+      <arg name="freq" type="u" direction="in"/>
+      <arg name="nchannels" type="y" direction="in"/>
+      <arg name="bytes_per_frame" type="u" direction="in"/>
+      <arg name="bytes_per_second" type="u" direction="in"/>
+      <arg name="be" type="b" direction="in"/>
+    </method>
+    <method name="Fini">
+      <arg name="id" type="t" direction="in"/>
+    </method>
+    <method name="SetEnabled">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="enabled" type="b" direction="in"/>
+    </method>
+    <method name="SetVolume">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="mute" type="b" direction="in"/>
+      <arg name="volume" type="ay" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+    <method name="Write">
+      <arg name="id" type="t" direction="in"/>
+      <arg type="ay" name="data" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+  </interface>
+
+  <!-- on client /org/qemu/Display1/AudioInListener -->
+  <interface name="org.qemu.Display1.AudioInListener">
+    <method name="Init">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="bits" type="y" direction="in"/>
+      <arg name="is_signed" type="b" direction="in"/>
+      <arg name="is_float" type="b" direction="in"/>
+      <arg name="freq" type="u" direction="in"/>
+      <arg name="nchannels" type="y" direction="in"/>
+      <arg name="bytes_per_frame" type="u" direction="in"/>
+      <arg name="bytes_per_second" type="u" direction="in"/>
+      <arg name="be" type="b" direction="in"/>
+    </method>
+    <method name="Fini">
+      <arg name="id" type="t" direction="in"/>
+    </method>
+    <method name="SetEnabled">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="enabled" type="b" direction="in"/>
+    </method>
+    <method name="SetVolume">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="mute" type="b" direction="in"/>
+      <arg name="volume" type="ay" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+    <method name="Read">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="size" type="t" direction="in"/>
+      <arg type="ay" name="data" direction="out">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+  </interface>
+
 </node>
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 16/27] vhost-user-gpu: add vg_send_disable_scanout()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (14 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 15/27] audio: add dbusaudio backend marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 17/27] vhost-user-gpu: add vg_send_scanout_dmabuf() marcandre.lureau
                   ` (12 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugpu.h          |  1 +
 contrib/vhost-user-gpu/vhost-user-gpu.c | 24 ++++++++++++++++--------
 contrib/vhost-user-gpu/virgl.c          |  8 +-------
 3 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index 04d5615812..e19abb670d 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -179,5 +179,6 @@ void    vg_send_msg(VuGpu *g, const VhostUserGpuMsg *msg, int fd);
 bool    vg_recv_msg(VuGpu *g, uint32_t expect_req, uint32_t expect_size,
                     gpointer payload);
 
+void    vg_send_disable_scanout(VuGpu *g, int scanout_id);
 
 #endif
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index f73f292c9f..69fedd376b 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -356,6 +356,21 @@ vg_resource_create_2d(VuGpu *g,
     QTAILQ_INSERT_HEAD(&g->reslist, res, next);
 }
 
+void
+vg_send_disable_scanout(VuGpu *g, int scanout_id)
+{
+    g_debug("send disable scanout %d", scanout_id);
+
+    if (g->sock_fd >= 0) {
+        VhostUserGpuMsg msg = {
+            .request = VHOST_USER_GPU_SCANOUT,
+            .size = sizeof(VhostUserGpuScanout),
+            .payload.scanout.scanout_id = scanout_id,
+        };
+        vg_send_msg(g, &msg, -1);
+    }
+}
+
 static void
 vg_disable_scanout(VuGpu *g, int scanout_id)
 {
@@ -374,14 +389,7 @@ vg_disable_scanout(VuGpu *g, int scanout_id)
     scanout->width = 0;
     scanout->height = 0;
 
-    if (g->sock_fd >= 0) {
-        VhostUserGpuMsg msg = {
-            .request = VHOST_USER_GPU_SCANOUT,
-            .size = sizeof(VhostUserGpuScanout),
-            .payload.scanout.scanout_id = scanout_id,
-        };
-        vg_send_msg(g, &msg, -1);
-    }
+    vg_send_disable_scanout(g, scanout_id);
 }
 
 static void
diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c
index 9e6660c7ab..fe153425f5 100644
--- a/contrib/vhost-user-gpu/virgl.c
+++ b/contrib/vhost-user-gpu/virgl.c
@@ -354,13 +354,7 @@ virgl_cmd_set_scanout(VuGpu *g,
         vg_send_msg(g, &msg, fd);
         close(fd);
     } else {
-        VhostUserGpuMsg msg = {
-            .request = VHOST_USER_GPU_DMABUF_SCANOUT,
-            .size = sizeof(VhostUserGpuDMABUFScanout),
-            .payload.dmabuf_scanout.scanout_id = ss.scanout_id,
-        };
-        g_debug("disable scanout");
-        vg_send_msg(g, &msg, -1);
+        vg_send_disable_scanout(g, ss.scanout_id);
     }
     g->scanout[ss.scanout_id].resource_id = ss.resource_id;
 }
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 17/27] vhost-user-gpu: add vg_send_scanout_dmabuf()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (15 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 16/27] vhost-user-gpu: add vg_send_disable_scanout() marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:00 ` [PATCH 18/27] vhost-user-gpu: add vg_send_dmabuf_update() marcandre.lureau
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugpu.h          |  8 ++++
 contrib/vhost-user-gpu/vhost-user-gpu.c | 57 +++++++++++++++++--------
 contrib/vhost-user-gpu/virgl.c          | 23 ++++------
 3 files changed, 56 insertions(+), 32 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index e19abb670d..759de56502 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -180,5 +180,13 @@ bool    vg_recv_msg(VuGpu *g, uint32_t expect_req, uint32_t expect_size,
                     gpointer payload);
 
 void    vg_send_disable_scanout(VuGpu *g, int scanout_id);
+void    vg_send_dmabuf_scanout(VuGpu *g,
+                               const struct virtio_gpu_set_scanout *ss,
+                               uint32_t fd_width,
+                               uint32_t fd_height,
+                               uint32_t fd_stride,
+                               int fd_drm_fourcc,
+                               uint32_t fd_flags,
+                               int fd);
 
 #endif
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index 69fedd376b..8c3ee8aad8 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -587,6 +587,38 @@ vg_transfer_to_host_2d(VuGpu *g,
     }
 }
 
+void
+vg_send_dmabuf_scanout(VuGpu *g,
+                       const struct virtio_gpu_set_scanout *ss,
+                       uint32_t fd_width,
+                       uint32_t fd_height,
+                       uint32_t fd_stride,
+                       int fd_drm_fourcc,
+                       uint32_t fd_flags,
+                       int fd)
+{
+    VhostUserGpuMsg msg = {
+        .request = VHOST_USER_GPU_DMABUF_SCANOUT,
+        .size = sizeof(VhostUserGpuDMABUFScanout),
+        .payload.dmabuf_scanout = (VhostUserGpuDMABUFScanout) {
+            .scanout_id = ss->scanout_id,
+            .x = ss->r.x,
+            .y = ss->r.y,
+            .width = ss->r.width,
+            .height = ss->r.height,
+            .fd_width = fd_width,
+            .fd_height = fd_height,
+            .fd_stride = fd_stride,
+            .fd_drm_fourcc = fd_drm_fourcc,
+            .fd_flags = fd_flags,
+        }
+    };
+
+    g_debug("send dmabuf scanout: %d", ss->scanout_id);
+    vg_send_msg(g, &msg, fd);
+}
+
+
 static void
 vg_set_scanout(VuGpu *g,
                struct virtio_gpu_ctrl_command *cmd)
@@ -651,24 +683,15 @@ vg_set_scanout(VuGpu *g,
     struct vugbm_buffer *buffer = &res->buffer;
 
     if (vugbm_buffer_can_get_dmabuf_fd(buffer)) {
-        VhostUserGpuMsg msg = {
-            .request = VHOST_USER_GPU_DMABUF_SCANOUT,
-            .size = sizeof(VhostUserGpuDMABUFScanout),
-            .payload.dmabuf_scanout = (VhostUserGpuDMABUFScanout) {
-                .scanout_id = ss.scanout_id,
-                .x = ss.r.x,
-                .y = ss.r.y,
-                .width = ss.r.width,
-                .height = ss.r.height,
-                .fd_width = buffer->width,
-                .fd_height = buffer->height,
-                .fd_stride = buffer->stride,
-                .fd_drm_fourcc = buffer->format
-            }
-        };
-
         if (vugbm_buffer_get_dmabuf_fd(buffer, &fd)) {
-            vg_send_msg(g, &msg, fd);
+            vg_send_dmabuf_scanout(g,
+                                   &ss,
+                                   buffer->width,
+                                   buffer->height,
+                                   buffer->stride,
+                                   buffer->format,
+                                   0,
+                                   fd);
             close(fd);
         }
     } else {
diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c
index fe153425f5..0c7f118556 100644
--- a/contrib/vhost-user-gpu/virgl.c
+++ b/contrib/vhost-user-gpu/virgl.c
@@ -337,21 +337,14 @@ virgl_cmd_set_scanout(VuGpu *g,
             return;
         }
         assert(fd >= 0);
-        VhostUserGpuMsg msg = {
-            .request = VHOST_USER_GPU_DMABUF_SCANOUT,
-            .size = sizeof(VhostUserGpuDMABUFScanout),
-            .payload.dmabuf_scanout.scanout_id = ss.scanout_id,
-            .payload.dmabuf_scanout.x =  ss.r.x,
-            .payload.dmabuf_scanout.y =  ss.r.y,
-            .payload.dmabuf_scanout.width = ss.r.width,
-            .payload.dmabuf_scanout.height = ss.r.height,
-            .payload.dmabuf_scanout.fd_width = info.width,
-            .payload.dmabuf_scanout.fd_height = info.height,
-            .payload.dmabuf_scanout.fd_stride = info.stride,
-            .payload.dmabuf_scanout.fd_flags = info.flags,
-            .payload.dmabuf_scanout.fd_drm_fourcc = info.drm_fourcc
-        };
-        vg_send_msg(g, &msg, fd);
+        vg_send_dmabuf_scanout(g,
+                               &ss,
+                               info.width,
+                               info.height,
+                               info.stride,
+                               info.drm_fourcc,
+                               info.flags,
+                               fd);
         close(fd);
     } else {
         vg_send_disable_scanout(g, ss.scanout_id);
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 18/27] vhost-user-gpu: add vg_send_dmabuf_update()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (16 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 17/27] vhost-user-gpu: add vg_send_scanout_dmabuf() marcandre.lureau
@ 2021-03-12 10:00 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 19/27] vhost-user-gpu: add vg_send_scanout() marcandre.lureau
                   ` (10 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugpu.h          |  6 +++++
 contrib/vhost-user-gpu/vhost-user-gpu.c | 32 +++++++++++++++----------
 contrib/vhost-user-gpu/virgl.c          | 11 +--------
 3 files changed, 27 insertions(+), 22 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index 759de56502..f2e80d255d 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -188,5 +188,11 @@ void    vg_send_dmabuf_scanout(VuGpu *g,
                                int fd_drm_fourcc,
                                uint32_t fd_flags,
                                int fd);
+void    vg_send_dmabuf_update(VuGpu *g,
+                              uint32_t scanout_id,
+                              uint32_t x,
+                              uint32_t y,
+                              uint32_t width,
+                              uint32_t height);
 
 #endif
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index 8c3ee8aad8..fc8e62f20e 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -618,6 +618,25 @@ vg_send_dmabuf_scanout(VuGpu *g,
     vg_send_msg(g, &msg, fd);
 }
 
+void
+vg_send_dmabuf_update(VuGpu *g,
+                      uint32_t scanout_id,
+                      uint32_t x,
+                      uint32_t y,
+                      uint32_t width,
+                      uint32_t height)
+{
+    VhostUserGpuMsg msg = {
+        .request = VHOST_USER_GPU_DMABUF_UPDATE,
+        .size = sizeof(VhostUserGpuUpdate),
+        .payload.update.scanout_id = scanout_id,
+        .payload.update.x = x,
+        .payload.update.y = y,
+        .payload.update.width = width,
+        .payload.update.height = height
+    };
+    vg_send_msg(g, &msg, -1);
+}
 
 static void
 vg_set_scanout(VuGpu *g,
@@ -765,18 +784,7 @@ vg_resource_flush(VuGpu *g,
         size_t height = extents->y2 - extents->y1;
 
         if (vugbm_buffer_can_get_dmabuf_fd(&res->buffer)) {
-            VhostUserGpuMsg vmsg = {
-                .request = VHOST_USER_GPU_DMABUF_UPDATE,
-                .size = sizeof(VhostUserGpuUpdate),
-                .payload.update = (VhostUserGpuUpdate) {
-                    .scanout_id = i,
-                    .x = extents->x1,
-                    .y = extents->y1,
-                    .width = width,
-                    .height = height,
-                }
-            };
-            vg_send_msg(g, &vmsg, -1);
+            vg_send_dmabuf_update(g, i, extents->x1, extents->y1, width, height);
             vg_wait_ok(g);
         } else {
             size_t bpp =
diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c
index 0c7f118556..b17cf22986 100644
--- a/contrib/vhost-user-gpu/virgl.c
+++ b/contrib/vhost-user-gpu/virgl.c
@@ -370,16 +370,7 @@ virgl_cmd_resource_flush(VuGpu *g,
         if (g->scanout[i].resource_id != rf.resource_id) {
             continue;
         }
-        VhostUserGpuMsg msg = {
-            .request = VHOST_USER_GPU_DMABUF_UPDATE,
-            .size = sizeof(VhostUserGpuUpdate),
-            .payload.update.scanout_id = i,
-            .payload.update.x = rf.r.x,
-            .payload.update.y = rf.r.y,
-            .payload.update.width = rf.r.width,
-            .payload.update.height = rf.r.height
-        };
-        vg_send_msg(g, &msg, -1);
+        vg_send_dmabuf_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.height);
         vg_wait_ok(g);
     }
 }
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 19/27] vhost-user-gpu: add vg_send_scanout()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (17 preceding siblings ...)
  2021-03-12 10:00 ` [PATCH 18/27] vhost-user-gpu: add vg_send_dmabuf_update() marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 20/27] vhost-user-gpu: add vg_send_cursor_update() marcandre.lureau
                   ` (9 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugpu.h          |  1 +
 contrib/vhost-user-gpu/vhost-user-gpu.c | 27 ++++++++++++++++---------
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index f2e80d255d..bf513e9295 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -194,5 +194,6 @@ void    vg_send_dmabuf_update(VuGpu *g,
                               uint32_t y,
                               uint32_t width,
                               uint32_t height);
+void    vg_send_scanout(VuGpu *g, uint32_t scanout_id);
 
 #endif
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index fc8e62f20e..a11f406350 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -638,6 +638,22 @@ vg_send_dmabuf_update(VuGpu *g,
     vg_send_msg(g, &msg, -1);
 }
 
+void
+vg_send_scanout(VuGpu *g, uint32_t scanout_id)
+{
+    struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id];
+    VhostUserGpuMsg msg = {
+        .request = VHOST_USER_GPU_SCANOUT,
+        .size = sizeof(VhostUserGpuScanout),
+        .payload.scanout = (VhostUserGpuScanout) {
+            .scanout_id = scanout_id,
+            .width = scanout->width,
+            .height = scanout->height
+        }
+    };
+    vg_send_msg(g, &msg, -1);
+}
+
 static void
 vg_set_scanout(VuGpu *g,
                struct virtio_gpu_ctrl_command *cmd)
@@ -714,16 +730,7 @@ vg_set_scanout(VuGpu *g,
             close(fd);
         }
     } else {
-        VhostUserGpuMsg msg = {
-            .request = VHOST_USER_GPU_SCANOUT,
-            .size = sizeof(VhostUserGpuScanout),
-            .payload.scanout = (VhostUserGpuScanout) {
-                .scanout_id = ss.scanout_id,
-                .width = scanout->width,
-                .height = scanout->height
-            }
-        };
-        vg_send_msg(g, &msg, -1);
+        vg_send_scanout(g, ss.scanout_id);
     }
 }
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 20/27] vhost-user-gpu: add vg_send_cursor_update()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (18 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 19/27] vhost-user-gpu: add vg_send_scanout() marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 21/27] vhost-user-gpu: add vg_send_cursor_pos() marcandre.lureau
                   ` (8 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugpu.h          |  4 ++-
 contrib/vhost-user-gpu/vhost-user-gpu.c | 46 +++++++++++++++----------
 2 files changed, 31 insertions(+), 19 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index bf513e9295..02a22bae34 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -195,5 +195,7 @@ void    vg_send_dmabuf_update(VuGpu *g,
                               uint32_t width,
                               uint32_t height);
 void    vg_send_scanout(VuGpu *g, uint32_t scanout_id);
-
+void    vg_send_cursor_update(VuGpu *g,
+                              const struct virtio_gpu_update_cursor *cursor,
+                              const void *data);
 #endif
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index a11f406350..32bcbaa9a1 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -935,6 +935,30 @@ update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data)
     memcpy(data, pixman_image_get_data(res->image), 64 * 64 * sizeof(uint32_t));
 }
 
+void
+vg_send_cursor_update(VuGpu *g,
+                      const struct virtio_gpu_update_cursor *cursor,
+                      const void *data)
+{
+    VhostUserGpuMsg msg = {
+        .request = VHOST_USER_GPU_CURSOR_UPDATE,
+        .size = sizeof(VhostUserGpuCursorUpdate),
+        .payload.cursor_update = {
+            .pos = {
+                .scanout_id = cursor->pos.scanout_id,
+                .x = cursor->pos.x,
+                .y = cursor->pos.y,
+            },
+            .hot_x = cursor->hot_x,
+            .hot_y = cursor->hot_y,
+        }
+    };
+    /* we can afford that cursor copy */
+    memcpy(msg.payload.cursor_update.data, data,
+           sizeof(msg.payload.cursor_update.data));
+    vg_send_msg(g, &msg, -1);
+}
+
 static void
 vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
 {
@@ -955,28 +979,14 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
         break;
     }
     case VIRTIO_GPU_CMD_UPDATE_CURSOR: {
-        VhostUserGpuMsg msg = {
-            .request = VHOST_USER_GPU_CURSOR_UPDATE,
-            .size = sizeof(VhostUserGpuCursorUpdate),
-            .payload.cursor_update = {
-                .pos = {
-                    .scanout_id = cursor->pos.scanout_id,
-                    .x = cursor->pos.x,
-                    .y = cursor->pos.y,
-                },
-                .hot_x = cursor->hot_x,
-                .hot_y = cursor->hot_y,
-            }
-        };
+        uint32_t data[64 * 64] = { 0, };
         g_debug("%s: update", G_STRFUNC);
         if (g->virgl) {
-            vg_virgl_update_cursor_data(g, cursor->resource_id,
-                                        msg.payload.cursor_update.data);
+            vg_virgl_update_cursor_data(g, cursor->resource_id, data);
         } else {
-            update_cursor_data_simple(g, cursor->resource_id,
-                                      msg.payload.cursor_update.data);
+            update_cursor_data_simple(g, cursor->resource_id, data);
         }
-        vg_send_msg(g, &msg, -1);
+        vg_send_cursor_update(g, cursor, data);
         break;
     }
     default:
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 21/27] vhost-user-gpu: add vg_send_cursor_pos()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (19 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 20/27] vhost-user-gpu: add vg_send_cursor_update() marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 22/27] vhost-user-gpu: add vg_send_update() marcandre.lureau
                   ` (7 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugpu.h          |  3 +++
 contrib/vhost-user-gpu/vhost-user-gpu.c | 28 +++++++++++++++----------
 2 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index 02a22bae34..1927fb4f24 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -198,4 +198,7 @@ void    vg_send_scanout(VuGpu *g, uint32_t scanout_id);
 void    vg_send_cursor_update(VuGpu *g,
                               const struct virtio_gpu_update_cursor *cursor,
                               const void *data);
+void    vg_send_cursor_pos(VuGpu *g,
+                           const struct virtio_gpu_update_cursor *cursor);
+
 #endif
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index 32bcbaa9a1..4972448dbc 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -959,23 +959,29 @@ vg_send_cursor_update(VuGpu *g,
     vg_send_msg(g, &msg, -1);
 }
 
+void
+vg_send_cursor_pos(VuGpu *g, const struct virtio_gpu_update_cursor *cursor)
+{
+    VhostUserGpuMsg msg = {
+        .request = cursor->resource_id ?
+        VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE,
+        .size = sizeof(VhostUserGpuCursorPos),
+        .payload.cursor_pos = {
+            .scanout_id = cursor->pos.scanout_id,
+            .x = cursor->pos.x,
+            .y = cursor->pos.y,
+        }
+    };
+    vg_send_msg(g, &msg, -1);
+}
+
 static void
 vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
 {
     switch (cursor->hdr.type) {
     case VIRTIO_GPU_CMD_MOVE_CURSOR: {
-        VhostUserGpuMsg msg = {
-            .request = cursor->resource_id ?
-                VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE,
-            .size = sizeof(VhostUserGpuCursorPos),
-            .payload.cursor_pos = {
-                .scanout_id = cursor->pos.scanout_id,
-                .x = cursor->pos.x,
-                .y = cursor->pos.y,
-            }
-        };
         g_debug("%s: move", G_STRFUNC);
-        vg_send_msg(g, &msg, -1);
+        vg_send_cursor_pos(g, cursor);
         break;
     }
     case VIRTIO_GPU_CMD_UPDATE_CURSOR: {
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 22/27] vhost-user-gpu: add vg_send_update()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (20 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 21/27] vhost-user-gpu: add vg_send_cursor_pos() marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 23/27] vhost-user: add VHOST_USER_GPU_QEMU_DBUS_LISTENER marcandre.lureau
                   ` (6 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugpu.h          | 11 ++++
 contrib/vhost-user-gpu/vhost-user-gpu.c | 84 +++++++++++++++++--------
 2 files changed, 68 insertions(+), 27 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index 1927fb4f24..370fd10667 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -201,4 +201,15 @@ void    vg_send_cursor_update(VuGpu *g,
 void    vg_send_cursor_pos(VuGpu *g,
                            const struct virtio_gpu_update_cursor *cursor);
 
+typedef void (*VgUpdateFill)(VuGpu *g, VhostUserGpuMsg *msg, void *fill_data);
+
+void    vg_send_update(VuGpu *g,
+                       uint32_t scanout_id,
+                       uint32_t x,
+                       uint32_t y,
+                       uint32_t width,
+                       uint32_t height,
+                       size_t data_size,
+                       VgUpdateFill fill_cb,
+                       void *fill_data);
 #endif
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index 4972448dbc..d74ea127d8 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -734,6 +734,61 @@ vg_set_scanout(VuGpu *g,
     }
 }
 
+void
+vg_send_update(VuGpu *g,
+               uint32_t scanout_id,
+               uint32_t x,
+               uint32_t y,
+               uint32_t width,
+               uint32_t height,
+               size_t data_size,
+               VgUpdateFill fill_cb,
+               void *fill_data)
+{
+    void *p = g_malloc(VHOST_USER_GPU_HDR_SIZE +
+                       sizeof(VhostUserGpuUpdate) + data_size);
+    VhostUserGpuMsg *msg = p;
+    msg->request = VHOST_USER_GPU_UPDATE;
+    msg->size = sizeof(VhostUserGpuUpdate) + data_size;
+    msg->payload.update = (VhostUserGpuUpdate) {
+        .scanout_id = scanout_id,
+        .x = x,
+        .y = y,
+        .width = width,
+        .height = height,
+    };
+    fill_cb(g, msg, fill_data);
+    vg_send_msg(g, msg, -1);
+    g_free(msg);
+}
+
+static void
+fill_update_data(VuGpu *g, VhostUserGpuMsg *msg, void *fill_data)
+{
+    struct virtio_gpu_simple_resource *res = fill_data;
+    pixman_image_t *i;
+    size_t bpp;
+    uint32_t x, y, width, height;
+
+    x = msg->payload.update.x;
+    y = msg->payload.update.y;
+    width = msg->payload.update.width;
+    height = msg->payload.update.height;
+
+    bpp = PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8;
+    i = pixman_image_create_bits(pixman_image_get_format(res->image),
+                                 width, height,
+                                 (void *)msg + offsetof(VhostUserGpuMsg,
+                                                        payload.update.data),
+                                 width * bpp);
+    pixman_image_composite(PIXMAN_OP_SRC,
+                           res->image, NULL, i,
+                           x, y,
+                           0, 0, 0, 0,
+                           width, height);
+    pixman_image_unref(i);
+}
+
 static void
 vg_resource_flush(VuGpu *g,
                   struct virtio_gpu_ctrl_command *cmd)
@@ -798,33 +853,8 @@ vg_resource_flush(VuGpu *g,
                 PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8;
             size_t size = width * height * bpp;
 
-            void *p = g_malloc(VHOST_USER_GPU_HDR_SIZE +
-                               sizeof(VhostUserGpuUpdate) + size);
-            VhostUserGpuMsg *msg = p;
-            msg->request = VHOST_USER_GPU_UPDATE;
-            msg->size = sizeof(VhostUserGpuUpdate) + size;
-            msg->payload.update = (VhostUserGpuUpdate) {
-                .scanout_id = i,
-                .x = extents->x1,
-                .y = extents->y1,
-                .width = width,
-                .height = height,
-            };
-            pixman_image_t *i =
-                pixman_image_create_bits(pixman_image_get_format(res->image),
-                                         msg->payload.update.width,
-                                         msg->payload.update.height,
-                                         p + offsetof(VhostUserGpuMsg,
-                                                      payload.update.data),
-                                         width * bpp);
-            pixman_image_composite(PIXMAN_OP_SRC,
-                                   res->image, NULL, i,
-                                   extents->x1, extents->y1,
-                                   0, 0, 0, 0,
-                                   width, height);
-            pixman_image_unref(i);
-            vg_send_msg(g, msg, -1);
-            g_free(msg);
+            vg_send_update(g, i, extents->x1, extents->y1, width, height,
+                           size, fill_update_data, res);
         }
         pixman_region_fini(&region);
         pixman_region_fini(&finalregion);
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 23/27] vhost-user: add VHOST_USER_GPU_QEMU_DBUS_LISTENER
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (21 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 22/27] vhost-user-gpu: add vg_send_update() marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 24/27] ui: add GraphicHwOps.register_dbus_listener() marcandre.lureau
                   ` (5 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Add a new feature & message to register a QEMU DBus console listener.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 docs/interop/vhost-user.rst               | 10 ++++++++++
 include/hw/virtio/vhost-backend.h         |  2 ++
 subprojects/libvhost-user/libvhost-user.h |  5 +++++
 hw/virtio/vhost-user.c                    | 23 +++++++++++++++++++++++
 4 files changed, 40 insertions(+)

diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst
index d6085f7045..13515fb948 100644
--- a/docs/interop/vhost-user.rst
+++ b/docs/interop/vhost-user.rst
@@ -834,6 +834,7 @@ Protocol features
   #define VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS 14
   #define VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS  15
   #define VHOST_USER_PROTOCOL_F_STATUS               16
+  #define VHOST_USER_PROTOCOL_F_GPU_QEMU_DBUS_LISTENER 17
 
 Master message types
 --------------------
@@ -1347,6 +1348,15 @@ Master message types
   query the backend for its device status as defined in the Virtio
   specification.
 
+``VHOST_USER_GPU_QEMU_DBUS_LISTENER``
+  :id: 41
+  :equivalent ioctl: N/A
+  :request payload: ``u32`` the scanout to listen for
+
+  When the ``VHOST_USER_PROTOCOL_F_GPU_QEMU_DBUS_LISTENER`` protocol feature has
+  been successfully negotiated, this message is submitted by the frontend to
+  register a graphical listener using a currently private QEMU DBus protocol.
+
 
 Slave message types
 -------------------
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index 8a6f8e2a7a..a64b92b863 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -190,5 +190,7 @@ int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev,
                                           struct vhost_iotlb_msg *imsg);
 
 int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd);
+int vhost_user_gpu_register_dbus_listener(struct vhost_dev *dev,
+                                          uint8_t idx, int fd);
 
 #endif /* VHOST_BACKEND_H */
diff --git a/subprojects/libvhost-user/libvhost-user.h b/subprojects/libvhost-user/libvhost-user.h
index 3d13dfadde..d4152acde1 100644
--- a/subprojects/libvhost-user/libvhost-user.h
+++ b/subprojects/libvhost-user/libvhost-user.h
@@ -64,6 +64,8 @@ enum VhostUserProtocolFeature {
     VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12,
     VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS = 14,
     VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS = 15,
+    VHOST_USER_PROTOCOL_F_STATUS = 16,
+    VHOST_USER_PROTOCOL_F_GPU_QEMU_DBUS_LISTENER = 17,
 
     VHOST_USER_PROTOCOL_F_MAX
 };
@@ -109,6 +111,9 @@ typedef enum VhostUserRequest {
     VHOST_USER_GET_MAX_MEM_SLOTS = 36,
     VHOST_USER_ADD_MEM_REG = 37,
     VHOST_USER_REM_MEM_REG = 38,
+    VHOST_USER_SET_STATUS = 39,
+    VHOST_USER_GET_STATUS = 40,
+    VHOST_USER_GPU_QEMU_DBUS_LISTENER = 41,
     VHOST_USER_MAX
 } VhostUserRequest;
 
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 2fdd5daf74..45758c84f9 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -79,6 +79,8 @@ enum VhostUserProtocolFeature {
     VHOST_USER_PROTOCOL_F_RESET_DEVICE = 13,
     /* Feature 14 reserved for VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS. */
     VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS = 15,
+    VHOST_USER_PROTOCOL_F_STATUS = 16,
+    VHOST_USER_PROTOCOL_F_GPU_QEMU_DBUS_LISTENER = 17,
     VHOST_USER_PROTOCOL_F_MAX
 };
 
@@ -124,6 +126,9 @@ typedef enum VhostUserRequest {
     VHOST_USER_GET_MAX_MEM_SLOTS = 36,
     VHOST_USER_ADD_MEM_REG = 37,
     VHOST_USER_REM_MEM_REG = 38,
+    VHOST_USER_SET_STATUS = 39,
+    VHOST_USER_GET_STATUS = 40,
+    VHOST_USER_GPU_QEMU_DBUS_LISTENER = 41,
     VHOST_USER_MAX
 } VhostUserRequest;
 
@@ -407,6 +412,24 @@ int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd)
     return vhost_user_write(dev, &msg, &fd, 1);
 }
 
+int vhost_user_gpu_register_dbus_listener(struct vhost_dev *dev, uint8_t idx, int fd)
+{
+    VhostUserMsg msg = {
+        .hdr.request = VHOST_USER_GPU_QEMU_DBUS_LISTENER,
+        .hdr.flags = VHOST_USER_VERSION,
+        .payload.u64 = idx,
+        .hdr.size = sizeof(msg.payload.u64),
+    };
+
+    if (!(dev->protocol_features &
+          (1ULL << VHOST_USER_PROTOCOL_F_GPU_QEMU_DBUS_LISTENER))) {
+        return -1;
+    }
+
+    return vhost_user_write(dev, &msg, &fd, 1);
+}
+
+
 static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
                                    struct vhost_log *log)
 {
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 24/27] ui: add GraphicHwOps.register_dbus_listener()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (22 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 23/27] vhost-user: add VHOST_USER_GPU_QEMU_DBUS_LISTENER marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 25/27] vhost-user-gpu: implement register_dbus_listener() marcandre.lureau
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h |  2 ++
 ui/console.c         | 11 +++++++++++
 ui/dbus-console.c    |  5 ++++-
 3 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 31141955d9..0ad0d3ad66 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -429,6 +429,7 @@ typedef struct GraphicHwOps {
     void (*update_interval)(void *opaque, uint64_t interval);
     int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info);
     void (*gl_block)(void *opaque, bool block);
+    bool (*register_dbus_listener)(void *opaque, QemuConsole *console, int fd);
 } GraphicHwOps;
 
 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
@@ -444,6 +445,7 @@ void graphic_hw_update_done(QemuConsole *con);
 void graphic_hw_invalidate(QemuConsole *con);
 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
 void graphic_hw_gl_block(QemuConsole *con, bool block);
+bool graphic_hw_register_dbus_listener(QemuConsole *con, int fd);
 
 void qemu_console_early_init(void);
 
diff --git a/ui/console.c b/ui/console.c
index 3c3be032ad..8c2a29a5d8 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -320,6 +320,17 @@ void graphic_hw_gl_block(QemuConsole *con, bool block)
     }
 }
 
+bool graphic_hw_register_dbus_listener(QemuConsole *con, int fd)
+{
+    assert(con != NULL);
+
+    if (con->hw_ops->register_dbus_listener) {
+        return con->hw_ops->register_dbus_listener(con->hw, con, fd);
+    }
+
+    return false;
+}
+
 int qemu_console_get_window_id(QemuConsole *con)
 {
     return con->window_id;
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index 3487a8f5cb..eaae914199 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -242,10 +242,13 @@ dbus_console_register_listener(DBusDisplayConsole *self,
         close(fd);
         return DBUS_METHOD_INVOCATION_HANDLED;
     }
-    socket_conn = g_socket_connection_factory_create_connection(socket);
     /* return now: easier for the other end, as it may handle priv dbus synchronously */
     dbus_display_display1_console_complete_register_listener(self->iface, invocation, NULL);
 
+    if (graphic_hw_register_dbus_listener(self->con, fd)) {
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+    socket_conn = g_socket_connection_factory_create_connection(socket);
     listener_conn = g_dbus_connection_new_sync(G_IO_STREAM(socket_conn),
                                                guid,
                                                G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 25/27] vhost-user-gpu: implement register_dbus_listener()
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (23 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 24/27] ui: add GraphicHwOps.register_dbus_listener() marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 26/27] vhost-user-gpu: check the PIXMAN format is supported marcandre.lureau
                   ` (3 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Send the listener fd over the vhost-user connection if the backend
supports it.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu.h |  1 +
 hw/display/vhost-user-gpu.c    | 33 +++++++++++++++++++++++++++++++++
 hw/display/virtio-gpu-base.c   | 14 ++++++++++++++
 hw/display/virtio-vga.c        | 10 ++++++++++
 4 files changed, 58 insertions(+)

diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index fae149235c..bf33c05121 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -122,6 +122,7 @@ struct VirtIOGPUBaseClass {
     VirtioDeviceClass parent;
 
     void (*gl_flushed)(VirtIOGPUBase *g);
+    bool (*register_dbus_listener)(VirtIOGPUBase *g, QemuConsole *con, int fd);
 };
 
 #define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf)                       \
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
index a2a011e9cc..b48ddabcc8 100644
--- a/hw/display/vhost-user-gpu.c
+++ b/hw/display/vhost-user-gpu.c
@@ -370,6 +370,38 @@ vhost_user_gpu_gl_flushed(VirtIOGPUBase *b)
     vhost_user_gpu_update_blocked(VHOST_USER_GPU(g), false);
 }
 
+static bool
+vhost_user_gpu_scanout_idx(VirtIOGPUBase *b, QemuConsole *con, uint8_t *idx)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(b);
+    struct virtio_gpu_scanout *s;
+    uint8_t i;
+
+    for (i = 0; i < G_N_ELEMENTS(g->parent_obj.scanout); i++) {
+        s = &g->parent_obj.scanout[i];
+        if (s->con == con) {
+            *idx = i;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool
+vhost_user_gpu_do_register_dbus_listener(VirtIOGPUBase *b, QemuConsole *con, int fd)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(b);
+    uint8_t idx = 0;
+
+    if (!vhost_user_gpu_scanout_idx(b, con, &idx)) {
+        error_report("Failed to find attached console %p", con);
+        return false;
+    }
+
+    return vhost_user_gpu_register_dbus_listener(&g->vhost->dev, idx, fd) == 0;
+}
+
 static bool
 vhost_user_gpu_do_set_socket(VhostUserGPU *g, Error **errp)
 {
@@ -577,6 +609,7 @@ vhost_user_gpu_class_init(ObjectClass *klass, void *data)
     VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_CLASS(klass);
 
     vgc->gl_flushed = vhost_user_gpu_gl_flushed;
+    vgc->register_dbus_listener = vhost_user_gpu_do_register_dbus_listener;
 
     vdc->realize = vhost_user_gpu_device_realize;
     vdc->reset = vhost_user_gpu_reset;
diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index ee2753001a..392719a830 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -141,6 +141,19 @@ virtio_gpu_get_flags(void *opaque)
     return flags;
 }
 
+static bool
+virtio_gpu_register_dbus_listener(void *opaque, QemuConsole *con, int fd)
+{
+    VirtIOGPUBase *g = opaque;
+    VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_GET_CLASS(g);
+
+    if (vgc->register_dbus_listener) {
+        return vgc->register_dbus_listener(g, con, fd);
+    }
+
+    return false;
+}
+
 static const GraphicHwOps virtio_gpu_ops = {
     .get_flags = virtio_gpu_get_flags,
     .invalidate = virtio_gpu_invalidate_display,
@@ -148,6 +161,7 @@ static const GraphicHwOps virtio_gpu_ops = {
     .text_update = virtio_gpu_text_update,
     .ui_info = virtio_gpu_ui_info,
     .gl_block = virtio_gpu_gl_block,
+    .register_dbus_listener = virtio_gpu_register_dbus_listener,
 };
 
 bool
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index b071909b68..777c7fc409 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -76,6 +76,15 @@ static int virtio_vga_base_get_flags(void *opaque)
     return g->hw_ops->get_flags(g);
 }
 
+static bool virtio_vga_base_register_dbus_listener(void *opaque,
+                                                   QemuConsole *con, int fd)
+{
+    VirtIOVGABase *vvga = opaque;
+    VirtIOGPUBase *g = vvga->vgpu;
+
+    return g->hw_ops->register_dbus_listener(g, con, fd);
+}
+
 static const GraphicHwOps virtio_vga_base_ops = {
     .get_flags = virtio_vga_base_get_flags,
     .invalidate = virtio_vga_base_invalidate_display,
@@ -83,6 +92,7 @@ static const GraphicHwOps virtio_vga_base_ops = {
     .text_update = virtio_vga_base_text_update,
     .ui_info = virtio_vga_base_ui_info,
     .gl_block = virtio_vga_base_gl_block,
+    .register_dbus_listener = virtio_vga_base_register_dbus_listener,
 };
 
 static const VMStateDescription vmstate_virtio_vga_base = {
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 26/27] vhost-user-gpu: check the PIXMAN format is supported
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (24 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 25/27] vhost-user-gpu: implement register_dbus_listener() marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 10:01 ` [PATCH 27/27] vhost-user-gpu: implement GPU_QEMU_DBUS_LISTENER marcandre.lureau
                   ` (2 subsequent siblings)
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

There is no support for other PIXMAN formats atm.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vhost-user-gpu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index d74ea127d8..36d7f1d9e6 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -332,7 +332,7 @@ vg_resource_create_2d(VuGpu *g,
     res->resource_id = c2d.resource_id;
 
     pformat = virtio_gpu_get_pixman_format(c2d.format);
-    if (!pformat) {
+    if (!pformat || pformat != PIXMAN_x8r8g8b8) {
         g_critical("%s: host couldn't handle guest format %d",
                    __func__, c2d.format);
         g_free(res);
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 27/27] vhost-user-gpu: implement GPU_QEMU_DBUS_LISTENER
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (25 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 26/27] vhost-user-gpu: check the PIXMAN format is supported marcandre.lureau
@ 2021-03-12 10:01 ` marcandre.lureau
  2021-03-12 11:16 ` [PATCH 00/27] Add D-Bus display backend no-reply
  2021-03-12 11:18 ` Gerd Hoffmann
  28 siblings, 0 replies; 39+ messages in thread
From: marcandre.lureau @ 2021-03-12 10:01 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Gerd Hoffmann

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Implement the QEMU DBus listener feature. This allows vhost-user-gpu to
notify directly the console/ui clients.

However, QEMU is still notified with the vhost-user-gpu protocol, for
compatibility reasons. A future mechanism should allow qemu to opt-out
for display updates.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/vugpu.h          |   3 +
 contrib/vhost-user-gpu/vhost-user-gpu.c | 272 +++++++++++++++++++++++-
 contrib/vhost-user-gpu/meson.build      |   6 +-
 3 files changed, 269 insertions(+), 12 deletions(-)

diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index 370fd10667..81ffddf347 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -119,12 +119,15 @@ typedef struct VuGpu {
     int drm_rnode_fd;
     GSource *renderer_source;
     guint wait_in;
+    guint wait_dbus;
 
     bool virgl;
     bool virgl_inited;
     uint32_t inflight;
 
     struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS];
+    GHashTable *listeners[VIRTIO_GPU_MAX_SCANOUTS];
+
     QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
 } VuGpu;
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index 36d7f1d9e6..261a909fd4 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -18,6 +18,7 @@
 
 #include <pixman.h>
 #include <glib-unix.h>
+#include <gio/gunixfdlist.h>
 
 #include "vugpu.h"
 #include "hw/virtio/virtio-gpu-bswap.h"
@@ -25,6 +26,8 @@
 #include "virgl.h"
 #include "vugbm.h"
 
+#include "ui/dbus-display1.h"
+
 enum {
     VHOST_USER_GPU_MAX_QUEUES = 2,
 };
@@ -359,7 +362,11 @@ vg_resource_create_2d(VuGpu *g,
 void
 vg_send_disable_scanout(VuGpu *g, int scanout_id)
 {
+    GHashTableIter iter;
+    DBusDisplayDisplay1Listener *listener;
+
     g_debug("send disable scanout %d", scanout_id);
+    g_return_if_fail(scanout_id < VIRTIO_GPU_MAX_SCANOUTS);
 
     if (g->sock_fd >= 0) {
         VhostUserGpuMsg msg = {
@@ -369,6 +376,12 @@ vg_send_disable_scanout(VuGpu *g, int scanout_id)
         };
         vg_send_msg(g, &msg, -1);
     }
+
+    g_hash_table_iter_init(&iter, g->listeners[scanout_id]);
+    while (g_hash_table_iter_next(&iter, (void **)&listener, NULL)) {
+        dbus_display_display1_listener_call_disable(
+            listener, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
 }
 
 static void
@@ -597,6 +610,10 @@ vg_send_dmabuf_scanout(VuGpu *g,
                        uint32_t fd_flags,
                        int fd)
 {
+    GHashTableIter iter;
+    DBusDisplayDisplay1Listener *listener;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GUnixFDList) fd_list = NULL;
     VhostUserGpuMsg msg = {
         .request = VHOST_USER_GPU_DMABUF_SCANOUT,
         .size = sizeof(VhostUserGpuDMABUFScanout),
@@ -615,7 +632,49 @@ vg_send_dmabuf_scanout(VuGpu *g,
     };
 
     g_debug("send dmabuf scanout: %d", ss->scanout_id);
+    g_return_if_fail(ss->scanout_id < VIRTIO_GPU_MAX_SCANOUTS);
+
     vg_send_msg(g, &msg, fd);
+
+    fd_list = g_unix_fd_list_new();
+    if (g_unix_fd_list_append(fd_list, fd, &err) != 0) {
+        g_warning("Failed to setup dmabuf fdlist: %s", err->message);
+        return;
+    }
+
+    g_hash_table_iter_init(&iter, g->listeners[ss->scanout_id]);
+    while (g_hash_table_iter_next(&iter, (void **)&listener, NULL)) {
+        dbus_display_display1_listener_call_scanout_dmabuf(
+            listener,
+            g_variant_new_handle(0),
+            fd_width,
+            fd_height,
+            fd_stride,
+            fd_drm_fourcc,
+            0, /* modifier */
+            fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, /* == VIRGL_Y_0_TOP */
+            G_DBUS_CALL_FLAGS_NONE,
+            -1,
+            fd_list,
+            NULL, NULL, NULL);
+    }
+}
+
+static void
+dbus_update_gl_cb(GObject *source_object,
+                  GAsyncResult *res,
+                  gpointer user_data)
+{
+    g_autoptr(GError) err = NULL;
+    VuGpu *g = user_data;
+
+    if (!dbus_display_display1_listener_call_update_dmabuf_finish(
+            DBUS_DISPLAY_DISPLAY1_LISTENER(source_object), res, &err)) {
+        g_warning("Failed to call update dmabuf: %s", err->message);
+    }
+
+    g->wait_dbus--;
+    vg_handle_ctrl(&g->dev.parent, 0);
 }
 
 void
@@ -626,6 +685,8 @@ vg_send_dmabuf_update(VuGpu *g,
                       uint32_t width,
                       uint32_t height)
 {
+    GHashTableIter iter;
+    DBusDisplayDisplay1Listener *listener;
     VhostUserGpuMsg msg = {
         .request = VHOST_USER_GPU_DMABUF_UPDATE,
         .size = sizeof(VhostUserGpuUpdate),
@@ -635,7 +696,22 @@ vg_send_dmabuf_update(VuGpu *g,
         .payload.update.width = width,
         .payload.update.height = height
     };
+
+    g_return_if_fail(scanout_id < VIRTIO_GPU_MAX_SCANOUTS);
+
     vg_send_msg(g, &msg, -1);
+
+    g_hash_table_iter_init(&iter, g->listeners[scanout_id]);
+    while (g_hash_table_iter_next(&iter, (void **)&listener, NULL)) {
+        g->wait_dbus++;
+        dbus_display_display1_listener_call_update_dmabuf(
+            listener,
+            x, y, width, height,
+            G_DBUS_CALL_FLAGS_NONE,
+            -1, NULL,
+            dbus_update_gl_cb,
+            g);
+    }
 }
 
 void
@@ -652,6 +728,8 @@ vg_send_scanout(VuGpu *g, uint32_t scanout_id)
         }
     };
     vg_send_msg(g, &msg, -1);
+
+    /* dbus-listener is postponed until data is available */
 }
 
 static void
@@ -745,9 +823,17 @@ vg_send_update(VuGpu *g,
                VgUpdateFill fill_cb,
                void *fill_data)
 {
-    void *p = g_malloc(VHOST_USER_GPU_HDR_SIZE +
+    GHashTableIter iter;
+    DBusDisplayDisplay1Listener *listener;
+    g_autoptr(GVariant) v_data = NULL;
+    bool scanout = false;
+    VhostUserGpuMsg *msg;
+    void *p = NULL;
+
+    g_return_if_fail(scanout_id < VIRTIO_GPU_MAX_SCANOUTS);
+
+    msg = p = g_malloc(VHOST_USER_GPU_HDR_SIZE +
                        sizeof(VhostUserGpuUpdate) + data_size);
-    VhostUserGpuMsg *msg = p;
     msg->request = VHOST_USER_GPU_UPDATE;
     msg->size = sizeof(VhostUserGpuUpdate) + data_size;
     msg->payload.update = (VhostUserGpuUpdate) {
@@ -759,7 +845,40 @@ vg_send_update(VuGpu *g,
     };
     fill_cb(g, msg, fill_data);
     vg_send_msg(g, msg, -1);
-    g_free(msg);
+
+    if (x == 0 && y == 0 &&
+        width == g->scanout[scanout_id].width &&
+        height == g->scanout[scanout_id].height) {
+        scanout = true;
+    }
+
+    v_data = g_variant_new_from_data(
+        G_VARIANT_TYPE("ay"),
+        p + offsetof(VhostUserGpuMsg, payload.update.data),
+        data_size,
+        TRUE,
+        g_free,
+        p);
+    g_variant_ref_sink(v_data);
+
+    g_hash_table_iter_init(&iter, g->listeners[scanout_id]);
+    while (g_hash_table_iter_next(&iter, (void **)&listener, NULL)) {
+        if (scanout) {
+            dbus_display_display1_listener_call_scanout(
+                listener,
+                width, height, width * 4, PIXMAN_x8r8g8b8,
+                v_data,
+                G_DBUS_CALL_FLAGS_NONE,
+                -1, NULL, NULL, NULL);
+        } else {
+            dbus_display_display1_listener_call_update(
+                listener,
+                x, y, width, height, width * 4, PIXMAN_x8r8g8b8,
+                v_data,
+                G_DBUS_CALL_FLAGS_NONE,
+                -1, NULL, NULL, NULL);
+        }
+    }
 }
 
 static void
@@ -912,7 +1031,7 @@ vg_handle_ctrl(VuDev *dev, int qidx)
     size_t len;
 
     for (;;) {
-        if (vg->wait_in != 0) {
+        if (vg->wait_in != 0 || vg->wait_dbus > 0) {
             return;
         }
 
@@ -970,7 +1089,15 @@ vg_send_cursor_update(VuGpu *g,
                       const struct virtio_gpu_update_cursor *cursor,
                       const void *data)
 {
-    VhostUserGpuMsg msg = {
+    GHashTableIter iter;
+    DBusDisplayDisplay1Listener *listener;
+    VhostUserGpuMsg *msg;
+    g_autoptr(GVariant) v_data = NULL;
+
+    g_return_if_fail(cursor->pos.scanout_id < VIRTIO_GPU_MAX_SCANOUTS);
+
+    msg = g_new0(VhostUserGpuMsg, 1);
+    *msg = (VhostUserGpuMsg) {
         .request = VHOST_USER_GPU_CURSOR_UPDATE,
         .size = sizeof(VhostUserGpuCursorUpdate),
         .payload.cursor_update = {
@@ -983,15 +1110,43 @@ vg_send_cursor_update(VuGpu *g,
             .hot_y = cursor->hot_y,
         }
     };
+
     /* we can afford that cursor copy */
-    memcpy(msg.payload.cursor_update.data, data,
-           sizeof(msg.payload.cursor_update.data));
-    vg_send_msg(g, &msg, -1);
+    memcpy(msg->payload.cursor_update.data, data,
+           sizeof(msg->payload.cursor_update.data));
+    vg_send_msg(g, msg, -1);
+
+    v_data = g_variant_new_from_data(
+        G_VARIANT_TYPE("ay"),
+        msg->payload.cursor_update.data,
+        sizeof(msg->payload.cursor_update.data),
+        TRUE,
+        g_free,
+        msg);
+    g_variant_ref_sink(v_data);
+
+    g_hash_table_iter_init(&iter, g->listeners[cursor->pos.scanout_id]);
+    while (g_hash_table_iter_next(&iter, (void **)&listener, NULL)) {
+        dbus_display_display1_listener_call_cursor_define(
+            listener,
+            64,
+            64,
+            cursor->hot_x,
+            cursor->hot_y,
+            v_data,
+            G_DBUS_CALL_FLAGS_NONE,
+            -1,
+            NULL,
+            NULL,
+            NULL);
+    }
 }
 
 void
 vg_send_cursor_pos(VuGpu *g, const struct virtio_gpu_update_cursor *cursor)
 {
+    GHashTableIter iter;
+    DBusDisplayDisplay1Listener *listener;
     VhostUserGpuMsg msg = {
         .request = cursor->resource_id ?
         VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE,
@@ -1002,7 +1157,20 @@ vg_send_cursor_pos(VuGpu *g, const struct virtio_gpu_update_cursor *cursor)
             .y = cursor->pos.y,
         }
     };
+
+    g_return_if_fail(cursor->pos.scanout_id < VIRTIO_GPU_MAX_SCANOUTS);
+
     vg_send_msg(g, &msg, -1);
+
+    g_hash_table_iter_init(&iter, g->listeners[cursor->pos.scanout_id]);
+    while (g_hash_table_iter_next(&iter, (void **)&listener, NULL)) {
+        dbus_display_display1_listener_call_mouse_set(
+            listener,
+            cursor->pos.x,
+            cursor->pos.y,
+            cursor->resource_id != 0,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
 }
 
 static void
@@ -1127,6 +1295,70 @@ set_gpu_protocol_features(VuGpu *g)
                                protocol_features_cb, g);
 }
 
+static void
+listener_vanished_cb(GDBusConnection *connection,
+                     gboolean remote_peer_vanished,
+                     GError *error,
+                     VuGpu *g)
+{
+    DBusDisplayDisplay1Listener *listener;
+    uint8_t idx;
+
+    listener = g_object_get_data(G_OBJECT(connection), "listener");
+    g_debug("Removing dbus-listener %p", listener);
+
+    idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(listener), "idx"));
+    g_hash_table_remove(g->listeners[idx], listener);
+}
+
+static void
+vg_register_dbus_listener(VuGpu *g, uint8_t idx, int fd)
+{
+    GDBusConnection *listener_conn;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GSocket) socket = NULL;
+    g_autoptr(GSocketConnection) socket_conn = NULL;
+    g_autofree char *guid = g_dbus_generate_guid();
+    DBusDisplayDisplay1Listener *listener;
+
+    socket = g_socket_new_from_fd(fd, &err);
+    if (err) {
+        g_warning("Couldn't make a socket: %s", err->message);
+        close(fd);
+        return;
+    }
+    socket_conn = g_socket_connection_factory_create_connection(socket);
+    listener_conn = g_dbus_connection_new_sync(G_IO_STREAM(socket_conn),
+                                               guid,
+                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
+                                               NULL, NULL, &err);
+    if (err) {
+        g_warning("Failed to setup peer connection: %s", err->message);
+        return;
+    }
+
+    listener = dbus_display_display1_listener_proxy_new_sync(
+        listener_conn,
+        G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+        NULL,
+        "/org/qemu/Display1/Listener",
+        NULL,
+        &err);
+    if (!listener) {
+        g_warning("failed to setup proxy: %s", err->message);
+        return;
+    }
+
+    g_debug("Adding dbus-listener %p", listener);
+
+    g_object_set_data(G_OBJECT(listener), "idx", GINT_TO_POINTER(idx));
+    g_object_set_data(G_OBJECT(listener_conn), "listener", listener);
+    g_hash_table_add(g->listeners[idx], listener);
+    g_object_connect(listener_conn,
+                     "signal::closed", listener_vanished_cb, g,
+                     NULL);
+}
+
 static int
 vg_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply)
 {
@@ -1140,6 +1372,12 @@ vg_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply)
         set_gpu_protocol_features(g);
         return 1;
     }
+    case VHOST_USER_GPU_QEMU_DBUS_LISTENER: {
+        g_return_val_if_fail(msg->fd_num == 1, 1);
+        g_return_val_if_fail(msg->payload.u64 < VIRTIO_GPU_MAX_SCANOUTS, 1);
+        vg_register_dbus_listener(g, msg->payload.u64, msg->fds[0]);
+        return 1;
+    }
     default:
         return 0;
     }
@@ -1175,6 +1413,12 @@ vg_set_features(VuDev *dev, uint64_t features)
     g->virgl = virgl;
 }
 
+static uint64_t
+vg_get_protocol_features(VuDev *dev)
+{
+    return 1 << VHOST_USER_PROTOCOL_F_GPU_QEMU_DBUS_LISTENER;
+}
+
 static int
 vg_get_config(VuDev *dev, uint8_t *config, uint32_t len)
 {
@@ -1211,6 +1455,7 @@ vg_set_config(VuDev *dev, const uint8_t *data,
 static const VuDevIface vuiface = {
     .set_features = vg_set_features,
     .get_features = vg_get_features,
+    .get_protocol_features = vg_get_protocol_features,
     .queue_set_started = vg_queue_set_started,
     .process_msg = vg_process_msg,
     .get_config = vg_get_config,
@@ -1221,9 +1466,14 @@ static void
 vg_destroy(VuGpu *g)
 {
     struct virtio_gpu_simple_resource *res, *tmp;
+    int i;
 
     vug_deinit(&g->dev);
 
+    for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
+        g_clear_pointer(&g->listeners[i], g_hash_table_unref);
+    }
+
     vg_sock_fd_close(g);
 
     QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
@@ -1253,7 +1503,7 @@ main(int argc, char *argv[])
     GOptionContext *context;
     GError *error = NULL;
     GMainLoop *loop = NULL;
-    int fd;
+    int i, fd;
     VuGpu g = { .sock_fd = -1, .drm_rnode_fd = -1 };
 
     QTAILQ_INIT(&g.reslist);
@@ -1307,6 +1557,10 @@ main(int argc, char *argv[])
         exit(EXIT_FAILURE);
     }
 
+    for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
+        g.listeners[i] = g_hash_table_new_full(NULL, NULL, g_object_unref, NULL);
+    }
+
     if (!vug_init(&g.dev, VHOST_USER_GPU_MAX_QUEUES, fd, vg_panic, &vuiface)) {
         g_printerr("Failed to initialize libvhost-user-glib.\n");
         exit(EXIT_FAILURE);
diff --git a/contrib/vhost-user-gpu/meson.build b/contrib/vhost-user-gpu/meson.build
index 0ce1515a10..773823e5bf 100644
--- a/contrib/vhost-user-gpu/meson.build
+++ b/contrib/vhost-user-gpu/meson.build
@@ -1,8 +1,8 @@
 if 'CONFIG_TOOLS' in config_host and 'CONFIG_VIRGL' in config_host \
     and 'CONFIG_GBM' in config_host and 'CONFIG_LINUX' in config_host \
-    and pixman.found()
-  executable('vhost-user-gpu', files('vhost-user-gpu.c', 'virgl.c', 'vugbm.c'),
-             dependencies: [qemuutil, pixman, gbm, virgl, vhost_user, opengl],
+    and pixman.found() and 'CONFIG_UI_DBUS' in config_host
+  executable('vhost-user-gpu', files('vhost-user-gpu.c', 'virgl.c', 'vugbm.c') + dbus_display1,
+             dependencies: [qemuutil, pixman, gbm, virgl, vhost_user, opengl, gio],
              install: true,
              install_dir: get_option('libexecdir'))
 
-- 
2.29.0



^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [PATCH 07/27] ui: make gl_block use a counter
  2021-03-12 10:00 ` [PATCH 07/27] ui: make gl_block use a counter marcandre.lureau
@ 2021-03-12 10:12   ` Philippe Mathieu-Daudé
  2021-03-12 11:21     ` Marc-André Lureau
  0 siblings, 1 reply; 39+ messages in thread
From: Philippe Mathieu-Daudé @ 2021-03-12 10:12 UTC (permalink / raw)
  To: marcandre.lureau, qemu-devel; +Cc: Gerd Hoffmann

On 3/12/21 11:00 AM, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Track multiple callers blocking requests.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/console.c | 17 +++++++++++++----
>  1 file changed, 13 insertions(+), 4 deletions(-)
> 
> diff --git a/ui/console.c b/ui/console.c
> index 53eba2019e..fedb9d8b13 100644
> --- a/ui/console.c
> +++ b/ui/console.c
> @@ -128,7 +128,7 @@ struct QemuConsole {
>      DisplaySurface *surface;
>      int dcls;
>      DisplayChangeListener *gl;
> -    bool gl_block;
> +    int gl_block;
>      int window_id;
>  
>      /* Graphic console state.  */
> @@ -288,10 +288,19 @@ void graphic_hw_gl_block(QemuConsole *con, bool block)
>  {
>      assert(con != NULL);
>  
> -    con->gl_block = block;
> -    if (con->hw_ops->gl_block) {
> -        con->hw_ops->gl_block(con->hw, block);
> +    if (block) {
> +        con->gl_block++;
> +    } else {
> +        con->gl_block--;
> +    }
> +    assert(con->gl_block >= 0);
> +    if (!con->hw_ops->gl_block) {
> +        return;
> +    }
> +    if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) {

Dubious condition check... Could you rewrite it KISS for review?

> +        return;
>      }
> +    con->hw_ops->gl_block(con->hw, block);
>  }
>  
>  void graphic_hw_gl_flushed(QemuConsole *con)
> 



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 14/27] ui: add a D-Bus display backend
  2021-03-12 10:00 ` [PATCH 14/27] ui: add a D-Bus display backend marcandre.lureau
@ 2021-03-12 11:03   ` Gerd Hoffmann
  2021-03-12 11:27     ` Marc-André Lureau
  0 siblings, 1 reply; 39+ messages in thread
From: Gerd Hoffmann @ 2021-03-12 11:03 UTC (permalink / raw)
  To: marcandre.lureau; +Cc: qemu-devel

  Hi,

> The "dbus" ui/display backend exports the QEMU consoles over a currently
> unstable D-Bus protocol defined in ui/dbus-display1.xml (it is subject
> to changes at each releases, so only client shipped with the same QEMU
> release will be fully compatible).

Hmm, so we get a new protocol here.

A different idea which crossed my mind after reading the pipewire
article of last weeks lwn.net edition:  pipewire has video support.
It also has audio support.  gnome uses it for remote desktop support,
so support for cursors and mouse/keyboard backchannel should be there
too.

In other words: pipewire should have all the pieces needed to build a
qemu UI with it.

I haven't found the time yet to investigate more, but integrating qemu
with pipewire looks much more attractive to me than inventing a new
protocol ...

take care,
  Gerd



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 00/27] Add D-Bus display backend
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (26 preceding siblings ...)
  2021-03-12 10:01 ` [PATCH 27/27] vhost-user-gpu: implement GPU_QEMU_DBUS_LISTENER marcandre.lureau
@ 2021-03-12 11:16 ` no-reply
  2021-03-12 11:18 ` Gerd Hoffmann
  28 siblings, 0 replies; 39+ messages in thread
From: no-reply @ 2021-03-12 11:16 UTC (permalink / raw)
  To: marcandre.lureau; +Cc: marcandre.lureau, qemu-devel, kraxel

Patchew URL: https://patchew.org/QEMU/20210312100108.2706195-1-marcandre.lureau@redhat.com/



Hi,

This series seems to have some coding style problems. See output below for
more information:

Type: series
Message-id: 20210312100108.2706195-1-marcandre.lureau@redhat.com
Subject: [PATCH 00/27] Add D-Bus display backend

=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
 * [new tag]         patchew/20210312105711.551423-1-joel@jms.id.au -> patchew/20210312105711.551423-1-joel@jms.id.au
Switched to a new branch 'test'
f73f113 vhost-user-gpu: implement GPU_QEMU_DBUS_LISTENER
6ba1380 vhost-user-gpu: check the PIXMAN format is supported
ad37d6c vhost-user-gpu: implement register_dbus_listener()
e2eafdc ui: add GraphicHwOps.register_dbus_listener()
1e8ed11 vhost-user: add VHOST_USER_GPU_QEMU_DBUS_LISTENER
b28fa97 vhost-user-gpu: add vg_send_update()
0086f32 vhost-user-gpu: add vg_send_cursor_pos()
65e37ed vhost-user-gpu: add vg_send_cursor_update()
3226e4d vhost-user-gpu: add vg_send_scanout()
b8c78ef vhost-user-gpu: add vg_send_dmabuf_update()
bf9cd35 vhost-user-gpu: add vg_send_scanout_dmabuf()
141fb49 vhost-user-gpu: add vg_send_disable_scanout()
a60e70f audio: add dbusaudio backend
7d71890 ui: add a D-Bus display backend
baacefe console: save current scanout details
4cf336e ui: move qemu_spice_fill_device_address to ui/util.c
0240791 ui: split the GL context in a different object
f7870a3 ui: dispatch GL events to all listeners
7a9e313 ui: simplify gl unblock & flush
6270081 ui: add a gl-unblock warning timer
1368d16 ui: make gl_block use a counter
07c825b ui: associate GL context outside of display listener registration
7288b5e ui: factor out qemu_console_set_display_gl_ctx()
fe72bec vhost-user-gpu: fix cursor move/update
b18bd90 vhost-user-gpu: fix vugbm_device_init fallback
14d3ee3 vhost-user-gpu: glFlush before notifying clients
2b2ff66 ui: fold qemu_alloc_display in only caller

=== OUTPUT BEGIN ===
1/27 Checking commit 2b2ff665eaea (ui: fold qemu_alloc_display in only caller)
2/27 Checking commit 14d3ee3cb6cf (vhost-user-gpu: glFlush before notifying clients)
3/27 Checking commit b18bd90d6f40 (vhost-user-gpu: fix vugbm_device_init fallback)
4/27 Checking commit fe72bec1d42e (vhost-user-gpu: fix cursor move/update)
5/27 Checking commit 7288b5e4f5bd (ui: factor out qemu_console_set_display_gl_ctx())
WARNING: line over 80 characters
#25: FILE: include/ui/console.h:416:
+void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayChangeListener *dcl);

WARNING: line over 80 characters
#38: FILE: ui/console.c:1505:
+void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayChangeListener *dcl)

total: 0 errors, 2 warnings, 41 lines checked

Patch 5/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
6/27 Checking commit 07c825b03ff5 (ui: associate GL context outside of display listener registration)
7/27 Checking commit 1368d16fcaf4 (ui: make gl_block use a counter)
8/27 Checking commit 627008147d02 (ui: add a gl-unblock warning timer)
9/27 Checking commit 7a9e3136fb14 (ui: simplify gl unblock & flush)
10/27 Checking commit f7870a3d7b63 (ui: dispatch GL events to all listeners)
11/27 Checking commit 024079131dc9 (ui: split the GL context in a different object)
WARNING: Block comments use a leading /* on a separate line
#63: FILE: include/ui/console.h:250:
+    /* We only check if the GLCtx is compatible with a DCL via ops. A natural

WARNING: Block comments use a trailing */ on a separate line
#65: FILE: include/ui/console.h:252:
+     * and allow various DCL kinds. */

ERROR: braces {} are necessary for all arms of this statement
#235: FILE: ui/console.c:1533:
+    if (!con->gl)
[...]

total: 1 errors, 2 warnings, 503 lines checked

Patch 11/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

12/27 Checking commit 4cf336e76ce9 (ui: move qemu_spice_fill_device_address to ui/util.c)
Use of uninitialized value $acpi_testexpected in string eq at ./scripts/checkpatch.pl line 1529.
WARNING: line over 80 characters
#32: FILE: hw/display/qxl.c:2207:
+    if (qemu_console_fill_device_address(qxl->vga.con, device_address, 256, &err)) {

WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#165: 
new file mode 100644

total: 0 errors, 2 warnings, 188 lines checked

Patch 12/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
13/27 Checking commit baacefec89aa (console: save current scanout details)
WARNING: line over 80 characters
#116: FILE: ui/console.c:1128:
+    if (con->scanout.kind == SCANOUT_DMABUF && displaychangelistener_has_dmabuf(dcl)) {

WARNING: line over 80 characters
#118: FILE: ui/console.c:1130:
+    } else if (con->scanout.kind == SCANOUT_TEXTURE && dcl->ops->dpy_gl_scanout_texture) {

WARNING: line over 80 characters
#128: FILE: ui/console.c:1140:
+    } else if (con->scanout.kind == SCANOUT_SURFACE && dcl->ops->dpy_gfx_switch) {

WARNING: line over 80 characters
#240: FILE: ui/console.c:1936:
+        backing_id, backing_y_0_top, backing_width, backing_height, x, y, width, height

total: 0 errors, 4 warnings, 332 lines checked

Patch 13/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
14/27 Checking commit 7d71890862ab (ui: add a D-Bus display backend)
Use of uninitialized value $acpi_testexpected in string eq at ./scripts/checkpatch.pl line 1529.
ERROR: space prohibited between function name and open parenthesis '('
#86: FILE: include/qemu/dbus.h:24:
+#define DBUS_DISPLAY_ERROR (dbus_display_error_quark ())

ERROR: space prohibited between function name and open parenthesis '('
#87: FILE: include/qemu/dbus.h:25:
+GQuark dbus_display_error_quark (void);

ERROR: open brace '{' following enum go on the same line
#90: FILE: include/qemu/dbus.h:28:
+typedef enum
+{

WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#198: 
new file mode 100644

WARNING: line over 80 characters
#254: FILE: ui/dbus-console.c:52:
+G_DEFINE_TYPE(DBusDisplayConsole, dbus_display_console, G_TYPE_DBUS_OBJECT_SKELETON)

WARNING: line over 80 characters
#257: FILE: ui/dbus-console.c:55:
+dbus_display_console_set_size(DBusDisplayConsole *self, uint32_t width, uint32_t height)

WARNING: line over 80 characters
#434: FILE: ui/dbus-console.c:232:
+                                              "Couldn't get peer fd: %s", err->message);

WARNING: line over 80 characters
#443: FILE: ui/dbus-console.c:241:
+                                              "Couldn't make a socket: %s", err->message);

WARNING: line over 80 characters
#448: FILE: ui/dbus-console.c:246:
+    /* return now: easier for the other end, as it may handle priv dbus synchronously */

ERROR: line over 90 characters
#449: FILE: ui/dbus-console.c:247:
+    dbus_display_display1_console_complete_register_listener(self->iface, invocation, NULL);

ERROR: line over 90 characters
#453: FILE: ui/dbus-console.c:251:
+                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,

WARNING: line over 80 characters
#503: FILE: ui/dbus-console.c:301:
+    dbus_display_display1_keyboard_complete_release(self->iface_kbd, invocation);

WARNING: line over 80 characters
#512: FILE: ui/dbus-console.c:310:
+    guint modifiers = dbus_display_display1_keyboard_get_modifiers(self->iface_kbd);

ERROR: do not use C99 // comments
#522: FILE: ui/dbus-console.c:320:
+    // FIXME: what about self->kbd state?

WARNING: line over 80 characters
#558: FILE: ui/dbus-console.c:356:
+    dbus_display_display1_mouse_complete_set_abs_position(self->iface_mouse, invocation);

WARNING: line over 80 characters
#619: FILE: ui/dbus-console.c:417:
+    qemu_console_fill_device_address(con, device_addr, sizeof(device_addr), NULL);

WARNING: line over 80 characters
#631: FILE: ui/dbus-console.c:429:
+        "swapped-signal::handle-register-listener", dbus_console_register_listener, self,

ERROR: that open brace { should be on the previous line
#822: FILE: ui/dbus-error.c:28:
+static const GDBusErrorEntry dbus_display_error_entries[] =
+{

WARNING: line over 80 characters
#828: FILE: ui/dbus-error.c:34:
+G_STATIC_ASSERT(G_N_ELEMENTS(dbus_display_error_entries) == DBUS_DISPLAY_N_ERRORS);

ERROR: do not initialise statics to 0 or NULL
#833: FILE: ui/dbus-error.c:39:
+  static volatile gsize quark_volatile = 0;

ERROR: Use of volatile is usually wrong, please add a comment
#833: FILE: ui/dbus-error.c:39:
+  static volatile gsize quark_volatile = 0;

ERROR: open brace '{' following struct go on the same line
#880: FILE: ui/dbus-listener.c:35:
+struct _DBusDisplayListener
+{

ERROR: line over 90 characters
#904: FILE: ui/dbus-listener.c:59:
+    if (!dbus_display_display1_listener_call_update_dmabuf_finish(self->proxy, res, &err)) {

WARNING: line over 80 characters
#978: FILE: ui/dbus-listener.c:133:
+                                       (EGLint *)&dmabuf.fourcc, &dmabuf.modifier);

WARNING: line over 80 characters
#999: FILE: ui/dbus-listener.c:154:
+            self->proxy, 0, 0, false, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);

WARNING: line over 80 characters
#1039: FILE: ui/dbus-listener.c:194:
+        self->proxy, pos_x, pos_y, true, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);

WARNING: line over 80 characters
#1395: FILE: ui/dbus.c:64:
+    g_dbus_object_skeleton_add_interface(vm, G_DBUS_INTERFACE_SKELETON(self->iface));

ERROR: line over 90 characters
#1474: FILE: ui/dbus.c:143:
+                                          consoles->data, consoles->len * sizeof(guint32), TRUE,

WARNING: line over 80 characters
#1475: FILE: ui/dbus.c:144:
+                                          (GDestroyNotify)g_array_unref, consoles);

WARNING: line over 80 characters
#1484: FILE: ui/dbus.c:153:
+    g_bus_own_name_on_connection(self->bus, "org.qemu", G_BUS_NAME_OWNER_FLAGS_NONE,

ERROR: line over 90 characters
#1642: FILE: ui/dbus.h:50:
+G_DECLARE_FINAL_TYPE(DBusDisplayConsole, dbus_display_console, DBUS_DISPLAY, CONSOLE, GDBusObjectSkeleton)

WARNING: line over 80 characters
#1644: FILE: ui/dbus.h:52:
+DBusDisplayConsole *dbus_display_console_new(DBusDisplay *display, QemuConsole *con);

ERROR: line over 90 characters
#1648: FILE: ui/dbus.h:56:
+G_DECLARE_FINAL_TYPE(DBusDisplayListener, dbus_display_listener, DBUS_DISPLAY, LISTENER, GObject)

WARNING: line over 80 characters
#1654: FILE: ui/dbus.h:62:
+DBusDisplayConsole *dbus_display_listener_get_console(DBusDisplayListener *self);

total: 14 errors, 20 warnings, 1584 lines checked

Patch 14/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

15/27 Checking commit a60e70f5e0c9 (audio: add dbusaudio backend)
Use of uninitialized value $acpi_testexpected in string eq at ./scripts/checkpatch.pl line 1529.
ERROR: space prohibited before that close parenthesis ')'
#40: FILE: audio/audio.c:1992:
+        CASE(DBUS, dbus, );

WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#83: 
new file mode 100644

WARNING: line over 80 characters
#219: FILE: audio/dbusaudio.c:132:
+dbus_init_out_listener(DBusDisplayDisplay1AudioOutListener *listener, HWVoiceOut *hw)

WARNING: line over 80 characters
#333: FILE: audio/dbusaudio.c:246:
+dbus_init_in_listener(DBusDisplayDisplay1AudioInListener *listener, HWVoiceIn *hw)

WARNING: line over 80 characters
#496: FILE: audio/dbusaudio.c:409:
+        g_dbus_object_manager_server_unexport(self->server, DBUS_DISPLAY1_AUDIO);

WARNING: line over 80 characters
#637: FILE: audio/dbusaudio.c:550:
+            dbus_init_in_listener(DBUS_DISPLAY_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);

WARNING: line over 80 characters
#697: FILE: audio/dbusaudio.c:610:
+                                         G_DBUS_INTERFACE_SKELETON(self->iface));

WARNING: line over 80 characters
#947: FILE: ui/dbus.c:140:
+            error_setg(errp, "Audiodev '%s' is not compatible with DBus", self->audiodev);

total: 1 errors, 7 warnings, 915 lines checked

Patch 15/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

16/27 Checking commit 141fb49ac075 (vhost-user-gpu: add vg_send_disable_scanout())
17/27 Checking commit bf9cd35f08c8 (vhost-user-gpu: add vg_send_scanout_dmabuf())
18/27 Checking commit b8c78ef6ec12 (vhost-user-gpu: add vg_send_dmabuf_update())
WARNING: line over 80 characters
#61: FILE: contrib/vhost-user-gpu/vhost-user-gpu.c:787:
+            vg_send_dmabuf_update(g, i, extents->x1, extents->y1, width, height);

total: 0 errors, 1 warnings, 72 lines checked

Patch 18/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
19/27 Checking commit 3226e4d60b92 (vhost-user-gpu: add vg_send_scanout())
20/27 Checking commit 65e37ed2d91a (vhost-user-gpu: add vg_send_cursor_update())
21/27 Checking commit 0086f329c241 (vhost-user-gpu: add vg_send_cursor_pos())
22/27 Checking commit b28fa9782800 (vhost-user-gpu: add vg_send_update())
23/27 Checking commit 1e8ed114910b (vhost-user: add VHOST_USER_GPU_QEMU_DBUS_LISTENER)
WARNING: line over 80 characters
#73: FILE: hw/virtio/vhost-user.c:415:
+int vhost_user_gpu_register_dbus_listener(struct vhost_dev *dev, uint8_t idx, int fd)

total: 0 errors, 1 warnings, 87 lines checked

Patch 23/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
24/27 Checking commit e2eafdc90571 (ui: add GraphicHwOps.register_dbus_listener())
25/27 Checking commit ad37d6c5d2b7 (vhost-user-gpu: implement register_dbus_listener())
WARNING: line over 80 characters
#45: FILE: hw/display/vhost-user-gpu.c:391:
+vhost_user_gpu_do_register_dbus_listener(VirtIOGPUBase *b, QemuConsole *con, int fd)

total: 0 errors, 1 warnings, 100 lines checked

Patch 25/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
26/27 Checking commit 6ba138036df3 (vhost-user-gpu: check the PIXMAN format is supported)
27/27 Checking commit f73f113ed649 (vhost-user-gpu: implement GPU_QEMU_DBUS_LISTENER)
ERROR: line over 90 characters
#381: FILE: contrib/vhost-user-gpu/vhost-user-gpu.c:1333:
+                                               G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,

WARNING: line over 80 characters
#476: FILE: contrib/vhost-user-gpu/vhost-user-gpu.c:1561:
+        g.listeners[i] = g_hash_table_new_full(NULL, NULL, g_object_unref, NULL);

total: 1 errors, 1 warnings, 444 lines checked

Patch 27/27 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

=== OUTPUT END ===

Test command exited with code: 1


The full log is available at
http://patchew.org/logs/20210312100108.2706195-1-marcandre.lureau@redhat.com/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 00/27] Add D-Bus display backend
  2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
                   ` (27 preceding siblings ...)
  2021-03-12 11:16 ` [PATCH 00/27] Add D-Bus display backend no-reply
@ 2021-03-12 11:18 ` Gerd Hoffmann
  2021-03-12 11:29   ` Marc-André Lureau
  28 siblings, 1 reply; 39+ messages in thread
From: Gerd Hoffmann @ 2021-03-12 11:18 UTC (permalink / raw)
  To: marcandre.lureau; +Cc: qemu-devel

On Fri, Mar 12, 2021 at 02:00:41PM +0400, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>

The whole series calls for a splitup.  First the bugfixes, which we
should be able to fast-track for 6.0.  Next the ui opengl changes.  For
those a cover letter would be nice, describing the overall direction and
motivation for the change.  Make sure these don't re-introduce a opengl
dependency to core qemu for modular builds.  Finally the new display
backend patches / discussion.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 07/27] ui: make gl_block use a counter
  2021-03-12 10:12   ` Philippe Mathieu-Daudé
@ 2021-03-12 11:21     ` Marc-André Lureau
  0 siblings, 0 replies; 39+ messages in thread
From: Marc-André Lureau @ 2021-03-12 11:21 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: qemu-devel, Gerd Hoffmann

[-- Attachment #1: Type: text/plain, Size: 1700 bytes --]

On Fri, Mar 12, 2021 at 2:12 PM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 3/12/21 11:00 AM, marcandre.lureau@redhat.com wrote:
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> > Track multiple callers blocking requests.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  ui/console.c | 17 +++++++++++++----
> >  1 file changed, 13 insertions(+), 4 deletions(-)
> >
> > diff --git a/ui/console.c b/ui/console.c
> > index 53eba2019e..fedb9d8b13 100644
> > --- a/ui/console.c
> > +++ b/ui/console.c
> > @@ -128,7 +128,7 @@ struct QemuConsole {
> >      DisplaySurface *surface;
> >      int dcls;
> >      DisplayChangeListener *gl;
> > -    bool gl_block;
> > +    int gl_block;
> >      int window_id;
> >
> >      /* Graphic console state.  */
> > @@ -288,10 +288,19 @@ void graphic_hw_gl_block(QemuConsole *con, bool
> block)
> >  {
> >      assert(con != NULL);
> >
> > -    con->gl_block = block;
> > -    if (con->hw_ops->gl_block) {
> > -        con->hw_ops->gl_block(con->hw, block);
> > +    if (block) {
> > +        con->gl_block++;
> > +    } else {
> > +        con->gl_block--;
> > +    }
> > +    assert(con->gl_block >= 0);
> > +    if (!con->hw_ops->gl_block) {
> > +        return;
> > +    }
> > +    if ((block && con->gl_block != 1) || (!block && con->gl_block !=
> 0)) {
>
> Dubious condition check... Could you rewrite it KISS for review?
>

Well, I have no good idea :) Break in two if-return blocks?


> > +        return;
> >      }
> > +    con->hw_ops->gl_block(con->hw, block);
> >  }
> >
> >  void graphic_hw_gl_flushed(QemuConsole *con)
> >
>
>

[-- Attachment #2: Type: text/html, Size: 2765 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 14/27] ui: add a D-Bus display backend
  2021-03-12 11:03   ` Gerd Hoffmann
@ 2021-03-12 11:27     ` Marc-André Lureau
  2021-03-12 15:16       ` Gerd Hoffmann
  0 siblings, 1 reply; 39+ messages in thread
From: Marc-André Lureau @ 2021-03-12 11:27 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1284 bytes --]

Hi

On Fri, Mar 12, 2021 at 3:03 PM Gerd Hoffmann <kraxel@redhat.com> wrote:

>   Hi,
>
> > The "dbus" ui/display backend exports the QEMU consoles over a currently
> > unstable D-Bus protocol defined in ui/dbus-display1.xml (it is subject
> > to changes at each releases, so only client shipped with the same QEMU
> > release will be fully compatible).
>
> Hmm, so we get a new protocol here.
>
> A different idea which crossed my mind after reading the pipewire
> article of last weeks lwn.net edition:  pipewire has video support.
> It also has audio support.  gnome uses it for remote desktop support,
> so support for cursors and mouse/keyboard backchannel should be there
> too.
>
> In other words: pipewire should have all the pieces needed to build a
> qemu UI with it.
>
> I haven't found the time yet to investigate more, but integrating qemu
> with pipewire looks much more attractive to me than inventing a new
> protocol ...
>
>
Perhaps that could work for audio & video (where is the protocol defined?).
But what about the rest? input events, consoles, usb redirection, etc.
Remember the goal is to be able to implement any display backend in a
standalone project. Eventually, the RegisterListener & Listener interface
could become RegisterPipewireListener for example.

[-- Attachment #2: Type: text/html, Size: 1782 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 00/27] Add D-Bus display backend
  2021-03-12 11:18 ` Gerd Hoffmann
@ 2021-03-12 11:29   ` Marc-André Lureau
  2021-03-12 14:37     ` Gerd Hoffmann
  0 siblings, 1 reply; 39+ messages in thread
From: Marc-André Lureau @ 2021-03-12 11:29 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 747 bytes --]

Hi

On Fri, Mar 12, 2021 at 3:18 PM Gerd Hoffmann <kraxel@redhat.com> wrote:

> On Fri, Mar 12, 2021 at 02:00:41PM +0400, marcandre.lureau@redhat.com
> wrote:
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> The whole series calls for a splitup.  First the bugfixes, which we
> should be able to fast-track for 6.0.  Next the ui opengl changes.  For
> those a cover letter would be nice, describing the overall direction and
> motivation for the change.  Make sure these don't re-introduce a opengl
> dependency to core qemu for modular builds.  Finally the new display
> backend patches / discussion.
>

I agree, what about reviewing and queuing the first patches? Then I can
resend up to the second part.

>
>

[-- Attachment #2: Type: text/html, Size: 1363 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 00/27] Add D-Bus display backend
  2021-03-12 11:29   ` Marc-André Lureau
@ 2021-03-12 14:37     ` Gerd Hoffmann
  2021-03-12 14:57       ` Marc-André Lureau
  0 siblings, 1 reply; 39+ messages in thread
From: Gerd Hoffmann @ 2021-03-12 14:37 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

> On Fri, Mar 12, 2021 at 3:18 PM Gerd Hoffmann <kraxel@redhat.com> wrote:
> 
> > On Fri, Mar 12, 2021 at 02:00:41PM +0400, marcandre.lureau@redhat.com
> > wrote:
> > > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> > The whole series calls for a splitup.  First the bugfixes, which we
> > should be able to fast-track for 6.0.  Next the ui opengl changes.  For
> > those a cover letter would be nice, describing the overall direction and
> > motivation for the change.  Make sure these don't re-introduce a opengl
> > dependency to core qemu for modular builds.  Finally the new display
> > backend patches / discussion.
> >
> 
> I agree, what about reviewing and queuing the first patches? Then I can
> resend up to the second part.

Can cherry-pick them.  First four patches I assume?

take care,
  Gerd



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 00/27] Add D-Bus display backend
  2021-03-12 14:37     ` Gerd Hoffmann
@ 2021-03-12 14:57       ` Marc-André Lureau
  0 siblings, 0 replies; 39+ messages in thread
From: Marc-André Lureau @ 2021-03-12 14:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1146 bytes --]

Hi

On Fri, Mar 12, 2021 at 6:37 PM Gerd Hoffmann <kraxel@redhat.com> wrote:

> > On Fri, Mar 12, 2021 at 3:18 PM Gerd Hoffmann <kraxel@redhat.com> wrote:
> >
> > > On Fri, Mar 12, 2021 at 02:00:41PM +0400, marcandre.lureau@redhat.com
> > > wrote:
> > > > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> > >
> > > The whole series calls for a splitup.  First the bugfixes, which we
> > > should be able to fast-track for 6.0.  Next the ui opengl changes.  For
> > > those a cover letter would be nice, describing the overall direction
> and
> > > motivation for the change.  Make sure these don't re-introduce a opengl
> > > dependency to core qemu for modular builds.  Finally the new display
> > > backend patches / discussion.
> > >
> >
> > I agree, what about reviewing and queuing the first patches? Then I can
> > resend up to the second part.
>
> Can cherry-pick them.  First four patches I assume?
>
>
Yes, those are clearly fixes. The following GL context/events patches could
be upstream too, if you agree with it, even if they don't really have need
until a display backend makes real use of them.

[-- Attachment #2: Type: text/html, Size: 1799 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 14/27] ui: add a D-Bus display backend
  2021-03-12 11:27     ` Marc-André Lureau
@ 2021-03-12 15:16       ` Gerd Hoffmann
  0 siblings, 0 replies; 39+ messages in thread
From: Gerd Hoffmann @ 2021-03-12 15:16 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

> On Fri, Mar 12, 2021 at 3:03 PM Gerd Hoffmann <kraxel@redhat.com> wrote:
> 
> >   Hi,
> >
> > > The "dbus" ui/display backend exports the QEMU consoles over a currently
> > > unstable D-Bus protocol defined in ui/dbus-display1.xml (it is subject
> > > to changes at each releases, so only client shipped with the same QEMU
> > > release will be fully compatible).
> >
> > Hmm, so we get a new protocol here.
> >
> > A different idea which crossed my mind after reading the pipewire
> > article of last weeks lwn.net edition:  pipewire has video support.
> > It also has audio support.  gnome uses it for remote desktop support,
> > so support for cursors and mouse/keyboard backchannel should be there
> > too.
> >
> > In other words: pipewire should have all the pieces needed to build a
> > qemu UI with it.
> >
> > I haven't found the time yet to investigate more, but integrating qemu
> > with pipewire looks much more attractive to me than inventing a new
> > protocol ...

> Perhaps that could work for audio & video (where is the protocol defined?).

Dunno.  I'd go for the library though (simliar to pulseaudio, you'll go
use the libraries instead of talking to the pulse daemon directly).

Documentation starting point is https://pipewire.org/#documentation

> But what about the rest? input events, consoles, usb redirection, etc.

I expect input events are being there (how else should a remote desktop
work?), but didn't came that far yet in wading through the docs.  Dunno
for console and usb redirection.  Maybe it is possible to create custom
data channels.

> Remember the goal is to be able to implement any display backend in a
> standalone project.

Yes.  With pipewire I hope to get nice integration with everything
speaking pipewire in addition to that.  Like firefox being able to
use a virtual machine display for desktop sharing.

But maybe it works better in the end if a pipewire bridge is built as
standalone display backend based on this new dbus protocol.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH 05/27] ui: factor out qemu_console_set_display_gl_ctx()
  2021-03-12 10:00 ` [PATCH 05/27] ui: factor out qemu_console_set_display_gl_ctx() marcandre.lureau
@ 2021-03-12 15:34   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 39+ messages in thread
From: Philippe Mathieu-Daudé @ 2021-03-12 15:34 UTC (permalink / raw)
  To: marcandre.lureau, qemu-devel; +Cc: Gerd Hoffmann

On 3/12/21 11:00 AM, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> The next patch will make use of this function to dissociate
> DisplayChangeListener from GL context.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/ui/console.h |  2 ++
>  ui/console.c         | 21 +++++++++++++--------
>  2 files changed, 15 insertions(+), 8 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>



^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2021-03-12 16:14 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-12 10:00 [PATCH 00/27] Add D-Bus display backend marcandre.lureau
2021-03-12 10:00 ` [PATCH 01/27] ui: fold qemu_alloc_display in only caller marcandre.lureau
2021-03-12 10:00 ` [PATCH 02/27] vhost-user-gpu: glFlush before notifying clients marcandre.lureau
2021-03-12 10:00 ` [PATCH 03/27] vhost-user-gpu: fix vugbm_device_init fallback marcandre.lureau
2021-03-12 10:00 ` [PATCH 04/27] vhost-user-gpu: fix cursor move/update marcandre.lureau
2021-03-12 10:00 ` [PATCH 05/27] ui: factor out qemu_console_set_display_gl_ctx() marcandre.lureau
2021-03-12 15:34   ` Philippe Mathieu-Daudé
2021-03-12 10:00 ` [PATCH 06/27] ui: associate GL context outside of display listener registration marcandre.lureau
2021-03-12 10:00 ` [PATCH 07/27] ui: make gl_block use a counter marcandre.lureau
2021-03-12 10:12   ` Philippe Mathieu-Daudé
2021-03-12 11:21     ` Marc-André Lureau
2021-03-12 10:00 ` [PATCH 08/27] ui: add a gl-unblock warning timer marcandre.lureau
2021-03-12 10:00 ` [PATCH 09/27] ui: simplify gl unblock & flush marcandre.lureau
2021-03-12 10:00 ` [PATCH 10/27] ui: dispatch GL events to all listeners marcandre.lureau
2021-03-12 10:00 ` [PATCH 11/27] ui: split the GL context in a different object marcandre.lureau
2021-03-12 10:00 ` [PATCH 12/27] ui: move qemu_spice_fill_device_address to ui/util.c marcandre.lureau
2021-03-12 10:00 ` [PATCH 13/27] console: save current scanout details marcandre.lureau
2021-03-12 10:00 ` [PATCH 14/27] ui: add a D-Bus display backend marcandre.lureau
2021-03-12 11:03   ` Gerd Hoffmann
2021-03-12 11:27     ` Marc-André Lureau
2021-03-12 15:16       ` Gerd Hoffmann
2021-03-12 10:00 ` [PATCH 15/27] audio: add dbusaudio backend marcandre.lureau
2021-03-12 10:00 ` [PATCH 16/27] vhost-user-gpu: add vg_send_disable_scanout() marcandre.lureau
2021-03-12 10:00 ` [PATCH 17/27] vhost-user-gpu: add vg_send_scanout_dmabuf() marcandre.lureau
2021-03-12 10:00 ` [PATCH 18/27] vhost-user-gpu: add vg_send_dmabuf_update() marcandre.lureau
2021-03-12 10:01 ` [PATCH 19/27] vhost-user-gpu: add vg_send_scanout() marcandre.lureau
2021-03-12 10:01 ` [PATCH 20/27] vhost-user-gpu: add vg_send_cursor_update() marcandre.lureau
2021-03-12 10:01 ` [PATCH 21/27] vhost-user-gpu: add vg_send_cursor_pos() marcandre.lureau
2021-03-12 10:01 ` [PATCH 22/27] vhost-user-gpu: add vg_send_update() marcandre.lureau
2021-03-12 10:01 ` [PATCH 23/27] vhost-user: add VHOST_USER_GPU_QEMU_DBUS_LISTENER marcandre.lureau
2021-03-12 10:01 ` [PATCH 24/27] ui: add GraphicHwOps.register_dbus_listener() marcandre.lureau
2021-03-12 10:01 ` [PATCH 25/27] vhost-user-gpu: implement register_dbus_listener() marcandre.lureau
2021-03-12 10:01 ` [PATCH 26/27] vhost-user-gpu: check the PIXMAN format is supported marcandre.lureau
2021-03-12 10:01 ` [PATCH 27/27] vhost-user-gpu: implement GPU_QEMU_DBUS_LISTENER marcandre.lureau
2021-03-12 11:16 ` [PATCH 00/27] Add D-Bus display backend no-reply
2021-03-12 11:18 ` Gerd Hoffmann
2021-03-12 11:29   ` Marc-André Lureau
2021-03-12 14:37     ` Gerd Hoffmann
2021-03-12 14:57       ` Marc-André Lureau

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.