All of lore.kernel.org
 help / color / mirror / Atom feed
* [PULL v2 00/15] UI patches
@ 2026-04-28 15:07 marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 01/15] ui: move FONT_WIDTH/HEIGHT to vgafont.h marcandre.lureau
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

The following changes since commit 759c456b1d22fe4083c8b384da27d3f56fd53f82:

  Merge tag 'linux-user-next-pull-request' of https://github.com/hdeller/qemu-hppa into staging (2026-04-27 12:57:33 -0400)

are available in the Git repository at:

  https://gitlab.com/marcandre.lureau/qemu.git tags/ui-pull-request

for you to fetch changes up to 335e32cbd0a24f13e326d96dd30318cd78740c60:

  ui/gtk: Turn clipboard flag into runtime option (2026-04-28 18:53:59 +0400)

----------------------------------------------------------------
UI-related fixes and cleanups

----------------------------------------------------------------

Akihiko Odaki (3):
  audio: Avoid unsigned sample wraparound
  audio: Clamp unsigned sample conversion
  audio: Use unsigned PCM bias

Jindřich Makovička (2):
  ui/gtk: Use non-blocking clipboard retrieval
  ui/gtk: Turn clipboard flag into runtime option

Marc-André Lureau (10):
  ui: move FONT_WIDTH/HEIGHT to vgafont.h
  ui: move DisplaySurface functions to display-surface.c
  ui: make qemu_default_pixelformat() static inline
  ui: make unregister_displaychangelistener() skip unregistered
  ui: minor code simplification
  ui: rename init_keyboard_layout->kbd_layout_new()
  ui/console: remove qemu_console_is_visible()
  ui/console: return completion status from gfx_update callback
  ui/console: move console_handle_touch_event() to input
  ui/dbus: fix warning for clients without "Interfaces" property

 meson.build                   |   4 -
 qapi/ui.json                  |   6 +-
 audio/mixeng_template.h       |  14 +--
 hw/display/qxl.h              |   2 +-
 include/ui/console.h          |  26 ++---
 include/ui/input.h            |  15 +++
 include/ui/qemu-pixman.h      |   8 ++
 include/ui/surface.h          |   2 -
 ui/console-priv.h             |   6 -
 ui/keymaps.h                  |   4 +-
 ui/vgafont.h                  |   6 +-
 hw/arm/musicpal.c             |   3 +-
 hw/display/artist.c           |   4 +-
 hw/display/bcm2835_fb.c       |   7 +-
 hw/display/bochs-display.c    |   6 +-
 hw/display/cg3.c              |   5 +-
 hw/display/dm163.c            |   4 +-
 hw/display/exynos4210_fimd.c  |   6 +-
 hw/display/g364fb.c           |   9 +-
 hw/display/imx6ul_lcdif.c     |   7 +-
 hw/display/jazz_led.c         |   6 +-
 hw/display/macfb.c            |   6 +-
 hw/display/next-fb.c          |   4 +-
 hw/display/omap_lcdc.c        |  14 ++-
 hw/display/pl110.c            |   5 +-
 hw/display/qxl-render.c       |   6 +-
 hw/display/qxl.c              |   7 +-
 hw/display/ramfb-standalone.c |   4 +-
 hw/display/sm501.c            |   8 +-
 hw/display/ssd0303.c          |  10 +-
 hw/display/ssd0323.c          |  11 +-
 hw/display/tcx.c              |   6 +-
 hw/display/vga.c              |   4 +-
 hw/display/virtio-gpu-base.c  |   3 +-
 hw/display/virtio-vga.c       |   6 +-
 hw/display/vmware_vga.c       |   7 +-
 hw/display/xenfb.c            |   6 +-
 hw/display/xlnx_dp.c          |  10 +-
 hw/vfio/display.c             |  17 +--
 ui/console-vc.c               |   1 +
 ui/console.c                  | 206 +---------------------------------
 ui/curses.c                   |   4 +-
 ui/dbus-console.c             |   6 +-
 ui/dbus-listener.c            |   4 +-
 ui/display-surface.c          | 107 ++++++++++++++++++
 ui/gtk-clipboard.c            |  53 ++++++---
 ui/gtk.c                      |  16 +--
 ui/input-barrier.c            |   4 +-
 ui/input.c                    |  65 +++++++++++
 ui/keymaps.c                  |   4 +-
 ui/sdl2.c                     |   8 +-
 ui/vnc.c                      |  12 +-
 hw/display/apple-gfx.m        |  10 +-
 meson_options.txt             |   7 --
 qemu-options.hx               |   9 +-
 scripts/meson-buildoptions.sh |   3 -
 ui/meson.build                |   5 +-
 57 files changed, 428 insertions(+), 380 deletions(-)
 create mode 100644 ui/display-surface.c

-- 
2.54.0



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

* [PULL v2 01/15] ui: move FONT_WIDTH/HEIGHT to vgafont.h
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 02/15] ui: move DisplaySurface functions to display-surface.c marcandre.lureau
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

Since those values are related to the VGA font, it make sense to move
them here.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/console-priv.h | 5 -----
 ui/vgafont.h      | 6 +++++-
 ui/console-vc.c   | 1 +
 ui/console.c      | 1 +
 4 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/ui/console-priv.h b/ui/console-priv.h
index 8bcdb79d914..39798c3e9d7 100644
--- a/ui/console-priv.h
+++ b/ui/console-priv.h
@@ -9,11 +9,6 @@
 #include "qemu/coroutine.h"
 #include "qemu/timer.h"
 
-#include "vgafont.h"
-
-#define FONT_HEIGHT 16
-#define FONT_WIDTH 8
-
 struct QemuConsole {
     Object parent;
 
diff --git a/ui/vgafont.h b/ui/vgafont.h
index 54aeeb7d192..d24b5d47a95 100644
--- a/ui/vgafont.h
+++ b/ui/vgafont.h
@@ -6,6 +6,10 @@
 
 #include <stdint.h>
 
-extern const uint8_t vgafont16[256 * 16];
+/* supports only vga 8x16 */
+#define FONT_WIDTH 8
+#define FONT_HEIGHT 16
+
+extern const uint8_t vgafont16[256 * FONT_HEIGHT];
 
 #endif
diff --git a/ui/console-vc.c b/ui/console-vc.c
index 107294af1cc..6163e21d2c6 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -10,6 +10,7 @@
 #include "qemu/option.h"
 #include "qemu/queue.h"
 #include "ui/console.h"
+#include "ui/vgafont.h"
 
 #include "pixman.h"
 #include "trace.h"
diff --git a/ui/console.c b/ui/console.c
index a7bd22515b9..9606da7b1a6 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -24,6 +24,7 @@
 
 #include "qemu/osdep.h"
 #include "ui/console.h"
+#include "ui/vgafont.h"
 #include "hw/core/qdev.h"
 #include "qapi/error.h"
 #include "qapi/qapi-commands-ui.h"
-- 
2.54.0



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

* [PULL v2 02/15] ui: move DisplaySurface functions to display-surface.c
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 01/15] ui: move FONT_WIDTH/HEIGHT to vgafont.h marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 03/15] ui: make qemu_default_pixelformat() static inline marcandre.lureau
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

Extract DisplaySurface creation and destruction functions from console.c
into their own file to reduce the size of console.c and improve code
organization.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/console.c         |  96 --------------------------------------
 ui/display-surface.c | 107 +++++++++++++++++++++++++++++++++++++++++++
 ui/meson.build       |   1 +
 3 files changed, 108 insertions(+), 96 deletions(-)
 create mode 100644 ui/display-surface.c

diff --git a/ui/console.c b/ui/console.c
index 9606da7b1a6..475a72aa08f 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -505,102 +505,6 @@ qemu_graphic_console_init(Object *obj)
 {
 }
 
-void qemu_displaysurface_set_share_handle(DisplaySurface *surface,
-                                          qemu_pixman_shareable handle,
-                                          uint32_t offset)
-{
-    assert(surface->share_handle == SHAREABLE_NONE);
-
-    surface->share_handle = handle;
-    surface->share_handle_offset = offset;
-
-}
-
-DisplaySurface *qemu_create_displaysurface(int width, int height)
-{
-    trace_displaysurface_create(width, height);
-
-    return qemu_create_displaysurface_from(
-        width, height,
-        PIXMAN_x8r8g8b8,
-        width * 4, NULL
-    );
-}
-
-DisplaySurface *qemu_create_displaysurface_from(int width, int height,
-                                                pixman_format_code_t format,
-                                                int linesize, uint8_t *data)
-{
-    DisplaySurface *surface = g_new0(DisplaySurface, 1);
-
-    trace_displaysurface_create_from(surface, width, height, format);
-    surface->share_handle = SHAREABLE_NONE;
-
-    if (data) {
-        surface->image = pixman_image_create_bits(format,
-                                                  width, height,
-                                                  (void *)data, linesize);
-    } else {
-        qemu_pixman_image_new_shareable(&surface->image,
-                                        &surface->share_handle,
-                                        "displaysurface",
-                                        format,
-                                        width,
-                                        height,
-                                        linesize,
-                                        &error_abort);
-        surface->flags = QEMU_ALLOCATED_FLAG;
-    }
-
-    assert(surface->image != NULL);
-    return surface;
-}
-
-DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
-{
-    DisplaySurface *surface = g_new0(DisplaySurface, 1);
-
-    trace_displaysurface_create_pixman(surface);
-    surface->share_handle = SHAREABLE_NONE;
-    surface->image = pixman_image_ref(image);
-
-    return surface;
-}
-
-DisplaySurface *qemu_create_placeholder_surface(int w, int h,
-                                                const char *msg)
-{
-    DisplaySurface *surface = qemu_create_displaysurface(w, h);
-#ifdef CONFIG_PIXMAN
-    pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK;
-    pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY;
-    pixman_image_t *glyph;
-    int len, x, y, i;
-
-    len = strlen(msg);
-    x = (w / FONT_WIDTH  - len) / 2;
-    y = (h / FONT_HEIGHT - 1)   / 2;
-    for (i = 0; i < len; i++) {
-        glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
-        qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
-                                 x+i, y, FONT_WIDTH, FONT_HEIGHT);
-        qemu_pixman_image_unref(glyph);
-    }
-#endif
-    surface->flags |= QEMU_PLACEHOLDER_FLAG;
-    return surface;
-}
-
-void qemu_free_displaysurface(DisplaySurface *surface)
-{
-    if (surface == NULL) {
-        return;
-    }
-    trace_displaysurface_free(surface);
-    qemu_pixman_image_unref(surface->image);
-    g_free(surface);
-}
-
 bool console_has_gl(QemuConsole *con)
 {
     return con->gl != NULL;
diff --git a/ui/display-surface.c b/ui/display-surface.c
new file mode 100644
index 00000000000..38e408513cc
--- /dev/null
+++ b/ui/display-surface.c
@@ -0,0 +1,107 @@
+/*
+ * QEMU graphical console surface helper
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#include "qemu/osdep.h"
+#include "ui/console.h"
+#include "ui/vgafont.h"
+#include "trace.h"
+
+void qemu_displaysurface_set_share_handle(DisplaySurface *surface,
+                                          qemu_pixman_shareable handle,
+                                          uint32_t offset)
+{
+    assert(surface->share_handle == SHAREABLE_NONE);
+
+    surface->share_handle = handle;
+    surface->share_handle_offset = offset;
+
+}
+
+DisplaySurface *qemu_create_displaysurface(int width, int height)
+{
+    trace_displaysurface_create(width, height);
+
+    return qemu_create_displaysurface_from(
+        width, height,
+        PIXMAN_x8r8g8b8,
+        width * 4, NULL
+    );
+}
+
+DisplaySurface *qemu_create_displaysurface_from(int width, int height,
+                                                pixman_format_code_t format,
+                                                int linesize, uint8_t *data)
+{
+    DisplaySurface *surface = g_new0(DisplaySurface, 1);
+
+    trace_displaysurface_create_from(surface, width, height, format);
+    surface->share_handle = SHAREABLE_NONE;
+
+    if (data) {
+        surface->image = pixman_image_create_bits(format,
+                                                  width, height,
+                                                  (void *)data, linesize);
+    } else {
+        qemu_pixman_image_new_shareable(&surface->image,
+                                        &surface->share_handle,
+                                        "displaysurface",
+                                        format,
+                                        width,
+                                        height,
+                                        linesize,
+                                        &error_abort);
+        surface->flags = QEMU_ALLOCATED_FLAG;
+    }
+
+    assert(surface->image != NULL);
+    return surface;
+}
+
+DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
+{
+    DisplaySurface *surface = g_new0(DisplaySurface, 1);
+
+    trace_displaysurface_create_pixman(surface);
+    surface->share_handle = SHAREABLE_NONE;
+    surface->image = pixman_image_ref(image);
+
+    return surface;
+}
+
+DisplaySurface *qemu_create_placeholder_surface(int w, int h,
+                                                const char *msg)
+{
+    DisplaySurface *surface = qemu_create_displaysurface(w, h);
+#ifdef CONFIG_PIXMAN
+    pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK;
+    pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY;
+    pixman_image_t *glyph;
+    int len, x, y, i;
+
+    len = strlen(msg);
+    x = (w / FONT_WIDTH  - len) / 2;
+    y = (h / FONT_HEIGHT - 1)   / 2;
+    for (i = 0; i < len; i++) {
+        glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
+        qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
+                                 x + i, y, FONT_WIDTH, FONT_HEIGHT);
+        qemu_pixman_image_unref(glyph);
+    }
+#endif
+    surface->flags |= QEMU_PLACEHOLDER_FLAG;
+    return surface;
+}
+
+void qemu_free_displaysurface(DisplaySurface *surface)
+{
+    if (surface == NULL) {
+        return;
+    }
+    trace_displaysurface_free(surface);
+    qemu_pixman_image_unref(surface->image);
+    g_free(surface);
+}
diff --git a/ui/meson.build b/ui/meson.build
index 4e533d30460..3d15ff96287 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -4,6 +4,7 @@ system_ss.add(files(
   'clipboard.c',
   'console.c',
   'cursor.c',
+  'display-surface.c',
   'dmabuf.c',
   'input-keymap.c',
   'input-legacy.c',
-- 
2.54.0



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

* [PULL v2 03/15] ui: make qemu_default_pixelformat() static inline
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 01/15] ui: move FONT_WIDTH/HEIGHT to vgafont.h marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 02/15] ui: move DisplaySurface functions to display-surface.c marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 04/15] ui: make unregister_displaychangelistener() skip unregistered marcandre.lureau
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

The function is a trivial wrapper around qemu_default_pixman_format()
and qemu_pixelformat_from_pixman(), so make it static inline in
qemu-pixman.h instead of a standalone function in console.c, allowing to
be easily reused.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/qemu-pixman.h | 8 ++++++++
 include/ui/surface.h     | 2 --
 ui/console.c             | 7 -------
 3 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index 2ca0ed7029c..4bc7a59698e 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -73,6 +73,14 @@ typedef struct PixelFormat {
 
 PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format);
 pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian);
+
+static inline PixelFormat qemu_default_pixelformat(int bpp)
+{
+    pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
+    PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
+    return pf;
+}
+
 pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format);
 uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman);
 int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian);
diff --git a/include/ui/surface.h b/include/ui/surface.h
index d2542d3ace5..8e5f3e35e8c 100644
--- a/include/ui/surface.h
+++ b/include/ui/surface.h
@@ -24,8 +24,6 @@ typedef struct DisplaySurface {
     uint32_t share_handle_offset;
 } DisplaySurface;
 
-PixelFormat qemu_default_pixelformat(int bpp);
-
 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
                                                 pixman_format_code_t format,
                                                 int linesize, uint8_t *data);
diff --git a/ui/console.c b/ui/console.c
index 475a72aa08f..ff561617657 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1390,13 +1390,6 @@ DisplaySurface *qemu_console_surface(QemuConsole *console)
     }
 }
 
-PixelFormat qemu_default_pixelformat(int bpp)
-{
-    pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
-    PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
-    return pf;
-}
-
 static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
 
 void qemu_display_register(QemuDisplay *ui)
-- 
2.54.0



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

* [PULL v2 04/15] ui: make unregister_displaychangelistener() skip unregistered
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (2 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 03/15] ui: make qemu_default_pixelformat() static inline marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 05/15] ui: minor code simplification marcandre.lureau
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

If the listener doesn't have associate ds / display state, it is already
unregistered.

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

diff --git a/ui/console.c b/ui/console.c
index ff561617657..8f65308958a 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -675,6 +675,9 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl)
 {
     DisplayState *ds = dcl->ds;
     trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
+    if (!ds) {
+        return;
+    }
     if (dcl->con) {
         dcl->con->dcls--;
     }
-- 
2.54.0



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

* [PULL v2 05/15] ui: minor code simplification
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (3 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 04/15] ui: make unregister_displaychangelistener() skip unregistered marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 06/15] ui: rename init_keyboard_layout->kbd_layout_new() marcandre.lureau
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

Drop memset() usage.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/sdl2.c | 8 ++++----
 ui/vnc.c  | 6 ++----
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/ui/sdl2.c b/ui/sdl2.c
index 987ad334bbe..5dd612d9a6a 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -600,10 +600,10 @@ static void handle_windowevent(SDL_Event *ev)
     switch (ev->window.event) {
     case SDL_WINDOWEVENT_RESIZED:
         {
-            QemuUIInfo info;
-            memset(&info, 0, sizeof(info));
-            info.width = ev->window.data1;
-            info.height = ev->window.data2;
+            QemuUIInfo info = {
+                .width = ev->window.data1,
+                .height = ev->window.data2,
+            };
             dpy_set_ui_info(scon->dcl.con, &info, true);
         }
         sdl2_redraw(scon);
diff --git a/ui/vnc.c b/ui/vnc.c
index ad8d58a23d7..16dbb0cd6c2 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -43,6 +43,7 @@
 #include "qapi/qapi-events-ui.h"
 #include "qapi/error.h"
 #include "qapi/qapi-commands-ui.h"
+#include "ui/console.h"
 #include "ui/input.h"
 #include "crypto/hash.h"
 #include "crypto/tlscreds.h"
@@ -2639,10 +2640,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
 
         trace_vnc_msg_client_set_desktop_size(vs, vs->ioc, w, h, screens);
         if (dpy_ui_info_supported(vs->vd->dcl.con)) {
-            QemuUIInfo info;
-            memset(&info, 0, sizeof(info));
-            info.width = w;
-            info.height = h;
+            QemuUIInfo info = { .width = w, .height = h };
             dpy_set_ui_info(vs->vd->dcl.con, &info, false);
             vnc_desktop_resize_ext(vs, 4 /* Request forwarded */);
         } else {
-- 
2.54.0



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

* [PULL v2 06/15] ui: rename init_keyboard_layout->kbd_layout_new()
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (4 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 05/15] ui: minor code simplification marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 07/15] ui/console: remove qemu_console_is_visible() marcandre.lureau
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

Use a more convential name.

Suggested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/keymaps.h       | 4 ++--
 ui/curses.c        | 4 ++--
 ui/input-barrier.c | 4 ++--
 ui/keymaps.c       | 4 ++--
 ui/vnc.c           | 6 +++---
 5 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/ui/keymaps.h b/ui/keymaps.h
index e8917e56404..89bfbb2704d 100644
--- a/ui/keymaps.h
+++ b/ui/keymaps.h
@@ -52,8 +52,8 @@ typedef struct {
 
 typedef struct kbd_layout_t kbd_layout_t;
 
-kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
-                                   const char *language, Error **errp);
+kbd_layout_t *kbd_layout_new(const name2keysym_t *table,
+                             const char *language, Error **errp);
 void kbd_layout_free(kbd_layout_t *k);
 int keysym2scancode(kbd_layout_t *k, int keysym,
                     QKbdState *kbd, bool down);
diff --git a/ui/curses.c b/ui/curses.c
index 161f78c35c3..af4ccb4227d 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -770,8 +770,8 @@ static void curses_keyboard_setup(void)
         keyboard_layout = "en-us";
 #endif
     if(keyboard_layout) {
-        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
-                                          &error_fatal);
+        kbd_layout = kbd_layout_new(name2keysym, keyboard_layout,
+                                    &error_fatal);
     }
 }
 
diff --git a/ui/input-barrier.c b/ui/input-barrier.c
index 0a2198ca500..74724be8f45 100644
--- a/ui/input-barrier.c
+++ b/ui/input-barrier.c
@@ -679,8 +679,8 @@ static void input_barrier_instance_init(Object *obj)
     /* always use generic keymaps */
     if (keyboard_layout && !kbd_layout) {
         /* We use X11 key id, so use VNC name2keysym */
-        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
-                                          &error_fatal);
+        kbd_layout = kbd_layout_new(name2keysym, keyboard_layout,
+                                    &error_fatal);
     }
 
     ib->saddr.type = SOCKET_ADDRESS_TYPE_INET;
diff --git a/ui/keymaps.c b/ui/keymaps.c
index d1b3f43dc8a..6822c097be7 100644
--- a/ui/keymaps.c
+++ b/ui/keymaps.c
@@ -187,8 +187,8 @@ void kbd_layout_free(kbd_layout_t *k)
     g_free(k);
 }
 
-kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
-                                   const char *language, Error **errp)
+kbd_layout_t *kbd_layout_new(const name2keysym_t *table,
+                             const char *language, Error **errp)
 {
     kbd_layout_t *k;
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 16dbb0cd6c2..c87d1f61a0a 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3443,10 +3443,10 @@ void vnc_display_init(const char *id, Error **errp)
 
     if (keyboard_layout) {
         trace_vnc_key_map_init(keyboard_layout);
-        vd->kbd_layout = init_keyboard_layout(name2keysym,
-                                              keyboard_layout, errp);
+        vd->kbd_layout = kbd_layout_new(name2keysym,
+                                        keyboard_layout, errp);
     } else {
-        vd->kbd_layout = init_keyboard_layout(name2keysym, "en-us", errp);
+        vd->kbd_layout = kbd_layout_new(name2keysym, "en-us", errp);
     }
 
     if (!vd->kbd_layout) {
-- 
2.54.0



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

* [PULL v2 07/15] ui/console: remove qemu_console_is_visible()
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (5 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 06/15] ui: rename init_keyboard_layout->kbd_layout_new() marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 08/15] ui/console: return completion status from gfx_update callback marcandre.lureau
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

Drop the bookkeeping, we can simply afford an empty "foreach".

Notice that dpy_gfx_update_texture() is now called even when there are
no listeners. This is more correct, as the texture is not fully
refreshed when a listener connects, so it may be outdated/garbaged.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h |  1 -
 ui/console-priv.h    |  1 -
 ui/console.c         | 29 -----------------------------
 3 files changed, 31 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index e2e5ff76ec1..f04844263b9 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -399,7 +399,6 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
                                                 uint32_t head, Error **errp);
 QEMUCursor *qemu_console_get_cursor(QemuConsole *con);
-bool qemu_console_is_visible(QemuConsole *con);
 bool qemu_console_is_graphic(QemuConsole *con);
 bool qemu_console_is_fixedsize(QemuConsole *con);
 bool qemu_console_is_gl_blocked(QemuConsole *con);
diff --git a/ui/console-priv.h b/ui/console-priv.h
index 39798c3e9d7..2299898984d 100644
--- a/ui/console-priv.h
+++ b/ui/console-priv.h
@@ -16,7 +16,6 @@ struct QemuConsole {
     DisplayState *ds;
     DisplaySurface *surface;
     DisplayScanout scanout;
-    int dcls;
     DisplayGLCtx *gl;
     int gl_block;
     QEMUTimer *gl_unblock_timer;
diff --git a/ui/console.c b/ui/console.c
index 8f65308958a..abb6cba2826 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -648,9 +648,6 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
     dcl->ds = get_alloc_displaystate();
     QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
     gui_setup_refresh(dcl->ds);
-    if (dcl->con) {
-        dcl->con->dcls++;
-    }
     displaychangelistener_display_console(dcl, &error_fatal);
     if (QEMU_IS_GRAPHIC_CONSOLE(dcl->con)) {
         dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(dcl->con));
@@ -678,9 +675,6 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl)
     if (!ds) {
         return;
     }
-    if (dcl->con) {
-        dcl->con->dcls--;
-    }
     QLIST_REMOVE(dcl, next);
     dcl->ds = NULL;
     gui_setup_refresh(ds);
@@ -745,9 +739,6 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
     w = MIN(w, width - x);
     h = MIN(h, height - y);
 
-    if (!qemu_console_is_visible(con)) {
-        return;
-    }
     dpy_gfx_update_texture(con, con->surface, x, y, w, h);
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (con != dcl->con) {
@@ -846,9 +837,6 @@ void dpy_text_cursor(QemuConsole *con, int x, int y)
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
 
-    if (!qemu_console_is_visible(con)) {
-        return;
-    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (con != dcl->con) {
             continue;
@@ -864,9 +852,6 @@ void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
 
-    if (!qemu_console_is_visible(con)) {
-        return;
-    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (con != dcl->con) {
             continue;
@@ -882,9 +867,6 @@ void dpy_text_resize(QemuConsole *con, int w, int h)
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
 
-    if (!qemu_console_is_visible(con)) {
-        return;
-    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (con != dcl->con) {
             continue;
@@ -904,9 +886,6 @@ void dpy_mouse_set(QemuConsole *c, int x, int y, bool on)
     con->cursor_x = x;
     con->cursor_y = y;
     con->cursor_on = on;
-    if (!qemu_console_is_visible(c)) {
-        return;
-    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (c != dcl->con) {
             continue;
@@ -925,9 +904,6 @@ void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor)
 
     cursor_unref(con->cursor);
     con->cursor = cursor_ref(cursor);
-    if (!qemu_console_is_visible(c)) {
-        return;
-    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (c != dcl->con) {
             continue;
@@ -1285,11 +1261,6 @@ QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
     return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL;
 }
 
-bool qemu_console_is_visible(QemuConsole *con)
-{
-    return con->dcls > 0;
-}
-
 bool qemu_console_is_graphic(QemuConsole *con)
 {
     return con && QEMU_IS_GRAPHIC_CONSOLE(con);
-- 
2.54.0



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

* [PULL v2 08/15] ui/console: return completion status from gfx_update callback
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (6 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 07/15] ui/console: remove qemu_console_is_visible() marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 09/15] ui/console: move console_handle_touch_event() to input marcandre.lureau
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel
  Cc: stefanha, Marc-André Lureau, Jan Kiszka, Peter Maydell,
	Phil Dennis-Jordan, Richard Henderson, Helge Deller,
	Philippe Mathieu-Daudé, Gerd Hoffmann, Mark Cave-Ayland,
	Samuel Tardieu, Hervé Poussineau, Aleksandar Rikalo,
	Jean-Christophe Dubois, Laurent Vivier, Thomas Huth,
	BALATON Zoltan, Alex Bennée, Akihiko Odaki, Dmitry Osipenko,
	Michael S. Tsirkin, Dmitry Fleytman, Stefano Stabellini,
	Anthony PERARD, Edgar E. Iglesias, Alistair Francis,
	Alex Williamson, Cédric Le Goater, open list:Musicpal,
	open list:sam460ex, open list:X86 Xen CPUs

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

Replace the two-field design (gfx_update void callback + gfx_update_async
flag) with a single bool return value from gfx_update. Returning true
means the update completed synchronously and graphic_hw_update_done()
should be called by the console layer. Returning false means the update
is deferred and the device will call graphic_hw_update_done() itself
later (as done by QXL/SPICE and Apple GFX).

This simplifies the interface and makes the async contract explicit at
each call site rather than relying on a separate struct field.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/display/qxl.h              |  2 +-
 include/ui/console.h          | 11 ++++++++---
 hw/arm/musicpal.c             |  3 ++-
 hw/display/artist.c           |  4 +++-
 hw/display/bcm2835_fb.c       |  7 ++++---
 hw/display/bochs-display.c    |  6 ++++--
 hw/display/cg3.c              |  5 +++--
 hw/display/dm163.c            |  4 +++-
 hw/display/exynos4210_fimd.c  |  6 ++++--
 hw/display/g364fb.c           |  9 ++++++---
 hw/display/imx6ul_lcdif.c     |  7 ++++---
 hw/display/jazz_led.c         |  6 ++++--
 hw/display/macfb.c            |  6 ++++--
 hw/display/next-fb.c          |  4 +++-
 hw/display/omap_lcdc.c        | 14 ++++++++------
 hw/display/pl110.c            |  5 +++--
 hw/display/qxl-render.c       |  6 +++---
 hw/display/qxl.c              |  7 +++----
 hw/display/ramfb-standalone.c |  4 +++-
 hw/display/sm501.c            |  8 +++++---
 hw/display/ssd0303.c          | 10 ++++++----
 hw/display/ssd0323.c          | 11 ++++++-----
 hw/display/tcx.c              |  6 ++++--
 hw/display/vga.c              |  4 +++-
 hw/display/virtio-gpu-base.c  |  3 ++-
 hw/display/virtio-vga.c       |  6 +++---
 hw/display/vmware_vga.c       |  7 ++++---
 hw/display/xenfb.c            |  6 ++++--
 hw/display/xlnx_dp.c          | 10 ++++++----
 hw/vfio/display.c             | 17 ++++++++++-------
 ui/console.c                  |  7 +------
 hw/display/apple-gfx.m        | 10 +++++-----
 32 files changed, 132 insertions(+), 89 deletions(-)

diff --git a/hw/display/qxl.h b/hw/display/qxl.h
index e0a85a5ca49..ad8a9128785 100644
--- a/hw/display/qxl.h
+++ b/hw/display/qxl.h
@@ -187,7 +187,7 @@ int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
 
 /* qxl-render.c */
 void qxl_render_resize(PCIQXLDevice *qxl);
-void qxl_render_update(PCIQXLDevice *qxl);
+bool qxl_render_update(PCIQXLDevice *qxl);
 int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
 void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie);
 void qxl_render_update_area_bh(void *opaque);
diff --git a/include/ui/console.h b/include/ui/console.h
index f04844263b9..0bc9e544fad 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -368,9 +368,14 @@ enum {
 typedef struct GraphicHwOps {
     int (*get_flags)(void *opaque); /* optional, default 0 */
     void (*invalidate)(void *opaque);
-    void (*gfx_update)(void *opaque);
-    bool gfx_update_async; /* if true, calls graphic_hw_update_done() */
-    void (*text_update)(void *opaque, console_ch_t *text);
+    /*
+     * Returns true if the update is handled synchronously, false if deferred
+     * and graphic_hw_update_done() will be called when ready (to resume waiting
+     * tasks/coroutines).
+     * Optional.
+     */
+    bool (*gfx_update)(void *opaque);
+    void (*text_update)(void *opaque, uint32_t *text);
     void (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info);
     void (*gl_block)(void *opaque, bool block);
 } GraphicHwOps;
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 441a11100a3..ba88ed756e2 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -152,7 +152,7 @@ static inline void set_lcd_pixel32(musicpal_lcd_state *s,
     }
 }
 
-static void lcd_refresh(void *opaque)
+static bool lcd_refresh(void *opaque)
 {
     musicpal_lcd_state *s = opaque;
     int x, y, col;
@@ -171,6 +171,7 @@ static void lcd_refresh(void *opaque)
     }
 
     dpy_gfx_update(s->con, 0, 0, 128*3, 64*3);
+    return true;
 }
 
 static void lcd_invalidate(void *opaque)
diff --git a/hw/display/artist.c b/hw/display/artist.c
index 206f77afba1..a07508378c7 100644
--- a/hw/display/artist.c
+++ b/hw/display/artist.c
@@ -1311,7 +1311,7 @@ static void artist_draw_line(void *opaque, uint8_t *d, const uint8_t *src,
     }
 }
 
-static void artist_update_display(void *opaque)
+static bool artist_update_display(void *opaque)
 {
     ARTISTState *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -1326,6 +1326,8 @@ static void artist_update_display(void *opaque)
     if (first >= 0) {
         dpy_gfx_update(s->con, 0, first, s->width, last - first + 1);
     }
+
+    return true;
 }
 
 static void artist_invalidate(void *opaque)
diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c
index 75d7c0f8499..83c4c03c7ca 100644
--- a/hw/display/bcm2835_fb.c
+++ b/hw/display/bcm2835_fb.c
@@ -150,7 +150,7 @@ static bool fb_use_offsets(BCM2835FBConfig *config)
         config->yres_virtual > config->yres;
 }
 
-static void fb_update_display(void *opaque)
+static bool fb_update_display(void *opaque)
 {
     BCM2835FBState *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -161,7 +161,7 @@ static void fb_update_display(void *opaque)
     uint32_t xoff = 0, yoff = 0;
 
     if (s->lock || !s->config.xres) {
-        return;
+        return true;
     }
 
     src_width = bcm2835_fb_get_pitch(&s->config);
@@ -174,7 +174,7 @@ static void fb_update_display(void *opaque)
 
     switch (surface_bits_per_pixel(surface)) {
     case 0:
-        return;
+        return true;
     case 8:
         break;
     case 15:
@@ -212,6 +212,7 @@ static void fb_update_display(void *opaque)
     }
 
     s->invalidate = false;
+    return true;
 }
 
 void bcm2835_fb_validate_config(BCM2835FBConfig *config)
diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c
index 5fb6b733cb5..8ef9b76cf85 100644
--- a/hw/display/bochs-display.c
+++ b/hw/display/bochs-display.c
@@ -198,7 +198,7 @@ static int bochs_display_get_mode(BochsDisplayState *s,
     return 0;
 }
 
-static void bochs_display_update(void *opaque)
+static bool bochs_display_update(void *opaque)
 {
     BochsDisplayState *s = opaque;
     DirtyBitmapSnapshot *snap = NULL;
@@ -212,7 +212,7 @@ static void bochs_display_update(void *opaque)
     ret = bochs_display_get_mode(s, &mode);
     if (ret < 0) {
         /* no (valid) video mode */
-        return;
+        return true;
     }
 
     if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) {
@@ -255,6 +255,8 @@ static void bochs_display_update(void *opaque)
 
         g_free(snap);
     }
+
+    return true;
 }
 
 static const GraphicHwOps bochs_display_gfx_ops = {
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index 0a413fbb7ec..963bb3427a6 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -85,7 +85,7 @@ struct CG3State {
     uint8_t dac_index, dac_state;
 };
 
-static void cg3_update_display(void *opaque)
+static bool cg3_update_display(void *opaque)
 {
     CG3State *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -98,7 +98,7 @@ static void cg3_update_display(void *opaque)
     DirtyBitmapSnapshot *snap = NULL;
 
     if (surface_bits_per_pixel(surface) != 32) {
-        return;
+        return true;
     }
     width = s->width;
     height = s->height;
@@ -154,6 +154,7 @@ static void cg3_update_display(void *opaque)
         qemu_irq_raise(s->irq);
     }
     g_free(snap);
+    return true;
 }
 
 static void cg3_invalidate_display(void *opaque)
diff --git a/hw/display/dm163.c b/hw/display/dm163.c
index 4feae912945..9ea62cb4f76 100644
--- a/hw/display/dm163.c
+++ b/hw/display/dm163.c
@@ -285,7 +285,7 @@ static uint32_t *update_display_of_row(DM163State *s, uint32_t *dest,
     return dest;
 }
 
-static void dm163_update_display(void *opaque)
+static bool dm163_update_display(void *opaque)
 {
     DM163State *s = (DM163State *)opaque;
     DisplaySurface *surface = qemu_console_surface(s->console);
@@ -300,6 +300,8 @@ static void dm163_update_display(void *opaque)
         }
         dest = update_display_of_row(s, dest, row);
     }
+
+    return true;
 }
 
 static const GraphicHwOps dm163_ops = {
diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index 2d8fa7ee944..a91f04aaf79 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1270,7 +1270,7 @@ static void exynos4210_update_resolution(Exynos4210fimdState *s)
     }
 }
 
-static void exynos4210_fimd_update(void *opaque)
+static bool exynos4210_fimd_update(void *opaque)
 {
     Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
     DisplaySurface *surface;
@@ -1287,7 +1287,7 @@ static void exynos4210_fimd_update(void *opaque)
 
     if (!s || !s->console || !s->enabled ||
         surface_bits_per_pixel(qemu_console_surface(s->console)) == 0) {
-        return;
+        return true;
     }
 
     global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1;
@@ -1348,6 +1348,8 @@ static void exynos4210_fimd_update(void *opaque)
         exynos4210_fimd_enable(s, false);
     }
     exynos4210_fimd_update_irq(s);
+
+    return true;
 }
 
 static void exynos4210_fimd_reset(DeviceState *d)
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
index 50952e9934b..bd15f6f0acc 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -238,15 +238,16 @@ static void g364fb_draw_blank(G364State *s)
     s->blanked = 1;
 }
 
-static void g364fb_update_display(void *opaque)
+static bool g364fb_update_display(void *opaque)
 {
     G364State *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
 
     qemu_flush_coalesced_mmio_buffer();
 
-    if (s->width == 0 || s->height == 0)
-        return;
+    if (s->width == 0 || s->height == 0) {
+        return true;
+    }
 
     if (s->width != surface_width(surface) ||
         s->height != surface_height(surface)) {
@@ -262,6 +263,8 @@ static void g364fb_update_display(void *opaque)
     }
 
     qemu_irq_raise(s->irq);
+
+    return true;
 }
 
 static inline void g364fb_invalidate_display(void *opaque)
diff --git a/hw/display/imx6ul_lcdif.c b/hw/display/imx6ul_lcdif.c
index 33cd00fbe16..afcd0805ca4 100644
--- a/hw/display/imx6ul_lcdif.c
+++ b/hw/display/imx6ul_lcdif.c
@@ -152,7 +152,7 @@ static void imx6ul_lcdif_draw_line_xrgb8888(void *opaque, uint8_t *dst,
     }
 }
 
-static void imx6ul_lcdif_update_display(void *opaque)
+static bool imx6ul_lcdif_update_display(void *opaque)
 {
     IMX6ULLCDIFState *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -167,7 +167,7 @@ static void imx6ul_lcdif_update_display(void *opaque)
     int src_width;
 
     if (!imx6ul_lcdif_is_running(s) || width == 0 || height == 0) {
-        return;
+        return true;
     }
 
     switch (FIELD_EX32(ctrl, CTRL, WORD_LENGTH)) {
@@ -180,7 +180,7 @@ static void imx6ul_lcdif_update_display(void *opaque)
         fn = imx6ul_lcdif_draw_line_xrgb8888;
         break;
     default:
-        return;
+        return true;
     }
 
     if (surface_width(surface) != width || surface_height(surface) != height) {
@@ -207,6 +207,7 @@ static void imx6ul_lcdif_update_display(void *opaque)
     }
 
     s->invalidate = false;
+    return true;
 }
 
 static void imx6ul_lcdif_invalidate_display(void *opaque)
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
index 9d62e51bed9..7d1a020d4d9 100644
--- a/hw/display/jazz_led.c
+++ b/hw/display/jazz_led.c
@@ -144,7 +144,7 @@ static void draw_vertical_line(DisplaySurface *ds,
     }
 }
 
-static void jazz_led_update_display(void *opaque)
+static bool jazz_led_update_display(void *opaque)
 {
     LedState *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -186,7 +186,7 @@ static void jazz_led_update_display(void *opaque)
             color_led = rgb_to_pixel32(0x00, 0xff, 0x00);
             break;
         default:
-            return;
+            return true;
         }
 
         /* display segments */
@@ -218,6 +218,8 @@ static void jazz_led_update_display(void *opaque)
 
     s->state = REDRAW_NONE;
     dpy_gfx_update_full(s->con);
+
+    return true;
 }
 
 static void jazz_led_invalidate_display(void *opaque)
diff --git a/hw/display/macfb.c b/hw/display/macfb.c
index b8115c2be13..848c3c282bd 100644
--- a/hw/display/macfb.c
+++ b/hw/display/macfb.c
@@ -454,7 +454,7 @@ static gchar *macfb_mode_list(void)
 }
 
 
-static void macfb_update_display(void *opaque)
+static bool macfb_update_display(void *opaque)
 {
     MacfbState *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -462,7 +462,7 @@ static void macfb_update_display(void *opaque)
     qemu_flush_coalesced_mmio_buffer();
 
     if (s->width == 0 || s->height == 0) {
-        return;
+        return true;
     }
 
     if (s->width != surface_width(surface) ||
@@ -471,6 +471,8 @@ static void macfb_update_display(void *opaque)
     }
 
     macfb_draw_graphic(s);
+
+    return true;
 }
 
 static void macfb_update_irq(MacfbState *s)
diff --git a/hw/display/next-fb.c b/hw/display/next-fb.c
index 3d97702fce5..e758b223ef7 100644
--- a/hw/display/next-fb.c
+++ b/hw/display/next-fb.c
@@ -67,7 +67,7 @@ static void nextfb_draw_line(void *opaque, uint8_t *d, const uint8_t *s,
     }
 }
 
-static void nextfb_update(void *opaque)
+static bool nextfb_update(void *opaque)
 {
     NeXTFbState *s = NEXTFB(opaque);
     int dest_width = 4;
@@ -90,6 +90,8 @@ static void nextfb_update(void *opaque)
                                s, &first, &last);
 
     dpy_gfx_update(s->con, 0, 0, s->cols, s->rows);
+
+    return true;
 }
 
 static void nextfb_invalidate(void *opaque)
diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c
index c41c65bb2e0..1e8385ebffb 100644
--- a/hw/display/omap_lcdc.c
+++ b/hw/display/omap_lcdc.c
@@ -197,7 +197,7 @@ static void draw_line16_32(void *opaque, uint8_t *d, const uint8_t *s,
     } while (-- width != 0);
 }
 
-static void omap_update_display(void *opaque)
+static bool omap_update_display(void *opaque)
 {
     struct omap_lcd_panel_s *omap_lcd = opaque;
     DisplaySurface *surface;
@@ -207,12 +207,12 @@ static void omap_update_display(void *opaque)
     hwaddr frame_base;
 
     if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable) {
-        return;
+        return true;
     }
 
     surface = qemu_console_surface(omap_lcd->con);
     if (!surface_bits_per_pixel(surface)) {
-        return;
+        return true;
     }
 
     frame_offset = 0;
@@ -256,7 +256,7 @@ static void omap_update_display(void *opaque)
 
     default:
         /* Unsupported at the moment.  */
-        return;
+        return true;
     }
 
     /* Resolution */
@@ -278,7 +278,7 @@ static void omap_update_display(void *opaque)
         omap_lcd->sync_error = 1;
         omap_lcd_interrupts(omap_lcd);
         omap_lcd->enable = 0;
-        return;
+        return true;
     }
 
     /* Content */
@@ -291,7 +291,7 @@ static void omap_update_display(void *opaque)
         omap_lcd->dma->current_frame ^= 1;
 
     if (!surface_bits_per_pixel(surface)) {
-        return;
+        return true;
     }
 
     first = 0;
@@ -323,6 +323,8 @@ static void omap_update_display(void *opaque)
         dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
     }
     omap_lcd->invalidate = 0;
+
+    return true;
 }
 
 static void omap_invalidate_display(void *opaque) {
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index 4cd62a98757..e134ac28eb6 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -210,7 +210,7 @@ static int pl110_enabled(PL110State *s)
   return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
 }
 
-static void pl110_update_display(void *opaque)
+static bool pl110_update_display(void *opaque)
 {
     PL110State *s = (PL110State *)opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -221,7 +221,7 @@ static void pl110_update_display(void *opaque)
     int last;
 
     if (!pl110_enabled(s)) {
-        return;
+        return true;
     }
 
     if (s->cr & PL110_CR_BGR)
@@ -306,6 +306,7 @@ static void pl110_update_display(void *opaque)
         dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
     }
     s->invalidate = 0;
+    return true;
 }
 
 static void pl110_invalidate_display(void * opaque)
diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
index c6a9ac1da10..5b4f8842011 100644
--- a/hw/display/qxl-render.c
+++ b/hw/display/qxl-render.c
@@ -173,7 +173,7 @@ end:
  * callbacks are called by spice_server thread, deferring to bh called from the
  * io thread.
  */
-void qxl_render_update(PCIQXLDevice *qxl)
+bool qxl_render_update(PCIQXLDevice *qxl)
 {
     QXLCookie *cookie;
 
@@ -183,8 +183,7 @@ void qxl_render_update(PCIQXLDevice *qxl)
         qxl->mode == QXL_MODE_UNDEFINED) {
         qxl_render_update_area_unlocked(qxl);
         qemu_mutex_unlock(&qxl->ssd.lock);
-        graphic_hw_update_done(qxl->ssd.dcl.con);
-        return;
+        return true;
     }
 
     qxl->guest_primary.commands = 0;
@@ -195,6 +194,7 @@ void qxl_render_update(PCIQXLDevice *qxl)
     qxl_set_rect_to_surface(qxl, &cookie->u.render.area);
     qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL,
                           0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie);
+    return false;
 }
 
 void qxl_render_update_area_bh(void *opaque)
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index f8fd7ee0698..0a3c42c8ec2 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -122,7 +122,7 @@ static void qxl_reset_memslots(PCIQXLDevice *d);
 static void qxl_reset_surfaces(PCIQXLDevice *d);
 static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
 
-static void qxl_hw_update(void *opaque);
+static bool qxl_hw_update(void *opaque);
 
 void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
 {
@@ -1144,7 +1144,6 @@ static const QXLInterface qxl_interface = {
 
 static const GraphicHwOps qxl_ops = {
     .gfx_update  = qxl_hw_update,
-    .gfx_update_async = true,
 };
 
 static void qxl_enter_vga_mode(PCIQXLDevice *d)
@@ -1928,11 +1927,11 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
 
 /* graphics console */
 
-static void qxl_hw_update(void *opaque)
+static bool qxl_hw_update(void *opaque)
 {
     PCIQXLDevice *qxl = opaque;
 
-    qxl_render_update(qxl);
+    return qxl_render_update(qxl);
 }
 
 static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c
index f1958be32ad..27f0ba19f90 100644
--- a/hw/display/ramfb-standalone.c
+++ b/hw/display/ramfb-standalone.c
@@ -20,7 +20,7 @@ struct RAMFBStandaloneState {
     bool use_legacy_x86_rom;
 };
 
-static void display_update_wrapper(void *dev)
+static bool display_update_wrapper(void *dev)
 {
     RAMFBStandaloneState *ramfb = RAMFB(dev);
 
@@ -29,6 +29,8 @@ static void display_update_wrapper(void *dev)
     } else {
         ramfb_display_update(ramfb->con, ramfb->state);
     }
+
+    return true;
 }
 
 static const GraphicHwOps wrapper_ops = {
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index a07aa9886f9..a3993ceba29 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1716,7 +1716,7 @@ static void draw_hwc_line_32(uint8_t *d, const uint8_t *s, int width,
     }
 }
 
-static void sm501_update_display(void *opaque)
+static bool sm501_update_display(void *opaque)
 {
     SM501State *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -1740,7 +1740,7 @@ static void sm501_update_display(void *opaque)
 
     if (!((crt ? s->dc_crt_control : s->dc_panel_control)
           & SM501_DC_CRT_CONTROL_ENABLE)) {
-        return;
+        return true;
     }
 
     palette = (uint32_t *)(crt ? &s->dc_palette[SM501_DC_CRT_PALETTE -
@@ -1761,7 +1761,7 @@ static void sm501_update_display(void *opaque)
     default:
         qemu_log_mask(LOG_GUEST_ERROR, "sm501: update display"
                       "invalid control register value.\n");
-        return;
+        return true;
     }
 
     /* set up to draw hardware cursor */
@@ -1833,6 +1833,8 @@ static void sm501_update_display(void *opaque)
     if (y_start >= 0) {
         dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
     }
+
+    return true;
 }
 
 static const GraphicHwOps sm501_ops = {
diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c
index 87781438cd5..229856cc427 100644
--- a/hw/display/ssd0303.c
+++ b/hw/display/ssd0303.c
@@ -203,7 +203,7 @@ static int ssd0303_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static void ssd0303_update_display(void *opaque)
+static bool ssd0303_update_display(void *opaque)
 {
     ssd0303_state *s = (ssd0303_state *)opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -218,11 +218,11 @@ static void ssd0303_update_display(void *opaque)
     uint8_t mask;
 
     if (!s->redraw)
-        return;
+        return true;
 
     switch (surface_bits_per_pixel(surface)) {
     case 0:
-        return;
+        return true;
     case 15:
         dest_width = 2;
         break;
@@ -237,7 +237,7 @@ static void ssd0303_update_display(void *opaque)
         break;
     default:
         BADF("Bad color depth\n");
-        return;
+        return true;
     }
     dest_width *= MAGNIFY;
     memset(colortab, 0xff, dest_width);
@@ -269,6 +269,8 @@ static void ssd0303_update_display(void *opaque)
     }
     s->redraw = 0;
     dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
+
+    return true;
 }
 
 static void ssd0303_invalidate_display(void * opaque)
diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c
index af5ff4fecdc..67db16086c8 100644
--- a/hw/display/ssd0323.c
+++ b/hw/display/ssd0323.c
@@ -181,7 +181,7 @@ static uint32_t ssd0323_transfer(SSIPeripheral *dev, uint32_t data)
     return 0;
 }
 
-static void ssd0323_update_display(void *opaque)
+static bool ssd0323_update_display(void *opaque)
 {
     ssd0323_state *s = (ssd0323_state *)opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -197,11 +197,11 @@ static void ssd0323_update_display(void *opaque)
     int dest_width;
 
     if (!s->redraw)
-        return;
+        return true;
 
     switch (surface_bits_per_pixel(surface)) {
     case 0:
-        return;
+        return true;
     case 15:
         dest_width = 2;
         break;
@@ -216,7 +216,7 @@ static void ssd0323_update_display(void *opaque)
         break;
     default:
         BADF("Bad color depth\n");
-        return;
+        return true;
     }
     p = colortab;
     for (i = 0; i < 16; i++) {
@@ -240,7 +240,7 @@ static void ssd0323_update_display(void *opaque)
             break;
         default:
             BADF("Bad color depth\n");
-            return;
+            return true;
         }
         p += dest_width;
     }
@@ -271,6 +271,7 @@ static void ssd0323_update_display(void *opaque)
     }
     s->redraw = 0;
     dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
+    return true;
 }
 
 static void ssd0323_invalidate_display(void * opaque)
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index ea92a48400c..cedbf5c7acd 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -209,7 +209,7 @@ static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
 /* Fixed line length 1024 allows us to do nice tricks not possible on
    VGA... */
 
-static void tcx_update_display(void *opaque)
+static bool tcx_update_display(void *opaque)
 {
     TCXState *ts = opaque;
     DisplaySurface *surface = qemu_console_surface(ts->con);
@@ -257,9 +257,10 @@ static void tcx_update_display(void *opaque)
                        ts->width, y - y_start);
     }
     g_free(snap);
+    return true;
 }
 
-static void tcx24_update_display(void *opaque)
+static bool tcx24_update_display(void *opaque)
 {
     TCXState *ts = opaque;
     DisplaySurface *surface = qemu_console_surface(ts->con);
@@ -312,6 +313,7 @@ static void tcx24_update_display(void *opaque)
                        ts->width, y - y_start);
     }
     g_free(snap);
+    return true;
 }
 
 static void tcx_invalidate_display(void *opaque)
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 0d69a53f27a..776aa443246 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1783,7 +1783,7 @@ static void vga_draw_blank(VGACommonState *s, int full_update)
 #define GMODE_GRAPH    1
 #define GMODE_BLANK 2
 
-static void vga_update_display(void *opaque)
+static bool vga_update_display(void *opaque)
 {
     VGACommonState *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->con);
@@ -1818,6 +1818,8 @@ static void vga_update_display(void *opaque)
             break;
         }
     }
+
+    return true;
 }
 
 /* force a full display refresh */
diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index cb76302e2d8..94cf362d152 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -83,8 +83,9 @@ static void virtio_gpu_invalidate_display(void *opaque)
 {
 }
 
-static void virtio_gpu_update_display(void *opaque)
+static bool virtio_gpu_update_display(void *opaque)
 {
+    return true;
 }
 
 static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index 5e087169f2f..f4713b91a66 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -19,15 +19,15 @@ static void virtio_vga_base_invalidate_display(void *opaque)
     }
 }
 
-static void virtio_vga_base_update_display(void *opaque)
+static bool virtio_vga_base_update_display(void *opaque)
 {
     VirtIOVGABase *vvga = opaque;
     VirtIOGPUBase *g = vvga->vgpu;
 
     if (g->enable) {
-        g->hw_ops->gfx_update(g);
+        return g->hw_ops->gfx_update(g);
     } else {
-        vvga->vga.hw_ops->gfx_update(&vvga->vga);
+        return vvga->vga.hw_ops->gfx_update(&vvga->vga);
     }
 }
 
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index 2b95787ddfd..c84c84a445e 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -1135,14 +1135,13 @@ static inline void vmsvga_check_size(struct vmsvga_state_s *s)
     }
 }
 
-static void vmsvga_update_display(void *opaque)
+static bool vmsvga_update_display(void *opaque)
 {
     struct vmsvga_state_s *s = opaque;
 
     if (!s->enable || !s->config) {
         /* in standard vga mode */
-        s->vga.hw_ops->gfx_update(&s->vga);
-        return;
+        return s->vga.hw_ops->gfx_update(&s->vga);
     }
 
     vmsvga_check_size(s);
@@ -1154,6 +1153,8 @@ static void vmsvga_update_display(void *opaque)
         s->invalidated = 0;
         dpy_gfx_update_full(s->vga.con);
     }
+
+    return true;
 }
 
 static void vmsvga_reset(DeviceState *dev)
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index ba886a940ee..2e431e27be6 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -709,14 +709,14 @@ static void xenfb_send_refresh_period(struct XenFB *xenfb, int period)
  * Our screen might be inactive.  When asked for
  * an update we know it is active.
  */
-static void xenfb_update(void *opaque)
+static bool xenfb_update(void *opaque)
 {
     struct XenFB *xenfb = opaque;
     DisplaySurface *surface;
     int i;
 
     if (xenfb->c.xendev.be_state != XenbusStateConnected)
-        return;
+        return true;
 
     if (!xenfb->feature_update) {
         /* we don't get update notifications, thus use the
@@ -770,6 +770,8 @@ static void xenfb_update(void *opaque)
     }
     xenfb->up_count = 0;
     xenfb->up_fullscreen = 0;
+
+    return true;
 }
 
 static void xenfb_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c
index 7d037b46a35..50e6ef10984 100644
--- a/hw/display/xlnx_dp.c
+++ b/hw/display/xlnx_dp.c
@@ -1252,12 +1252,12 @@ static inline void xlnx_dp_blend_surface(XlnxDPState *s)
                            surface_height(s->g_plane.surface));
 }
 
-static void xlnx_dp_update_display(void *opaque)
+static bool xlnx_dp_update_display(void *opaque)
 {
     XlnxDPState *s = XLNX_DP(opaque);
 
     if ((s->core_registers[DP_TRANSMITTER_ENABLE] & 0x01) == 0) {
-        return;
+        return true;
     }
 
     xlnx_dpdma_trigger_vsync_irq(s->dpdma);
@@ -1272,14 +1272,14 @@ static void xlnx_dp_update_display(void *opaque)
          */
         s->core_registers[DP_INT_STATUS] |= (1 << 21);
         xlnx_dp_update_irq(s);
-        return;
+        return true;
     }
 
     if (xlnx_dp_global_alpha_enabled(s)) {
         if (!xlnx_dpdma_start_operation(s->dpdma, 0, false)) {
             s->core_registers[DP_INT_STATUS] |= (1 << 21);
             xlnx_dp_update_irq(s);
-            return;
+            return true;
         }
         xlnx_dp_blend_surface(s);
     }
@@ -1288,6 +1288,8 @@ static void xlnx_dp_update_display(void *opaque)
      * XXX: We might want to update only what changed.
      */
     dpy_gfx_update_full(s->console);
+
+    return true;
 }
 
 static const GraphicHwOps xlnx_dp_gfx_ops = {
diff --git a/hw/vfio/display.c b/hw/vfio/display.c
index 5a42a6f7a29..4a9a58036e3 100644
--- a/hw/vfio/display.c
+++ b/hw/vfio/display.c
@@ -285,7 +285,7 @@ static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev)
     }
 }
 
-static void vfio_display_dmabuf_update(void *opaque)
+static bool vfio_display_dmabuf_update(void *opaque)
 {
     VFIOPCIDevice *vdev = opaque;
     VFIODisplay *dpy = vdev->dpy;
@@ -298,7 +298,7 @@ static void vfio_display_dmabuf_update(void *opaque)
         if (dpy->ramfb) {
             ramfb_display_update(dpy->con, dpy->ramfb);
         }
-        return;
+        return true;
     }
 
     width = qemu_dmabuf_get_width(primary->buf);
@@ -340,6 +340,8 @@ static void vfio_display_dmabuf_update(void *opaque)
     if (free_bufs) {
         vfio_display_free_dmabufs(vdev);
     }
+
+    return true;
 }
 
 static int vfio_display_get_flags(void *opaque)
@@ -399,7 +401,7 @@ void vfio_display_reset(VFIOPCIDevice *vdev)
     dpy_gfx_update_full(vdev->dpy->con);
 }
 
-static void vfio_display_region_update(void *opaque)
+static bool vfio_display_region_update(void *opaque)
 {
     VFIOPCIDevice *vdev = opaque;
     VFIODisplay *dpy = vdev->dpy;
@@ -414,18 +416,18 @@ static void vfio_display_region_update(void *opaque)
     if (ret < 0) {
         error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s",
                      strerror(errno));
-        return;
+        return true;
     }
     if (!plane.drm_format || !plane.size) {
         if (dpy->ramfb) {
             ramfb_display_update(dpy->con, dpy->ramfb);
             dpy->region.surface = NULL;
         }
-        return;
+        return true;
     }
     format = qemu_drm_format_to_pixman(plane.drm_format);
     if (!format) {
-        return;
+        return true;
     }
 
     if (dpy->region.buffer.size &&
@@ -476,11 +478,12 @@ static void vfio_display_region_update(void *opaque)
     dpy_gfx_update(dpy->con, 0, 0,
                    surface_width(dpy->region.surface),
                    surface_height(dpy->region.surface));
-    return;
+    return true;
 
 err:
     vfio_region_exit(&dpy->region.buffer);
     vfio_region_finalize(&dpy->region.buffer);
+    return true;
 }
 
 static const GraphicHwOps vfio_display_region_ops = {
diff --git a/ui/console.c b/ui/console.c
index abb6cba2826..a608c29b843 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -136,15 +136,10 @@ void graphic_hw_update_done(QemuConsole *con)
 
 void graphic_hw_update(QemuConsole *con)
 {
-    bool async = false;
     if (!con) {
         return;
     }
-    if (con->hw_ops->gfx_update) {
-        con->hw_ops->gfx_update(con->hw);
-        async = con->hw_ops->gfx_update_async;
-    }
-    if (!async) {
+    if (!con->hw_ops->gfx_update || con->hw_ops->gfx_update(con->hw)) {
         graphic_hw_update_done(con);
     }
 }
diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
index e0a765fcb1f..77d80fb7cef 100644
--- a/hw/display/apple-gfx.m
+++ b/hw/display/apple-gfx.m
@@ -330,25 +330,25 @@ static void apple_gfx_render_frame_completed_bh(void *opaque)
     }
 }
 
-static void apple_gfx_fb_update_display(void *opaque)
+static bool apple_gfx_fb_update_display(void *opaque)
 {
     AppleGFXState *s = opaque;
+    bool done = true;
 
     assert(bql_locked());
     if (s->new_frame_ready) {
         dpy_gfx_update_full(s->con);
         s->new_frame_ready = false;
-        graphic_hw_update_done(s->con);
     } else if (s->pending_frames > 0) {
         s->gfx_update_requested = true;
-    } else {
-        graphic_hw_update_done(s->con);
+        done = false;
     }
+
+    return done;
 }
 
 static const GraphicHwOps apple_gfx_fb_ops = {
     .gfx_update = apple_gfx_fb_update_display,
-    .gfx_update_async = true,
 };
 
 /* ------ Mouse cursor and display mode setting ------ */
-- 
2.54.0



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

* [PULL v2 09/15] ui/console: move console_handle_touch_event() to input
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (7 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 08/15] ui/console: return completion status from gfx_update callback marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 10/15] ui/dbus: fix warning for clients without "Interfaces" property marcandre.lureau
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

The function uses input.c functions to provide a simpler abstraction for
touch events. Let's move it from the already overloaded console.c, and
to avoid some unnecessary dependency from console.c on input.c.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h | 14 ----------
 include/ui/input.h   | 15 ++++++++++
 ui/console.c         | 65 --------------------------------------------
 ui/dbus-console.c    |  6 ++--
 ui/gtk.c             |  8 +++---
 ui/input.c           | 65 ++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 87 insertions(+), 86 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 0bc9e544fad..27eacc39cc0 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -111,20 +111,6 @@ void qemu_text_console_put_keysym(QemuTextConsole *s, int keysym);
 bool qemu_text_console_put_qcode(QemuTextConsole *s, int qcode, bool ctrl);
 void qemu_text_console_put_string(QemuTextConsole *s, const char *str, int len);
 
-/* Touch devices */
-typedef struct touch_slot {
-    int x;
-    int y;
-    int tracking_id;
-} touch_slot;
-
-void console_handle_touch_event(QemuConsole *con,
-                                struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
-                                uint64_t num_slot,
-                                int width, int height,
-                                double x, double y,
-                                InputMultiTouchType type,
-                                Error **errp);
 /* consoles */
 
 struct QemuConsoleClass {
diff --git a/include/ui/input.h b/include/ui/input.h
index 8f9aac562ed..52c164bde57 100644
--- a/include/ui/input.h
+++ b/include/ui/input.h
@@ -70,6 +70,21 @@ void qemu_input_queue_mtt_abs(QemuConsole *src, InputAxis axis, int value,
                               int min_in, int max_in,
                               int slot, int tracking_id);
 
+/* Touch devices */
+typedef struct touch_slot {
+    int x;
+    int y;
+    int tracking_id;
+} touch_slot;
+
+void qemu_input_touch_event(QemuConsole *con,
+                            struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
+                            uint64_t num_slot,
+                            int width, int height,
+                            double x, double y,
+                            InputMultiTouchType type,
+                            Error **errp);
+
 void qemu_input_check_mode_change(void);
 void qemu_add_mouse_mode_change_notifier(Notifier *notify);
 void qemu_remove_mouse_mode_change_notifier(Notifier *notify);
diff --git a/ui/console.c b/ui/console.c
index a608c29b843..799d61ec1a5 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -548,71 +548,6 @@ static bool console_compatible_with(QemuConsole *con,
     return true;
 }
 
-void console_handle_touch_event(QemuConsole *con,
-                                struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
-                                uint64_t num_slot,
-                                int width, int height,
-                                double x, double y,
-                                InputMultiTouchType type,
-                                Error **errp)
-{
-    struct touch_slot *slot;
-    bool needs_sync = false;
-    int update;
-    int i;
-
-    if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
-        error_setg(errp,
-                   "Unexpected touch slot number: % " PRId64" >= %d",
-                   num_slot, INPUT_EVENT_SLOTS_MAX);
-        return;
-    }
-
-    slot = &touch_slots[num_slot];
-    slot->x = x;
-    slot->y = y;
-
-    if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
-        slot->tracking_id = num_slot;
-    }
-
-    for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
-        if (i == num_slot) {
-            update = type;
-        } else {
-            update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
-        }
-
-        slot = &touch_slots[i];
-
-        if (slot->tracking_id == -1) {
-            continue;
-        }
-
-        if (update == INPUT_MULTI_TOUCH_TYPE_END) {
-            slot->tracking_id = -1;
-            qemu_input_queue_mtt(con, update, i, slot->tracking_id);
-            needs_sync = true;
-        } else {
-            qemu_input_queue_mtt(con, update, i, slot->tracking_id);
-            qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
-            qemu_input_queue_mtt_abs(con,
-                                    INPUT_AXIS_X, (int) slot->x,
-                                    0, width,
-                                    i, slot->tracking_id);
-            qemu_input_queue_mtt_abs(con,
-                                    INPUT_AXIS_Y, (int) slot->y,
-                                    0, height,
-                                    i, slot->tracking_id);
-            needs_sync = true;
-        }
-    }
-
-    if (needs_sync) {
-        qemu_input_event_sync();
-    }
-}
-
 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
 {
     /* display has opengl support */
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index 85e215ef233..564f004bd86 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -424,9 +424,9 @@ dbus_touch_send_event(DBusDisplayConsole *ddc,
     width = qemu_console_get_width(ddc->dcl.con, 0);
     height = qemu_console_get_height(ddc->dcl.con, 0);
 
-    console_handle_touch_event(ddc->dcl.con, touch_slots,
-                               num_slot, width, height,
-                               x, y, kind, &error);
+    qemu_input_touch_event(ddc->dcl.con, touch_slots,
+                           num_slot, width, height,
+                           x, y, kind, &error);
     if (error != NULL) {
         g_dbus_method_invocation_return_error(
             invocation, DBUS_DISPLAY_ERROR,
diff --git a/ui/gtk.c b/ui/gtk.c
index 9ebe7e8df0d..8fd39c90c29 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1201,10 +1201,10 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch,
         return FALSE;
     }
 
-    console_handle_touch_event(vc->gfx.dcl.con, touch_slots,
-                               num_slot, surface_width(vc->gfx.ds),
-                               surface_height(vc->gfx.ds), touch->x,
-                               touch->y, type, &err);
+    qemu_input_touch_event(vc->gfx.dcl.con, touch_slots,
+                           num_slot, surface_width(vc->gfx.ds),
+                           surface_height(vc->gfx.ds), touch->x,
+                           touch->y, type, &err);
     if (err) {
         warn_report_err(err);
     }
diff --git a/ui/input.c b/ui/input.c
index 147e69c1c3c..57e7817878a 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -609,3 +609,68 @@ bool qemu_mouse_set(int index, Error **errp)
     notifier_list_notify(&mouse_mode_notifiers, NULL);
     return true;
 }
+
+void qemu_input_touch_event(QemuConsole *con,
+                            struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
+                            uint64_t num_slot,
+                            int width, int height,
+                            double x, double y,
+                            InputMultiTouchType type,
+                            Error **errp)
+{
+    struct touch_slot *slot;
+    bool needs_sync = false;
+    int update;
+    int i;
+
+    if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
+        error_setg(errp,
+                   "Unexpected touch slot number: % " PRId64" >= %d",
+                   num_slot, INPUT_EVENT_SLOTS_MAX);
+        return;
+    }
+
+    slot = &touch_slots[num_slot];
+    slot->x = x;
+    slot->y = y;
+
+    if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
+        slot->tracking_id = num_slot;
+    }
+
+    for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
+        if (i == num_slot) {
+            update = type;
+        } else {
+            update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
+        }
+
+        slot = &touch_slots[i];
+
+        if (slot->tracking_id == -1) {
+            continue;
+        }
+
+        if (update == INPUT_MULTI_TOUCH_TYPE_END) {
+            slot->tracking_id = -1;
+            qemu_input_queue_mtt(con, update, i, slot->tracking_id);
+            needs_sync = true;
+        } else {
+            qemu_input_queue_mtt(con, update, i, slot->tracking_id);
+            qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
+            qemu_input_queue_mtt_abs(con,
+                                    INPUT_AXIS_X, (int) slot->x,
+                                    0, width,
+                                    i, slot->tracking_id);
+            qemu_input_queue_mtt_abs(con,
+                                    INPUT_AXIS_Y, (int) slot->y,
+                                    0, height,
+                                    i, slot->tracking_id);
+            needs_sync = true;
+        }
+    }
+
+    if (needs_sync) {
+        qemu_input_event_sync();
+    }
+}
-- 
2.54.0



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

* [PULL v2 10/15] ui/dbus: fix warning for clients without "Interfaces" property
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (8 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 09/15] ui/console: move console_handle_touch_event() to input marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 11/15] audio: Avoid unsigned sample wraparound marcandre.lureau
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Marc-André Lureau

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

The "Interfaces" property is not strictly required. We can just assume
the peer doesn't implement the given interface. Fix warning:
GLib: g_strv_contains: assertion 'strv != NULL' failed

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 ui/dbus-listener.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 37945236e11..e5ce92d1257 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -1026,9 +1026,11 @@ static bool
 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface)
 {
     QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy);
+    const char * const *interfaces;
     bool implements;
 
-    implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface);
+    interfaces = qemu_dbus_display1_listener_get_interfaces(l);
+    implements = interfaces && g_strv_contains(interfaces, iface);
     if (!implements) {
         g_debug("Display listener does not implement: `%s`", iface);
     }
-- 
2.54.0



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

* [PULL v2 11/15] audio: Avoid unsigned sample wraparound
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (9 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 10/15] ui/dbus: fix warning for clients without "Interfaces" property marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 12/15] audio: Clamp unsigned sample conversion marcandre.lureau
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Akihiko Odaki, Gerd Hoffmann, Marc-André Lureau

From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>

When !defined(SIGNED), nv - HALF will wrap around if nv < HALF because
nv and HALF are unsigned. Fix it by casting nv to mixeng_real, which is
signed.

Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20260423-audio-v1-1-e1d6b65c76f9@rsg.ci.i.u-tokyo.ac.jp>
---
 audio/mixeng_template.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h
index bc8509e423f..881653c44bf 100644
--- a/audio/mixeng_template.h
+++ b/audio/mixeng_template.h
@@ -43,13 +43,13 @@ static inline mixeng_real glue (conv_, ET) (IN_T v)
 #ifdef SIGNED
     return nv * (2.f / ((mixeng_real)IN_MAX - IN_MIN));
 #else
-    return (nv - HALF) * (2.f / (mixeng_real)IN_MAX);
+    return ((mixeng_real)nv - HALF) * (2.f / (mixeng_real)IN_MAX);
 #endif
 #else  /* !RECIPROCAL */
 #ifdef SIGNED
     return nv / (((mixeng_real)IN_MAX - IN_MIN) / 2.f);
 #else
-    return (nv - HALF) / ((mixeng_real)IN_MAX / 2.f);
+    return ((mixeng_real)nv - HALF) / ((mixeng_real)IN_MAX / 2.f);
 #endif
 #endif
 }
-- 
2.54.0



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

* [PULL v2 12/15] audio: Clamp unsigned sample conversion
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (10 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 11/15] audio: Avoid unsigned sample wraparound marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 13/15] audio: Use unsigned PCM bias marcandre.lureau
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Akihiko Odaki, Gerd Hoffmann, Marc-André Lureau

From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>

clip_*_uint32_t() returns 0 when v == 1.f because it computes the result
as (IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF):

- (v * ((mixeng_real)IN_MAX / 2.f)) + HALF == 0x100000000.f, which does
  not fit in uint32_t.
- (v * ((mixeng_real)IN_MAX / 2.f)) == 0x80000000.f
- ((mixeng_real)IN_MAX / 2.f) == 0x80000000.f
- (mixeng_real)IN_MAX == 0x100000000.f because 0xffffffff cannot be
  represented exactly in float.
- HALF == 0x7fffffff, which is implicitly converted to 0x80000000.f.

Clamp the result to avoid the overflow.

Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20260423-audio-v1-2-e1d6b65c76f9@rsg.ci.i.u-tokyo.ac.jp>
---
 audio/mixeng_template.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h
index 881653c44bf..5b0014bdd9a 100644
--- a/audio/mixeng_template.h
+++ b/audio/mixeng_template.h
@@ -65,7 +65,9 @@ static inline IN_T glue (clip_, ET) (mixeng_real v)
 #ifdef SIGNED
     return ENDIAN_CONVERT((IN_T)(v * (((mixeng_real)IN_MAX - IN_MIN) / 2.f)));
 #else
-    return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF));
+    return ENDIAN_CONVERT(MIN((int64_t)((v * ((mixeng_real)IN_MAX / 2.f)) +
+                                        HALF),
+                              IN_MAX));
 #endif
 }
 
-- 
2.54.0



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

* [PULL v2 13/15] audio: Use unsigned PCM bias
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (11 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 12/15] audio: Clamp unsigned sample conversion marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 14/15] ui/gtk: Use non-blocking clipboard retrieval marcandre.lureau
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Akihiko Odaki, Gerd Hoffmann, Marc-André Lureau

From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>

Clang warns for the uint32_t clip_ instantiations because HALF cannot be
represented with mixeng_real:

[1115/2559] Compiling C object libqemuaudio.a.p/audio_mixeng.c.o
In file included from ../../qemu/audio/mixeng.c:147:
../../qemu/audio/mixeng_template.h:68:70: warning: implicit conversion from 'unsigned int' to 'float' changes value from 2147483647 to 2147483648 [-Wimplicit-const-int-float-conversion]
   68 |     return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF));
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
../../qemu/audio/mixeng_template.h:31:22: note: expanded from macro 'HALF'
   31 | #define HALF (IN_MAX >> 1)
      |                      ^
../../qemu/audio/mixeng.c:146:28: note: expanded from macro 'ENDIAN_CONVERT'
  146 | #define ENDIAN_CONVERT(v) (v)
      |                            ^
In file included from ../../qemu/audio/mixeng.c:152:
../../qemu/audio/mixeng_template.h:68:70: warning: implicit conversion from 'unsigned int' to 'float' changes value from 2147483647 to 2147483648 [-Wimplicit-const-int-float-conversion]
   68 |     return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF));
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
../../qemu/audio/mixeng_template.h:31:22: note: expanded from macro 'HALF'
   31 | #define HALF (IN_MAX >> 1)
      |                      ^
../../qemu/audio/mixeng.c:151:36: note: expanded from macro 'ENDIAN_CONVERT'
  151 | #define ENDIAN_CONVERT(v) bswap32 (v)
      |                           ~~~~~~~~~^~
/Users/person/v/qemu/include/qemu/bswap.h:10:39: note: expanded from macro 'bswap32'
   10 | #define bswap32(_x) __builtin_bswap32(_x)
      |                                       ^~
2 warnings generated.

HALF is not the right value here anyway. IN_MAX is odd, so the integer
sample range has two middle codes. Unsigned PCM normally uses the upper
middle code as the "bias": 0x80, 0x8000, or 0x80000000. HALF is instead
defined as the lower middle code: 0x7f, 0x7fff, or 0x7fffffff.

Replace HALF with BIAS, defined as the upper middle code. This fixes the
warnings, since the value can be exactly represented with mixeng_real.

Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20260423-audio-v1-3-e1d6b65c76f9@rsg.ci.i.u-tokyo.ac.jp>
---
 audio/mixeng_template.h | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h
index 5b0014bdd9a..6db742188ed 100644
--- a/audio/mixeng_template.h
+++ b/audio/mixeng_template.h
@@ -28,7 +28,7 @@
  */
 
 #ifndef SIGNED
-#define HALF (IN_MAX >> 1)
+#define BIAS ((IN_T)1 << (SHIFT - 1))
 #endif
 
 #define ET glue (ENDIAN_CONVERSION, glue (glue (glue (_, ITYPE), BSIZE), _t))
@@ -43,13 +43,13 @@ static inline mixeng_real glue (conv_, ET) (IN_T v)
 #ifdef SIGNED
     return nv * (2.f / ((mixeng_real)IN_MAX - IN_MIN));
 #else
-    return ((mixeng_real)nv - HALF) * (2.f / (mixeng_real)IN_MAX);
+    return ((mixeng_real)nv - BIAS) * (1.f / BIAS);
 #endif
 #else  /* !RECIPROCAL */
 #ifdef SIGNED
     return nv / (((mixeng_real)IN_MAX - IN_MIN) / 2.f);
 #else
-    return ((mixeng_real)nv - HALF) / ((mixeng_real)IN_MAX / 2.f);
+    return ((mixeng_real)nv - BIAS) / BIAS;
 #endif
 #endif
 }
@@ -65,9 +65,7 @@ static inline IN_T glue (clip_, ET) (mixeng_real v)
 #ifdef SIGNED
     return ENDIAN_CONVERT((IN_T)(v * (((mixeng_real)IN_MAX - IN_MIN) / 2.f)));
 #else
-    return ENDIAN_CONVERT(MIN((int64_t)((v * ((mixeng_real)IN_MAX / 2.f)) +
-                                        HALF),
-                              IN_MAX));
+    return ENDIAN_CONVERT(MIN((int64_t)(v * BIAS) + BIAS, IN_MAX));
 #endif
 }
 
@@ -79,7 +77,7 @@ static inline int64_t glue (conv_, ET) (IN_T v)
 #ifdef SIGNED
     return ((int64_t) nv) << (32 - SHIFT);
 #else
-    return ((int64_t) nv - HALF) << (32 - SHIFT);
+    return ((int64_t) nv - BIAS) << (32 - SHIFT);
 #endif
 }
 
@@ -94,7 +92,7 @@ static inline IN_T glue (clip_, ET) (int64_t v)
 #ifdef SIGNED
     return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT)));
 #else
-    return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF));
+    return ENDIAN_CONVERT((IN_T)((v >> (32 - SHIFT)) + BIAS));
 #endif
 }
 #endif
@@ -150,5 +148,5 @@ static void glue (glue (clip_, ET), _from_mono)
 }
 
 #undef ET
-#undef HALF
+#undef BIAS
 #undef IN_T
-- 
2.54.0



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

* [PULL v2 14/15] ui/gtk: Use non-blocking clipboard retrieval
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (12 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 13/15] audio: Use unsigned PCM bias marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-28 15:07 ` [PULL v2 15/15] ui/gtk: Turn clipboard flag into runtime option marcandre.lureau
  2026-04-29 16:23 ` [PULL v2 00/15] UI patches Stefan Hajnoczi
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, Jindřich Makovička, Marc-André Lureau

From: Jindřich Makovička <makovick@gmail.com>

Signed-off-by: Jindrich Makovicka <makovick@gmail.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <20260427-gtk-clipboard-v5-1-6968feb31a5d@gmail.com>
---
 ui/gtk-clipboard.c | 53 ++++++++++++++++++++++++++++++++++------------
 1 file changed, 40 insertions(+), 13 deletions(-)

diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c
index 65d89ec601d..463ed4e905b 100644
--- a/ui/gtk-clipboard.c
+++ b/ui/gtk-clipboard.c
@@ -136,26 +136,55 @@ static void gd_clipboard_notify(Notifier *notifier, void *data)
     }
 }
 
+static void
+gd_clipboard_request_text_received_callback(GtkClipboard *clipboard,
+                                            const gchar *text,
+                                            gpointer data)
+{
+    QemuClipboardInfo *info = (QemuClipboardInfo *)data;
+
+    if (text) {
+        qemu_clipboard_set_data(info->owner, info, QEMU_CLIPBOARD_TYPE_TEXT,
+                                strlen(text), text, true);
+    }
+    qemu_clipboard_info_unref(info);
+}
+
 static void gd_clipboard_request(QemuClipboardInfo *info,
                                  QemuClipboardType type)
 {
     GtkDisplayState *gd = container_of(info->owner, GtkDisplayState, cbpeer);
-    char *text;
 
     switch (type) {
     case QEMU_CLIPBOARD_TYPE_TEXT:
-        text = gtk_clipboard_wait_for_text(gd->gtkcb[info->selection]);
-        if (text) {
-            qemu_clipboard_set_data(&gd->cbpeer, info, type,
-                                    strlen(text), text, true);
-            g_free(text);
-        }
+        qemu_clipboard_info_ref(info);
+        gtk_clipboard_request_text(gd->gtkcb[info->selection],
+                                   gd_clipboard_request_text_received_callback,
+                                   info);
         break;
     default:
         break;
     }
 }
 
+static void gd_clipboard_owner_change_targets_received_callback(
+    GtkClipboard *clipboard,
+    GdkAtom *targets,
+    gint n_targets,
+    gpointer data)
+{
+    QemuClipboardInfo *info = (QemuClipboardInfo *)data;
+
+    if (n_targets) {
+        if (gtk_targets_include_text(targets, n_targets)) {
+            info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
+        }
+    }
+
+    qemu_clipboard_update(info);
+    qemu_clipboard_info_unref(info);
+}
+
 static void gd_owner_change(GtkClipboard *clipboard,
                             GdkEvent *event,
                             gpointer data)
@@ -173,12 +202,10 @@ static void gd_owner_change(GtkClipboard *clipboard,
     switch (event->owner_change.reason) {
     case GDK_OWNER_CHANGE_NEW_OWNER:
         info = qemu_clipboard_info_new(&gd->cbpeer, s);
-        if (gtk_clipboard_wait_is_text_available(clipboard)) {
-            info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
-        }
-
-        qemu_clipboard_update(info);
-        qemu_clipboard_info_unref(info);
+        gtk_clipboard_request_targets(
+            clipboard,
+            gd_clipboard_owner_change_targets_received_callback,
+            info);
         break;
     default:
         qemu_clipboard_peer_release(&gd->cbpeer, s);
-- 
2.54.0



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

* [PULL v2 15/15] ui/gtk: Turn clipboard flag into runtime option
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (13 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 14/15] ui/gtk: Use non-blocking clipboard retrieval marcandre.lureau
@ 2026-04-28 15:07 ` marcandre.lureau
  2026-04-29 16:23 ` [PULL v2 00/15] UI patches Stefan Hajnoczi
  15 siblings, 0 replies; 17+ messages in thread
From: marcandre.lureau @ 2026-04-28 15:07 UTC (permalink / raw)
  To: qemu-devel
  Cc: stefanha, Jindřich Makovička, Paolo Bonzini,
	Marc-André Lureau, Daniel P. Berrangé,
	Philippe Mathieu-Daudé, Pierrick Bouvier, Eric Blake,
	Markus Armbruster, Alex Bennée

From: Jindřich Makovička <makovick@gmail.com>

- Compile the GTK clipboard support unconditionally

- Introduce GTK clipboard option, defaulting to off

Signed-off-by: Jindrich Makovicka <makovick@gmail.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-ID: <20260427-gtk-clipboard-v5-2-6968feb31a5d@gmail.com>
---
 meson.build                   | 4 ----
 qapi/ui.json                  | 6 +++++-
 ui/gtk.c                      | 8 +++++---
 meson_options.txt             | 7 -------
 qemu-options.hx               | 9 ++++++---
 scripts/meson-buildoptions.sh | 3 ---
 ui/meson.build                | 4 +---
 7 files changed, 17 insertions(+), 24 deletions(-)

diff --git a/meson.build b/meson.build
index 7d25b1ec7ef..4176d020c21 100644
--- a/meson.build
+++ b/meson.build
@@ -1932,7 +1932,6 @@ endif
 gtk = not_found
 gtkx11 = not_found
 vte = not_found
-have_gtk_clipboard = get_option('gtk_clipboard').enabled()
 
 if get_option('gtk') \
              .disable_auto_if(not have_system) \
@@ -1954,8 +1953,6 @@ if get_option('gtk') \
                        method: 'pkg-config',
                        required: get_option('vte'))
     endif
-  elif have_gtk_clipboard
-    error('GTK clipboard requested, but GTK not found')
   endif
 endif
 
@@ -2475,7 +2472,6 @@ if glusterfs.found()
 endif
 config_host_data.set('CONFIG_GTK', gtk.found())
 config_host_data.set('CONFIG_VTE', vte.found())
-config_host_data.set('CONFIG_GTK_CLIPBOARD', have_gtk_clipboard)
 config_host_data.set('CONFIG_HEXAGON_IDEF_PARSER', get_option('hexagon_idef_parser'))
 config_host_data.set('CONFIG_LIBATTR', have_old_libattr)
 config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found())
diff --git a/qapi/ui.json b/qapi/ui.json
index e3da77632a8..b2c42a7f578 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1319,6 +1319,9 @@
 #
 # GTK display options.
 #
+# @clipboard: Enable host-guest clipboard sharing.  Defaults to "off".
+#     (Since 11.1)
+#
 # @grab-on-hover: Grab keyboard input on mouse hover.
 #
 # @zoom-to-fit: Zoom guest display to fit into the host window.  When
@@ -1344,7 +1347,8 @@
 # Since: 2.12
 ##
 { 'struct'  : 'DisplayGTK',
-  'data'    : { '*grab-on-hover'     : 'bool',
+  'data'    : { '*clipboard'         : 'bool',
+                '*grab-on-hover'     : 'bool',
                 '*zoom-to-fit'       : 'bool',
                 '*show-tabs'         : 'bool',
                 '*show-menubar'      : 'bool',
diff --git a/ui/gtk.c b/ui/gtk.c
index 8fd39c90c29..ec95f0f294a 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -2601,9 +2601,11 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
         opts->u.gtk.show_tabs) {
         gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item));
     }
-#ifdef CONFIG_GTK_CLIPBOARD
-    gd_clipboard_init(s);
-#endif /* CONFIG_GTK_CLIPBOARD */
+
+    if (opts->u.gtk.has_clipboard &&
+        opts->u.gtk.clipboard) {
+        gd_clipboard_init(s);
+    }
 
     /* GTK's event polling must happen on the main thread. */
     qemu_main = NULL;
diff --git a/meson_options.txt b/meson_options.txt
index 31d5916cfce..286461129bd 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -262,13 +262,6 @@ option('vnc_sasl', type : 'feature', value : 'auto',
        description: 'SASL authentication for VNC server')
 option('vte', type : 'feature', value : 'auto',
        description: 'vte support for the gtk UI')
-
-# GTK Clipboard implementation is disabled by default, since it may cause hangs
-# of the guest VCPUs. See gitlab issue 1150:
-# https://gitlab.com/qemu-project/qemu/-/issues/1150
-
-option('gtk_clipboard', type: 'feature', value : 'disabled',
-       description: 'clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)')
 option('xkbcommon', type : 'feature', value : 'auto',
        description: 'xkbcommon support')
 option('zstd', type : 'feature', value : 'auto',
diff --git a/qemu-options.hx b/qemu-options.hx
index 21972f83268..e780bc2ac06 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2209,9 +2209,9 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
     "            [,window-close=on|off]\n"
 #endif
 #if defined(CONFIG_GTK)
-    "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n"
-    "            [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n"
-    "            [,show-menubar=on|off][,zoom-to-fit=on|off]\n"
+    "-display gtk[,clipboard=on|off][,full-screen=on|off][,gl=on|off]\n"
+    "            [,grab-on-hover=on|off][,show-tabs=on|off][,show-cursor=on|off]\n"
+    "            [,window-close=on|off][,show-menubar=on|off][,zoom-to-fit=on|off]\n"
 #endif
 #if defined(CONFIG_VNC)
     "-display vnc=<display>[,<optargs>]\n"
@@ -2295,6 +2295,9 @@ SRST
         drop-down menus and other UI elements to configure and control
         the VM during runtime. Valid parameters are:
 
+        ``clipboard=on|off`` : Enable host-guest clipboard sharing,
+                               defaults to "off"
+
         ``full-screen=on|off`` : Start in fullscreen mode
 
         ``gl=on|off`` : Use OpenGL for displaying
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index ca5b113119a..80de8c4af42 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -124,7 +124,6 @@ meson_options_help() {
   printf "%s\n" '  glusterfs       Glusterfs block device driver'
   printf "%s\n" '  gnutls          GNUTLS cryptography support'
   printf "%s\n" '  gtk             GTK+ user interface'
-  printf "%s\n" '  gtk-clipboard   clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)'
   printf "%s\n" '  guest-agent     Build QEMU Guest Agent'
   printf "%s\n" '  guest-agent-msi Build MSI package for the QEMU Guest Agent'
   printf "%s\n" '  hv-balloon      hv-balloon driver (requires Glib 2.68+ GTree API)'
@@ -336,8 +335,6 @@ _meson_option_parse() {
     --disable-gnutls) printf "%s" -Dgnutls=disabled ;;
     --enable-gtk) printf "%s" -Dgtk=enabled ;;
     --disable-gtk) printf "%s" -Dgtk=disabled ;;
-    --enable-gtk-clipboard) printf "%s" -Dgtk_clipboard=enabled ;;
-    --disable-gtk-clipboard) printf "%s" -Dgtk_clipboard=disabled ;;
     --enable-guest-agent) printf "%s" -Dguest_agent=enabled ;;
     --disable-guest-agent) printf "%s" -Dguest_agent=disabled ;;
     --enable-guest-agent-msi) printf "%s" -Dguest_agent_msi=enabled ;;
diff --git a/ui/meson.build b/ui/meson.build
index 3d15ff96287..ceaf110683d 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -112,9 +112,7 @@ if gtk.found()
 
   gtk_ss = ss.source_set()
   gtk_ss.add(gtk, vte, pixman, files('gtk.c'))
-  if have_gtk_clipboard
-    gtk_ss.add(files('gtk-clipboard.c'))
-  endif
+  gtk_ss.add(files('gtk-clipboard.c'))
   gtk_ss.add(when: x11, if_true: files('x_keymap.c'))
   gtk_ss.add(when: opengl, if_true: files('gtk-gl-area.c'))
   gtk_ss.add(when: [x11, opengl], if_true: files('gtk-egl.c'))
-- 
2.54.0



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

* Re: [PULL v2 00/15] UI patches
  2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
                   ` (14 preceding siblings ...)
  2026-04-28 15:07 ` [PULL v2 15/15] ui/gtk: Turn clipboard flag into runtime option marcandre.lureau
@ 2026-04-29 16:23 ` Stefan Hajnoczi
  15 siblings, 0 replies; 17+ messages in thread
From: Stefan Hajnoczi @ 2026-04-29 16:23 UTC (permalink / raw)
  To: marcandre.lureau; +Cc: qemu-devel, stefanha, Marc-André Lureau

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

Applied, thanks.

Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

end of thread, other threads:[~2026-04-29 16:24 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-28 15:07 [PULL v2 00/15] UI patches marcandre.lureau
2026-04-28 15:07 ` [PULL v2 01/15] ui: move FONT_WIDTH/HEIGHT to vgafont.h marcandre.lureau
2026-04-28 15:07 ` [PULL v2 02/15] ui: move DisplaySurface functions to display-surface.c marcandre.lureau
2026-04-28 15:07 ` [PULL v2 03/15] ui: make qemu_default_pixelformat() static inline marcandre.lureau
2026-04-28 15:07 ` [PULL v2 04/15] ui: make unregister_displaychangelistener() skip unregistered marcandre.lureau
2026-04-28 15:07 ` [PULL v2 05/15] ui: minor code simplification marcandre.lureau
2026-04-28 15:07 ` [PULL v2 06/15] ui: rename init_keyboard_layout->kbd_layout_new() marcandre.lureau
2026-04-28 15:07 ` [PULL v2 07/15] ui/console: remove qemu_console_is_visible() marcandre.lureau
2026-04-28 15:07 ` [PULL v2 08/15] ui/console: return completion status from gfx_update callback marcandre.lureau
2026-04-28 15:07 ` [PULL v2 09/15] ui/console: move console_handle_touch_event() to input marcandre.lureau
2026-04-28 15:07 ` [PULL v2 10/15] ui/dbus: fix warning for clients without "Interfaces" property marcandre.lureau
2026-04-28 15:07 ` [PULL v2 11/15] audio: Avoid unsigned sample wraparound marcandre.lureau
2026-04-28 15:07 ` [PULL v2 12/15] audio: Clamp unsigned sample conversion marcandre.lureau
2026-04-28 15:07 ` [PULL v2 13/15] audio: Use unsigned PCM bias marcandre.lureau
2026-04-28 15:07 ` [PULL v2 14/15] ui/gtk: Use non-blocking clipboard retrieval marcandre.lureau
2026-04-28 15:07 ` [PULL v2 15/15] ui/gtk: Turn clipboard flag into runtime option marcandre.lureau
2026-04-29 16:23 ` [PULL v2 00/15] UI patches Stefan Hajnoczi

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.