diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index e062f721d76..ec035c427db 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -33,11 +33,10 @@
 
 struct _DBusDisplayConsole {
     GDBusObjectSkeleton parent_instance;
-    DisplayChangeListener dcl;
 
     DBusDisplay *display;
     QemuConsole *con;
-    GHashTable *listeners;
+    DBusDisplayListener *listener;
     QemuDBusDisplay1Console *iface;
 
     QemuDBusDisplay1Keyboard *iface_kbd;
@@ -54,7 +53,7 @@ G_DEFINE_TYPE(DBusDisplayConsole,
               dbus_display_console,
               G_TYPE_DBUS_OBJECT_SKELETON)
 
-static void
+void
 dbus_display_console_set_size(DBusDisplayConsole *ddc,
                               uint32_t width, uint32_t height)
 {
@@ -64,78 +63,9 @@ dbus_display_console_set_size(DBusDisplayConsole *ddc,
                  NULL);
 }
 
-static void
-dbus_gfx_switch(DisplayChangeListener *dcl,
-                struct DisplaySurface *new_surface)
-{
-    DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
-
-    dbus_display_console_set_size(ddc,
-                                  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 *ddc = container_of(dcl, DBusDisplayConsole, dcl);
-
-    dbus_display_console_set_size(ddc, w, h);
-}
-
-static void
-dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl,
-                       QemuDmaBuf *dmabuf)
-{
-    DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
-
-    dbus_display_console_set_size(ddc,
-                                  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 *ddc = DBUS_DISPLAY_CONSOLE(object);
-
-    ddc->listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                            NULL, g_object_unref);
-    ddc->dcl.ops = &dbus_console_dcl_ops;
 }
 
 static void
@@ -143,10 +73,10 @@ dbus_display_console_dispose(GObject *object)
 {
     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
 
-    unregister_displaychangelistener(&ddc->dcl);
     g_clear_object(&ddc->iface_kbd);
     g_clear_object(&ddc->iface);
-    g_clear_pointer(&ddc->listeners, g_hash_table_unref);
+    dbus_display_listener_unref_all_connections(ddc->listener);
+    g_clear_object(&ddc->listener);
     g_clear_pointer(&ddc->kbd, qkbd_state_free);
 
     G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object);
@@ -161,14 +91,14 @@ dbus_display_console_class_init(DBusDisplayConsoleClass *klass)
 }
 
 static void
-listener_vanished_cb(DBusDisplayListener *listener)
+listener_vanished_cb(DBusDisplayListenerConnection *ddlc)
 {
-    DBusDisplayConsole *ddc = dbus_display_listener_get_console(listener);
-    const char *name = dbus_display_listener_get_bus_name(listener);
+    DBusDisplayConsole *ddc = dbus_display_listener_connection_get_console(ddlc);
+    const char *name = dbus_display_listener_connection_get_bus_name(ddlc);
 
     trace_dbus_listener_vanished(name);
 
-    g_hash_table_remove(ddc->listeners, name);
+    g_object_unref(ddlc);
     qkbd_state_lift_all_keys(ddc->kbd);
 }
 
@@ -211,15 +141,15 @@ dbus_console_register_listener(DBusDisplayConsole *ddc,
                                GVariant *arg_listener)
 {
     const char *sender = g_dbus_method_invocation_get_sender(invocation);
-    GDBusConnection *listener_conn;
+    GDBusConnection *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;
+    DBusDisplayListenerConnection *listener_conn;
     int fd;
 
-    if (sender && g_hash_table_contains(ddc->listeners, sender)) {
+    if (sender && dbus_display_listener_has_connection(ddc->listener, sender)) {
         g_dbus_method_invocation_return_error(
             invocation,
             DBUS_DISPLAY_ERROR,
@@ -254,7 +184,7 @@ dbus_console_register_listener(DBusDisplayConsole *ddc,
     qemu_dbus_display1_console_complete_register_listener(
         ddc->iface, invocation, NULL);
 
-    listener_conn = g_dbus_connection_new_sync(
+    conn = g_dbus_connection_new_sync(
         G_IO_STREAM(socket_conn),
         guid,
         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
@@ -264,16 +194,15 @@ dbus_console_register_listener(DBusDisplayConsole *ddc,
         return DBUS_METHOD_INVOCATION_HANDLED;
     }
 
-    listener = dbus_display_listener_new(sender, listener_conn, ddc);
-    if (!listener) {
+    listener_conn = dbus_display_listener_add_connection(ddc->listener,
+                                                         sender, conn);
+    if (!listener_conn) {
         return DBUS_METHOD_INVOCATION_HANDLED;
     }
 
-    g_hash_table_insert(ddc->listeners,
-                        (gpointer)dbus_display_listener_get_bus_name(listener),
-                        listener);
-    g_object_connect(listener_conn,
-                     "swapped-signal::closed", listener_vanished_cb, listener,
+    g_object_connect(conn,
+                     "swapped-signal::closed", listener_vanished_cb,
+                     listener_conn,
                      NULL);
 
     trace_dbus_registered_listener(sender);
@@ -489,7 +418,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
         G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse));
 
-    register_displaychangelistener(&ddc->dcl);
+    ddc->listener = dbus_display_listener_new(ddc);
     ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change;
     qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
 
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 81c119b13a2..e4242d69de2 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -31,18 +31,36 @@
 #include "ui/egl-context.h"
 #include "trace.h"
 
-struct _DBusDisplayListener {
+struct _DBusDisplayListenerConnection {
     GObject parent;
 
     char *bus_name;
-    DBusDisplayConsole *console;
+    DBusDisplayListener *listener;
     GDBusConnection *conn;
 
     QemuDBusDisplay1Listener *proxy;
+};
+
+G_DEFINE_TYPE(DBusDisplayListenerConnection,
+              dbus_display_listener_connection,
+              G_TYPE_OBJECT)
+
+struct _DBusDisplayListener {
+    GObject parent;
+
+    GHashTable *conns;
+    DBusDisplayConsole *console;
 
     DisplayChangeListener dcl;
     DisplaySurface *ds;
     QemuGLShader *gls;
+    GUnixFDList *dmabuf_fd_list;
+    uint32_t dmabuf_width;
+    uint32_t dmabuf_height;
+    uint32_t dmabuf_stride;
+    uint32_t dmabuf_fourcc;
+    uint64_t dmabuf_modifier;
+    bool dmabuf_y0_top;
     int gl_updates;
 };
 
@@ -53,65 +71,98 @@ static void dbus_update_gl_cb(GObject *source_object,
                            gpointer user_data)
 {
     g_autoptr(GError) err = NULL;
-    DBusDisplayListener *ddl = user_data;
+    DBusDisplayListenerConnection *ddlc = user_data;
 
-    if (!qemu_dbus_display1_listener_call_update_dmabuf_finish(ddl->proxy,
+    if (!qemu_dbus_display1_listener_call_update_dmabuf_finish(ddlc->proxy,
                                                                res, &err)) {
         error_report("Failed to call update: %s", err->message);
     }
 
-    graphic_hw_gl_block(ddl->dcl.con, false);
-    g_object_unref(ddl);
+    graphic_hw_gl_block(ddlc->listener->dcl.con, false);
+    g_object_unref(ddlc);
 }
 
 static void dbus_call_update_gl(DBusDisplayListener *ddl,
                                 int x, int y, int w, int h)
 {
-    graphic_hw_gl_block(ddl->dcl.con, true);
-    glFlush();
-    qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy,
-        x, y, w, h,
-        G_DBUS_CALL_FLAGS_NONE,
-        DBUS_DEFAULT_TIMEOUT, NULL,
-        dbus_update_gl_cb,
-        g_object_ref(ddl));
+    GHashTableIter iter;
+    gpointer ddlc;
+
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        graphic_hw_gl_block(ddl->dcl.con, true);
+        glFlush();
+        qemu_dbus_display1_listener_call_update_dmabuf(
+            ((DBusDisplayListenerConnection *)ddlc)->proxy,
+            x, y, w, h,
+            G_DBUS_CALL_FLAGS_NONE,
+            DBUS_DEFAULT_TIMEOUT, NULL,
+            dbus_update_gl_cb,
+            g_object_ref(ddlc));
+    }
 }
 
 static void dbus_scanout_disable(DisplayChangeListener *dcl)
 {
     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+    GHashTableIter iter;
+    gpointer ddlc;
 
     ddl->ds = NULL;
-    qemu_dbus_display1_listener_call_disable(
-        ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        qemu_dbus_display1_listener_call_disable(
+            ((DBusDisplayListenerConnection *)ddlc)->proxy,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
 }
 
 static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
                                 QemuDmaBuf *dmabuf)
 {
     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+    GHashTableIter iter;
+    gpointer ddlc;
     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) {
+    if (ddl->dmabuf_fd_list) {
+        g_clear_object(&ddl->dmabuf_fd_list);
+    }
+
+    ddl->dmabuf_fd_list = g_unix_fd_list_new();
+    if (g_unix_fd_list_append(ddl->dmabuf_fd_list, dmabuf->fd, &err) != 0) {
+        g_clear_object(&ddl->dmabuf_fd_list);
         error_report("Failed to setup dmabuf fdlist: %s", err->message);
         return;
     }
 
-    qemu_dbus_display1_listener_call_scanout_dmabuf(
-        ddl->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);
+    ddl->dmabuf_width = dmabuf->width;
+    ddl->dmabuf_height = dmabuf->height;
+    ddl->dmabuf_stride = dmabuf->stride;
+    ddl->dmabuf_fourcc = dmabuf->fourcc;
+    ddl->dmabuf_modifier = dmabuf->modifier;
+    ddl->dmabuf_y0_top = dmabuf->y0_top;
+
+    dbus_display_console_set_size(ddl->console, dmabuf->width, dmabuf->height);
+
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        qemu_dbus_display1_listener_call_scanout_dmabuf(
+            ((DBusDisplayListenerConnection *)ddlc)->proxy,
+            g_variant_new_handle(0),
+            ddl->dmabuf_width,
+            ddl->dmabuf_height,
+            ddl->dmabuf_stride,
+            ddl->dmabuf_fourcc,
+            ddl->dmabuf_modifier,
+            ddl->dmabuf_y0_top,
+            G_DBUS_CALL_FLAGS_NONE,
+            -1,
+            ddl->dmabuf_fd_list,
+            NULL, NULL, NULL);
+    }
 }
 
 static void dbus_scanout_texture(DisplayChangeListener *dcl,
@@ -150,11 +201,16 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
     DisplaySurface *ds;
     GVariant *v_data = NULL;
     egl_fb cursor_fb;
+    GHashTableIter iter;
+    gpointer ddlc;
 
     if (!dmabuf) {
-        qemu_dbus_display1_listener_call_mouse_set(
-            ddl->proxy, 0, 0, false,
-            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        g_hash_table_iter_init(&iter, ddl->conns);
+        while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+            qemu_dbus_display1_listener_call_mouse_set(
+                ((DBusDisplayListenerConnection *)ddlc)->proxy, 0, 0, false,
+                G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        }
         return;
     }
 
@@ -174,28 +230,37 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
         TRUE,
         (GDestroyNotify)qemu_free_displaysurface,
         ds);
-    qemu_dbus_display1_listener_call_cursor_define(
-        ddl->proxy,
-        surface_width(ds),
-        surface_height(ds),
-        hot_x,
-        hot_y,
-        v_data,
-        G_DBUS_CALL_FLAGS_NONE,
-        -1,
-        NULL,
-        NULL,
-        NULL);
+
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        qemu_dbus_display1_listener_call_cursor_define(
+            ((DBusDisplayListenerConnection *)ddlc)->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 *ddl = container_of(dcl, DBusDisplayListener, dcl);
+    GHashTableIter iter;
+    gpointer ddlc;
 
-    qemu_dbus_display1_listener_call_mouse_set(
-        ddl->proxy, pos_x, pos_y, true,
-        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        qemu_dbus_display1_listener_call_mouse_set(
+            ((DBusDisplayListenerConnection *)ddlc)->proxy,
+            pos_x, pos_y, true, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
 }
 
 static void dbus_release_dmabuf(DisplayChangeListener *dcl,
@@ -254,6 +319,8 @@ static void dbus_gfx_update(DisplayChangeListener *dcl,
     pixman_image_t *img;
     GVariant *v_data;
     size_t stride;
+    GHashTableIter iter;
+    gpointer ddlc;
 
     assert(ddl->ds);
     stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
@@ -273,11 +340,16 @@ static void dbus_gfx_update(DisplayChangeListener *dcl,
         TRUE,
         (GDestroyNotify)pixman_image_unref,
         img);
-    qemu_dbus_display1_listener_call_update(ddl->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);
+
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        qemu_dbus_display1_listener_call_update(
+            ((DBusDisplayListenerConnection *)ddlc)->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,
@@ -293,6 +365,8 @@ static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
         int width = surface_width(ddl->ds);
         int height = surface_height(ddl->ds);
 
+        dbus_display_console_set_size(ddl->console, width, height);
+
         surface_gl_create_texture(ddl->gls, ddl->ds);
         /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
         dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
@@ -305,6 +379,8 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl,
 {
     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
     GVariant *v_data = NULL;
+    GHashTableIter iter;
+    gpointer ddlc;
 
     ddl->ds = new_surface;
     if (!ddl->ds) {
@@ -312,6 +388,10 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl,
         return;
     }
 
+    dbus_display_console_set_size(ddl->console,
+                                  surface_width(ddl->ds),
+                                  surface_height(ddl->ds));
+
     v_data = g_variant_new_from_data(
         G_VARIANT_TYPE("ay"),
         surface_data(ddl->ds),
@@ -319,23 +399,34 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl,
         TRUE,
         (GDestroyNotify)pixman_image_unref,
         pixman_image_ref(ddl->ds->image));
-    qemu_dbus_display1_listener_call_scanout(ddl->proxy,
-        surface_width(ddl->ds),
-        surface_height(ddl->ds),
-        surface_stride(ddl->ds),
-        surface_format(ddl->ds),
-        v_data,
-        G_DBUS_CALL_FLAGS_NONE,
-        DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        qemu_dbus_display1_listener_call_scanout(
+            ((DBusDisplayListenerConnection *)ddlc)->proxy,
+            surface_width(ddl->ds),
+            surface_height(ddl->ds),
+            surface_stride(ddl->ds),
+            surface_format(ddl->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 *ddl = container_of(dcl, DBusDisplayListener, dcl);
+    GHashTableIter iter;
+    gpointer ddlc;
 
-    qemu_dbus_display1_listener_call_mouse_set(
-        ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        qemu_dbus_display1_listener_call_mouse_set(
+            ((DBusDisplayListenerConnection *)ddlc)->proxy,
+            x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
 }
 
 static void dbus_cursor_define(DisplayChangeListener *dcl,
@@ -343,6 +434,8 @@ static void dbus_cursor_define(DisplayChangeListener *dcl,
 {
     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
     GVariant *v_data = NULL;
+    GHashTableIter iter;
+    gpointer ddlc;
 
     cursor_get(c);
     v_data = g_variant_new_from_data(
@@ -353,18 +446,21 @@ static void dbus_cursor_define(DisplayChangeListener *dcl,
         (GDestroyNotify)cursor_put,
         c);
 
-    qemu_dbus_display1_listener_call_cursor_define(
-        ddl->proxy,
-        c->width,
-        c->height,
-        c->hot_x,
-        c->hot_y,
-        v_data,
-        G_DBUS_CALL_FLAGS_NONE,
-        -1,
-        NULL,
-        NULL,
-        NULL);
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        qemu_dbus_display1_listener_call_cursor_define(
+            ((DBusDisplayListenerConnection *)ddlc)->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 = {
@@ -400,9 +496,8 @@ dbus_display_listener_dispose(GObject *object)
     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
 
     unregister_displaychangelistener(&ddl->dcl);
-    g_clear_object(&ddl->conn);
-    g_clear_pointer(&ddl->bus_name, g_free);
-    g_clear_object(&ddl->proxy);
+    g_clear_object(&ddl->dmabuf_fd_list);
+    g_clear_pointer(&ddl->conns, g_hash_table_unref);
     g_clear_pointer(&ddl->gls, qemu_gl_fini_shader);
 
     G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
@@ -435,46 +530,146 @@ dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
 static void
 dbus_display_listener_init(DBusDisplayListener *ddl)
 {
+    ddl->conns = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
 }
 
-const char *
-dbus_display_listener_get_bus_name(DBusDisplayListener *ddl)
-{
-    return ddl->bus_name ?: "p2p";
-}
-
-DBusDisplayConsole *
-dbus_display_listener_get_console(DBusDisplayListener *ddl)
+DBusDisplayListenerConnection *
+dbus_display_listener_add_connection(DBusDisplayListener *ddl,
+                                     const char *bus_name,
+                                     GDBusConnection *conn)
 {
-    return ddl->console;
-}
-
-DBusDisplayListener *
-dbus_display_listener_new(const char *bus_name,
-                          GDBusConnection *conn,
-                          DBusDisplayConsole *console)
-{
-    DBusDisplayListener *ddl;
-    QemuConsole *con;
+    DBusDisplayListenerConnection *ddlc;
+    QemuDBusDisplay1Listener *proxy;
     g_autoptr(GError) err = NULL;
 
-    ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL);
-    ddl->proxy =
+    proxy =
         qemu_dbus_display1_listener_proxy_new_sync(conn,
             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
             NULL,
             "/org/qemu/Display1/Listener",
             NULL,
             &err);
-    if (!ddl->proxy) {
+    if (!proxy) {
         error_report("Failed to setup proxy: %s", err->message);
         g_object_unref(conn);
-        g_object_unref(ddl);
         return NULL;
     }
 
-    ddl->bus_name = g_strdup(bus_name);
-    ddl->conn = conn;
+    ddlc = g_object_new(DBUS_DISPLAY_TYPE_LISTENER_CONNECTION, NULL);
+    ddlc->listener = g_object_ref(ddl);
+    ddlc->proxy = proxy;
+    ddlc->bus_name = g_strdup(bus_name);
+    ddlc->conn = conn;
+
+    g_hash_table_insert(ddl->conns, ddlc->bus_name, ddlc);
+
+    if (display_opengl) {
+        if (ddl->dmabuf_fd_list) {
+            qemu_dbus_display1_listener_call_scanout_dmabuf(
+                ddlc->proxy,
+                g_variant_new_handle(0),
+                ddl->dmabuf_width,
+                ddl->dmabuf_height,
+                ddl->dmabuf_stride,
+                ddl->dmabuf_fourcc,
+                ddl->dmabuf_modifier,
+                ddl->dmabuf_y0_top,
+                G_DBUS_CALL_FLAGS_NONE,
+                -1,
+                ddl->dmabuf_fd_list,
+                NULL, NULL, NULL);
+        }
+    } else {
+        if (ddl->ds) {
+            GVariant *v_data = NULL;
+
+            v_data = g_variant_new_from_data(
+                G_VARIANT_TYPE("ay"),
+                surface_data(ddl->ds),
+                surface_stride(ddl->ds) * surface_height(ddl->ds),
+                TRUE,
+                (GDestroyNotify)pixman_image_unref,
+                pixman_image_ref(ddl->ds->image));
+
+            qemu_dbus_display1_listener_call_scanout(
+                ((DBusDisplayListenerConnection *)ddlc)->proxy,
+                surface_width(ddl->ds),
+                surface_height(ddl->ds),
+                surface_stride(ddl->ds),
+                surface_format(ddl->ds),
+                v_data,
+                G_DBUS_CALL_FLAGS_NONE,
+                DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+        }
+    }
+
+    return ddlc;
+}
+
+bool
+dbus_display_listener_has_connection(DBusDisplayListener *ddl,
+                                     const char *bus_name)
+{
+    return g_hash_table_contains(ddl->conns, bus_name);
+}
+
+void
+dbus_display_listener_unref_all_connections(DBusDisplayListener *ddl)
+{
+    GHashTableIter iter;
+    gpointer ddlc;
+
+    g_hash_table_iter_init(&iter, ddl->conns);
+    while (g_hash_table_iter_next(&iter, NULL, &ddlc)) {
+        g_object_unref(ddlc);
+    }
+}
+
+static void
+dbus_display_listener_connection_dispose(GObject *object)
+{
+    DBusDisplayListenerConnection *ddlc =
+        DBUS_DISPLAY_LISTENER_CONNECTION(object);
+
+    g_hash_table_remove(ddlc->listener->conns, ddlc->bus_name);
+    g_clear_object(&ddlc->listener);
+    g_clear_object(&ddlc->conn);
+    g_clear_pointer(&ddlc->bus_name, g_free);
+    g_clear_object(&ddlc->proxy);
+
+    G_OBJECT_CLASS(dbus_display_listener_connection_parent_class)->dispose(object);
+}
+
+static void
+dbus_display_listener_connection_class_init(DBusDisplayListenerConnectionClass *klass)
+{
+    G_OBJECT_CLASS(klass)->dispose = dbus_display_listener_connection_dispose;
+}
+
+static void
+dbus_display_listener_connection_init(DBusDisplayListenerConnection *ddl)
+{
+}
+
+const char *
+dbus_display_listener_connection_get_bus_name(DBusDisplayListenerConnection *ddlc)
+{
+    return ddlc->bus_name ?: "p2p";
+}
+
+DBusDisplayConsole *
+dbus_display_listener_connection_get_console(DBusDisplayListenerConnection *ddlc)
+{
+    return ddlc->listener->console;
+}
+
+DBusDisplayListener *
+dbus_display_listener_new(DBusDisplayConsole *console)
+{
+    DBusDisplayListener *ddl;
+    QemuConsole *con;
+
+    ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL);
     ddl->console = console;
 
     con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
diff --git a/ui/dbus.h b/ui/dbus.h
index 64c77cab444..fd24b299bbf 100644
--- a/ui/dbus.h
+++ b/ui/dbus.h
@@ -76,9 +76,19 @@ G_DECLARE_FINAL_TYPE(DBusDisplayConsole,
 DBusDisplayConsole *
 dbus_display_console_new(DBusDisplay *display, QemuConsole *con);
 
+void
+dbus_display_console_set_size(DBusDisplayConsole *ddc,
+                              uint32_t width, uint32_t height);
+
 int
 dbus_display_console_get_index(DBusDisplayConsole *ddc);
 
+G_DECLARE_FINAL_TYPE(DBusDisplayListenerConnection,
+                     dbus_display_listener_connection,
+                     DBUS_DISPLAY,
+                     LISTENER_CONNECTION,
+                     GObject)
+
 #define DBUS_DISPLAY_TYPE_LISTENER dbus_display_listener_get_type()
 G_DECLARE_FINAL_TYPE(DBusDisplayListener,
                      dbus_display_listener,
@@ -86,16 +96,28 @@ G_DECLARE_FINAL_TYPE(DBusDisplayListener,
                      LISTENER,
                      GObject)
 
+#define DBUS_DISPLAY_TYPE_LISTENER_CONNECTION \
+    dbus_display_listener_connection_get_type()
 DBusDisplayListener *
-dbus_display_listener_new(const char *bus_name,
-                          GDBusConnection *conn,
-                          DBusDisplayConsole *console);
+dbus_display_listener_new(DBusDisplayConsole *console);
+
+DBusDisplayListenerConnection *
+dbus_display_listener_add_connection(DBusDisplayListener *ddl,
+                                     const char *bus_name,
+                                     GDBusConnection *conn);
+
+bool
+dbus_display_listener_has_connection(DBusDisplayListener *ddl,
+                                     const char *bus_name);
+
+void
+dbus_display_listener_unref_all_connections(DBusDisplayListener *ddl);
 
 DBusDisplayConsole *
-dbus_display_listener_get_console(DBusDisplayListener *ddl);
+dbus_display_listener_connection_get_console(DBusDisplayListenerConnection *ddlc);
 
 const char *
-dbus_display_listener_get_bus_name(DBusDisplayListener *ddl);
+dbus_display_listener_connection_get_bus_name(DBusDisplayListenerConnection *ddlc);
 
 extern const DisplayChangeListenerOps dbus_gl_dcl_ops;
 extern const DisplayChangeListenerOps dbus_dcl_ops;
