Commit e12c1693bd for qemu.org

commit e12c1693bde22b35e008d60dda4babefd5c254ed
Author: Christian Quante <christian@quante.one>
Date:   Thu Oct 22 15:43:32 2026 +0200

    ui/dbus: send a Scanout before the first Update of a new surface

    Without OpenGL and without shared memory, the D-Bus listener sends a
    Scanout only for an update that covers the whole surface, and
    dbus_gfx_switch() only records the new surface. So when a device draws
    a new surface in parts, the client gets Updates for a size that no
    Scanout has given it. VGA text mode is such a device: vga_draw_text()
    sends one update per row of characters, so a text mode that changes its
    size does not send a Scanout. According to ui/dbus-display1.xml, Update
    is only called after a Scanout call, and Scanout is the call that
    resizes the display.

    The client can learn the new size only from the Width and Height
    properties of the Console. They come on a different connection, and
    the generated skeleton sends PropertiesChanged from an idle callback,
    after the redraw. A client that sets up a new buffer when this signal
    comes loses the rows that came before it. VGA text mode does not send
    them again: it redraws only the characters that change, and all of
    them only after changes such as a new font or palette, or with blinking
    enabled.

    The Windows NT 4.0 setup shows this. With blinking off, it changes the
    character height from 16 to 12 lines (720x400 to 720x396), and in a
    D-Bus client large parts of its blue background stayed black.

    With shared memory, dbus_scanout_map() already sends ScanoutMap with the
    new size at the first update after a surface switch. Do the same without
    shared memory: the first update after a switch sends a Scanout of the
    whole surface.

    Signed-off-by: Christian Quante <christian@quante.one>
    Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
    Message-ID: <20261022134335.12241-2-christian@quante.one>

diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 624d0fb71d..26ed84d700 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -71,6 +71,8 @@ struct _DBusDisplayListener {
     DisplayChangeListener dcl;
     DisplaySurface *ds;
     enum share_kind ds_share;
+    /* true until a Scanout or ScanoutMap of ds has been sent */
+    bool ds_scanout_pending;

     bool ds_mapped;
     bool can_share_map;
@@ -480,6 +482,7 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl)
     }

     ddl->ds_share = SHARE_KIND_MAPPED;
+    ddl->ds_scanout_pending = false;

     return true;
 }
@@ -591,6 +594,7 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl)
     }

     ddl->ds_share = SHARE_KIND_MAPPED;
+    ddl->ds_scanout_pending = false;

     return true;
 }
@@ -844,6 +848,7 @@ static void ddl_scanout(DBusDisplayListener *ddl)
         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);
+    ddl->ds_scanout_pending = false;
 }

 static void dbus_gfx_update(DisplayChangeListener *dcl,
@@ -872,7 +877,9 @@ static void dbus_gfx_update(DisplayChangeListener *dcl,
         return;
     }

-    if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
+    if (ddl->ds_scanout_pending ||
+        (x == 0 && y == 0 &&
+         w == surface_width(ddl->ds) && h == surface_height(ddl->ds))) {
         return ddl_scanout(ddl);
     }

@@ -907,6 +914,7 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl,

     ddl->ds = new_surface;
     ddl->ds_share = SHARE_KIND_NONE;
+    ddl->ds_scanout_pending = true;
 }

 static void dbus_mouse_set(DisplayChangeListener *dcl,