public inbox for qemu-devel@nongnu.org
 help / color / mirror / Atom feed
* [PATCH 00/60] ui: add standalone VNC server over D-Bus
@ 2026-03-17  8:50 Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 01/60] ui/vnc-jobs: fix VncRectEntry leak on job cleanup Marc-André Lureau
                   ` (60 more replies)
  0 siblings, 61 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

This series adds qemu-vnc, a standalone VNC server that connects to a
running QEMU instance via the D-Bus display interface (org.qemu.Display1).
This allows serving a VNC display as a separate process with an independent
lifecycle and privilege domain, without requiring VNC support compiled into
the QEMU system emulator itself.

The bulk of the series is preparatory refactoring:

- Clean up VNC code: merge init/open, fix leaks, simplify error handling
- Extract and clean up VT100 emulation from console-vc into a reusable unit
- Reorganize ui/ code: move DisplaySurface functions, vgafont, datadir
and other pieces into their own files
- Refactor console APIs: rename methods, simplify listener registration,
return completion status from gfx_update
- Extract common ui sources into a static library that can be linked by
both the system emulator and the new standalone binary

The final patch adds contrib/qemu-vnc, built when both VNC and D-Bus
display support are enabled.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
Marc-André Lureau (60):
      ui/vnc-jobs: fix VncRectEntry leak on job cleanup
      ui/vnc-jobs: clear source tag
      ui/vnc-jobs: remove needless buffer_reset() before end
      ui/vnc: clarify intent using buffer_empty() function
      ui/vnc-jobs: vnc_has_job_locked() argument cannot be NULL
      ui/vnc-jobs: remove dead VncJobQueue.exit
      ui/vnc-jobs: remove vnc_queue_clear()
      ui/vnc-jobs: narrow taking the lock when pushing empty jobs
      ui/vnc-jobs: drop redundant (and needless) qemu_thread_get_self()
      ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen)
      ui/console-vc: add UTF-8 input decoding with CP437 rendering
      ui/console-vc: ignore string-type escape sequences
      ui/console-vc: fix comment shift-out/in comments
      ui/console: dispatch get_label() through QOM virtual method
      ui/console-vc: introduce QemuVT100
      ui/console-vc: set vt100 associated pixman image
      ui/console-vc: vga_putcharxy()->vt100_putcharxy()
      ui/console-vc: make invalidate_xy() take vt100
      ui/console-vc: make show_cursor() take vt100
      ui/console-vc: decouple VT100 display updates via function pointer
      ui/console-vc: console_refresh() -> vt100_refresh()
      ui/console-vc: move cursor blinking logic into VT100 layer
      ui/console-vc: console_scroll() -> vt100_scroll()
      ui/console-vc: refactor text_console_resize() into vt100_set_image()
      ui/console-vc: move vc_put_lf() to VT100 layer as vt100_put_lf()
      ui/console-vc: unify the write path
      ui/console-vc: move VT100 state machine and output FIFO into QemuVT100
      ui/console-vc: extract vt100_input() from vc_chr_write()
      ui/console-vc: extract vt100_keysym() from qemu_text_console_handle_keysym()
      ui/console-vc: extract vt100_init() and vt100_fini()
      ui/console: remove console_ch_t typedef and console_write_ch()
      ui: avoid duplicating vgafont16 in each translation unit
      ui/vgafont: add SPDX license header
      ui: move FONT_WIDTH/HEIGHT to vgafont.h
      ui/console-vc: move VT100 emulation into separate unit
      util: move datadir.c from system/
      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
      system: make qemu_del_vm_change_state_handler accept NULL
      ui/vnc: assert preconditions instead of silently returning
      ui/vnc: simplify vnc_init_func error handling
      ui/vnc: VncDisplay.id is not const
      ui/vnc: fix vnc_display_init() leak on failure
      ui/vnc: merge vnc_display_init() and vnc_display_open()
      ui/vnc: report an error for duplicate display id
      ui/vnc: defer listener registration until the console is known
      ui/vnc: explicitly link with png
      ui/vnc: add vnc-system unit, to allow different implementations
      ui/console: remove qemu_console_is_visible()
      ui/console: simplify registering display/console change listener
      ui/console: return completion status from gfx_update callback
      ui/console: rename public API to use consistent qemu_console_ prefix
      ui/console: move console_handle_touch_event() to input
      ui: extract common sources into a static library
      tests: rename the dbus-daemon helper script
      tests/qtest: fix dbus-vmstate-test compilation
      tests/qtest: drop DBUS_VMSTATE_TEST_TMPDIR
      contrib/qemu-vnc: add standalone VNC server over D-Bus

 MAINTAINERS                                      |    5 +
 docs/conf.py                                     |    3 +
 docs/interop/dbus-display.rst                    |    2 +
 docs/interop/dbus-vnc.rst                        |   26 +
 docs/interop/index.rst                           |    1 +
 docs/meson.build                                 |    1 +
 docs/tools/index.rst                             |    1 +
 docs/tools/qemu-vnc.rst                          |  199 +
 meson.build                                      |   17 +
 contrib/qemu-vnc/qemu-vnc.h                      |   46 +
 contrib/qemu-vnc/trace.h                         |    4 +
 hw/display/qxl.h                                 |    2 +-
 include/ui/console.h                             |  146 +-
 include/ui/input.h                               |   15 +
 include/ui/qemu-pixman.h                         |    8 +
 include/ui/surface.h                             |    2 -
 ui/console-priv.h                                |    8 -
 ui/cp437.h                                       |   13 +
 ui/keymaps.h                                     |    1 +
 ui/vgafont.h                                     | 4618 +---------------------
 ui/vnc.h                                         |    9 +-
 ui/vt100.h                                       |   92 +
 contrib/qemu-vnc/audio.c                         |  307 ++
 contrib/qemu-vnc/chardev.c                       |  127 +
 contrib/qemu-vnc/clipboard.c                     |  378 ++
 contrib/qemu-vnc/console.c                       |  168 +
 contrib/qemu-vnc/dbus.c                          |  439 ++
 contrib/qemu-vnc/display.c                       |  456 +++
 contrib/qemu-vnc/input.c                         |  239 ++
 contrib/qemu-vnc/qemu-vnc.c                      |  450 +++
 contrib/qemu-vnc/stubs.c                         |   66 +
 contrib/qemu-vnc/utils.c                         |   59 +
 hw/arm/musicpal.c                                |    7 +-
 hw/display/artist.c                              |    8 +-
 hw/display/ati.c                                 |   16 +-
 hw/display/bcm2835_fb.c                          |   12 +-
 hw/display/bochs-display.c                       |   20 +-
 hw/display/cg3.c                                 |   11 +-
 hw/display/cirrus_vga.c                          |    8 +-
 hw/display/cirrus_vga_isa.c                      |    2 +-
 hw/display/dm163.c                               |   10 +-
 hw/display/exynos4210_fimd.c                     |   10 +-
 hw/display/g364fb.c                              |   19 +-
 hw/display/jazz_led.c                            |   24 +-
 hw/display/macfb.c                               |   12 +-
 hw/display/next-fb.c                             |    8 +-
 hw/display/omap_lcdc.c                           |   18 +-
 hw/display/pl110.c                               |    9 +-
 hw/display/qxl-render.c                          |   18 +-
 hw/display/qxl.c                                 |   25 +-
 hw/display/ramfb-standalone.c                    |    6 +-
 hw/display/ramfb.c                               |    4 +-
 hw/display/sm501.c                               |   14 +-
 hw/display/ssd0303.c                             |   14 +-
 hw/display/ssd0323.c                             |   16 +-
 hw/display/tcx.c                                 |   22 +-
 hw/display/vga-isa.c                             |    2 +-
 hw/display/vga-mmio.c                            |    2 +-
 hw/display/vga-pci.c                             |    6 +-
 hw/display/vga.c                                 |   60 +-
 hw/display/vhost-user-gpu.c                      |   22 +-
 hw/display/virtio-gpu-base.c                     |    7 +-
 hw/display/virtio-gpu-rutabaga.c                 |   10 +-
 hw/display/virtio-gpu-udmabuf.c                  |    4 +-
 hw/display/virtio-gpu-virgl.c                    |   20 +-
 hw/display/virtio-gpu.c                          |   26 +-
 hw/display/virtio-vga.c                          |   10 +-
 hw/display/vmware_vga.c                          |   21 +-
 hw/display/xenfb.c                               |   12 +-
 hw/display/xlnx_dp.c                             |   20 +-
 hw/vfio/display.c                                |   49 +-
 system/runstate.c                                |    3 +
 tests/qtest/dbus-vmstate-test.c                  |   13 +-
 tests/qtest/dbus-vnc-test.c                      |  733 ++++
 ui/console-vc-stubs.c                            |    9 +-
 ui/console-vc.c                                  | 1021 +----
 ui/console.c                                     |  471 +--
 ui/cp437.c                                       |  205 +
 ui/curses.c                                      |   23 +-
 ui/dbus-console.c                                |   16 +-
 ui/dbus-listener.c                               |   37 +-
 ui/display-surface.c                             |  107 +
 ui/egl-headless.c                                |    8 +-
 ui/gtk-egl.c                                     |    6 +-
 ui/gtk-gl-area.c                                 |    6 +-
 ui/gtk.c                                         |   36 +-
 ui/input.c                                       |   65 +
 ui/keymaps.c                                     |   13 +-
 ui/sdl2-2d.c                                     |    2 +-
 ui/sdl2-gl.c                                     |    2 +-
 ui/sdl2.c                                        |   24 +-
 ui/spice-display.c                               |   24 +-
 ui/vgafont.c                                     | 4616 +++++++++++++++++++++
 ui/vnc-jobs.c                                    |   54 +-
 ui/vnc-system.c                                  |   19 +
 ui/vnc.c                                         |  202 +-
 ui/vt100.c                                       |  987 +++++
 {system => util}/datadir.c                       |    0
 contrib/qemu-vnc/meson.build                     |   26 +
 contrib/qemu-vnc/qemu-vnc1.xml                   |  174 +
 contrib/qemu-vnc/trace-events                    |   20 +
 hw/display/apple-gfx.m                           |   26 +-
 meson_options.txt                                |    2 +
 scripts/meson-buildoptions.sh                    |    3 +
 system/meson.build                               |    1 -
 system/trace-events                              |    1 -
 tests/{dbus-vmstate-daemon.sh => dbus-daemon.sh} |   16 +-
 tests/qtest/meson.build                          |   13 +-
 ui/cocoa.m                                       |   23 +-
 ui/meson.build                                   |  103 +-
 util/meson.build                                 |    1 +
 util/trace-events                                |    3 +
 112 files changed, 10982 insertions(+), 6624 deletions(-)
---
base-commit: 559919ce54927d59b215a4665eda7ab6118a48aa
change-id: 20260312-qemu-vnc-9662fc572262

Best regards,
-- 
Marc-André Lureau <marcandre.lureau@redhat.com>



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

* [PATCH 01/60] ui/vnc-jobs: fix VncRectEntry leak on job cleanup
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 13:43   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 02/60] ui/vnc-jobs: clear source tag Marc-André Lureau
                   ` (59 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

When a VncJob is freed, its associated VncRectEntry list must also be
freed. Previously, vnc_job_push() and the disconnected path in
vnc_worker_thread_loop() called g_free(job) directly, leaking all
VncRectEntry allocations.

Introduce vnc_job_free() which iterates and frees the rectangle entries
before freeing the job itself, and use it in both paths.

Also add QLIST_REMOVE() in the worker loop before g_free(entry), so
that entries processed during normal operation are properly unlinked.
Without this, vnc_job_free() would iterate dangling pointers to
already-freed entries, causing use-after-free.

Fixes: bd023f953e5e ("vnc: threaded VNC server")
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vnc-jobs.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index b296d19e089..ca625da6d05 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -107,11 +107,25 @@ int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
     return 1;
 }
 
+static void vnc_job_free(VncJob *job)
+{
+    VncRectEntry *entry, *tmp;
+
+    if (!job) {
+        return;
+    }
+    QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
+        /* no need for QLIST_REMOVE(entry, next) */
+        g_free(entry);
+    }
+    g_free(job);
+}
+
 void vnc_job_push(VncJob *job)
 {
     vnc_lock_queue(queue);
     if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
-        g_free(job);
+        vnc_job_free(job);
     } else {
         QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
         qemu_cond_broadcast(&queue->cond);
@@ -296,6 +310,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
                 n_rectangles += n;
             }
         }
+        QLIST_REMOVE(entry, next);
         g_free(entry);
     }
     trace_vnc_job_nrects(&vs, job, n_rectangles);
@@ -324,7 +339,7 @@ disconnected:
     QTAILQ_REMOVE(&queue->jobs, job, next);
     vnc_unlock_queue(queue);
     qemu_cond_broadcast(&queue->cond);
-    g_free(job);
+    vnc_job_free(job);
     vs.magic = 0;
     return 0;
 }

-- 
2.53.0



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

* [PATCH 02/60] ui/vnc-jobs: clear source tag
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 01/60] ui/vnc-jobs: fix VncRectEntry leak on job cleanup Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 13:44   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 03/60] ui/vnc-jobs: remove needless buffer_reset() before end Marc-André Lureau
                   ` (58 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Avoid potentially removing a dangling source & simplify code.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vnc-jobs.c |  4 +---
 ui/vnc.c      | 31 +++++++------------------------
 2 files changed, 8 insertions(+), 27 deletions(-)

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index ca625da6d05..ec90ae6d5fc 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -162,9 +162,7 @@ void vnc_jobs_consume_buffer(VncState *vs)
     vnc_lock_output(vs);
     if (vs->jobs_buffer.offset) {
         if (vs->ioc != NULL && buffer_empty(&vs->output)) {
-            if (vs->ioc_tag) {
-                g_source_remove(vs->ioc_tag);
-            }
+            g_clear_handle_id(&vs->ioc_tag, g_source_remove);
             if (vs->disconnecting == FALSE) {
                 vs->ioc_tag = qio_channel_add_watch(
                     vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT,
diff --git a/ui/vnc.c b/ui/vnc.c
index 952976e9649..ccc73bd7aa4 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1301,10 +1301,7 @@ static void vnc_disconnect_start(VncState *vs)
     }
     trace_vnc_client_disconnect_start(vs, vs->ioc);
     vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
-    if (vs->ioc_tag) {
-        g_source_remove(vs->ioc_tag);
-        vs->ioc_tag = 0;
-    }
+    g_clear_handle_id(&vs->ioc_tag, g_source_remove);
     qio_channel_close(vs->ioc, NULL);
     vs->disconnecting = TRUE;
 }
@@ -1462,9 +1459,7 @@ static size_t vnc_client_write_plain(VncState *vs)
     }
 
     if (vs->output.offset == 0) {
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
+        g_clear_handle_id(&vs->ioc_tag, g_source_remove);
         vs->ioc_tag = qio_channel_add_watch(
             vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
             vnc_client_io, vs, NULL);
@@ -1500,9 +1495,7 @@ static void vnc_client_write(VncState *vs)
     if (vs->output.offset) {
         vnc_client_write_locked(vs);
     } else if (vs->ioc != NULL) {
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
+        g_clear_handle_id(&vs->ioc_tag, g_source_remove);
         vs->ioc_tag = qio_channel_add_watch(
             vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
             vnc_client_io, vs, NULL);
@@ -1638,10 +1631,7 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
     }
 
     if (vs->disconnecting) {
-        if (vs->ioc_tag != 0) {
-            g_source_remove(vs->ioc_tag);
-        }
-        vs->ioc_tag = 0;
+        g_clear_handle_id(&vs->ioc_tag, g_source_remove);
     }
     return TRUE;
 }
@@ -1684,9 +1674,7 @@ void vnc_write(VncState *vs, const void *data, size_t len)
     buffer_reserve(&vs->output, len);
 
     if (vs->ioc != NULL && buffer_empty(&vs->output)) {
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
+        g_clear_handle_id(&vs->ioc_tag, g_source_remove);
         vs->ioc_tag = qio_channel_add_watch(
             vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT,
             vnc_client_io, vs, NULL);
@@ -1734,10 +1722,7 @@ void vnc_flush(VncState *vs)
         vnc_client_write_locked(vs);
     }
     if (vs->disconnecting) {
-        if (vs->ioc_tag != 0) {
-            g_source_remove(vs->ioc_tag);
-        }
-        vs->ioc_tag = 0;
+        g_clear_handle_id(&vs->ioc_tag, g_source_remove);
     }
     vnc_unlock_output(vs);
 }
@@ -3342,9 +3327,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
     VNC_DEBUG("New client on socket %p\n", vs->sioc);
     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
     qio_channel_set_blocking(vs->ioc, false, &error_abort);
-    if (vs->ioc_tag) {
-        g_source_remove(vs->ioc_tag);
-    }
+    g_clear_handle_id(&vs->ioc_tag, g_source_remove);
     if (websocket) {
         vs->websocket = 1;
         if (vd->tlscreds) {

-- 
2.53.0



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

* [PATCH 03/60] ui/vnc-jobs: remove needless buffer_reset() before end
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 01/60] ui/vnc-jobs: fix VncRectEntry leak on job cleanup Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 02/60] ui/vnc-jobs: clear source tag Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 13:45   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 04/60] ui/vnc: clarify intent using buffer_empty() function Marc-André Lureau
                   ` (57 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

vnc_async_encoding_end() does buffer_free() next.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vnc-jobs.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index ec90ae6d5fc..a6ab733faa1 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -326,7 +326,6 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
 
         qemu_bh_schedule(job->vs->bh);
     }  else {
-        buffer_reset(&vs.output);
         /* Copy persistent encoding data */
         vnc_async_encoding_end(job->vs, &vs);
     }

-- 
2.53.0



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

* [PATCH 04/60] ui/vnc: clarify intent using buffer_empty() function
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (2 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 03/60] ui/vnc-jobs: remove needless buffer_reset() before end Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 13:45   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 05/60] ui/vnc-jobs: vnc_has_job_locked() argument cannot be NULL Marc-André Lureau
                   ` (56 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vnc-jobs.c | 2 +-
 ui/vnc.c      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index a6ab733faa1..4dd7ccad969 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -160,7 +160,7 @@ void vnc_jobs_consume_buffer(VncState *vs)
     bool flush;
 
     vnc_lock_output(vs);
-    if (vs->jobs_buffer.offset) {
+    if (!buffer_empty(&vs->jobs_buffer)) {
         if (vs->ioc != NULL && buffer_empty(&vs->output)) {
             g_clear_handle_id(&vs->ioc_tag, g_source_remove);
             if (vs->disconnecting == FALSE) {
diff --git a/ui/vnc.c b/ui/vnc.c
index ccc73bd7aa4..4aa446a48d7 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1718,7 +1718,7 @@ void vnc_write_u8(VncState *vs, uint8_t value)
 void vnc_flush(VncState *vs)
 {
     vnc_lock_output(vs);
-    if (vs->ioc != NULL && vs->output.offset) {
+    if (vs->ioc != NULL && !buffer_empty(&vs->output)) {
         vnc_client_write_locked(vs);
     }
     if (vs->disconnecting) {

-- 
2.53.0



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

* [PATCH 05/60] ui/vnc-jobs: vnc_has_job_locked() argument cannot be NULL
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (3 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 04/60] ui/vnc: clarify intent using buffer_empty() function Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 13:46   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 06/60] ui/vnc-jobs: remove dead VncJobQueue.exit Marc-André Lureau
                   ` (55 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

The only caller, vnc_jobs_join() cannot take vs == NULL argument, or it
would later crash.

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

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 4dd7ccad969..8cb30e72276 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -138,7 +138,7 @@ static bool vnc_has_job_locked(VncState *vs)
     VncJob *job;
 
     QTAILQ_FOREACH(job, &queue->jobs, next) {
-        if (job->vs == vs || !vs) {
+        if (job->vs == vs) {
             return true;
         }
     }

-- 
2.53.0



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

* [PATCH 06/60] ui/vnc-jobs: remove dead VncJobQueue.exit
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (4 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 05/60] ui/vnc-jobs: vnc_has_job_locked() argument cannot be NULL Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 13:49   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 07/60] ui/vnc-jobs: remove vnc_queue_clear() Marc-André Lureau
                   ` (54 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Since commit 09526058d0a5 ("ui/vnc: Remove vnc_stop_worker_thread()"),
it's not used anymore. It seems stopping worker thread hasn't been
supported ever.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vnc-jobs.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 8cb30e72276..9e536b07b90 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -56,7 +56,6 @@ struct VncJobQueue {
     QemuCond cond;
     QemuMutex mutex;
     QemuThread thread;
-    bool exit;
     QTAILQ_HEAD(, VncJob) jobs;
 };
 
@@ -124,7 +123,7 @@ static void vnc_job_free(VncJob *job)
 void vnc_job_push(VncJob *job)
 {
     vnc_lock_queue(queue);
-    if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
+    if (QLIST_EMPTY(&job->rectangles)) {
         vnc_job_free(job);
     } else {
         QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
@@ -248,17 +247,12 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
     int saved_offset;
 
     vnc_lock_queue(queue);
-    while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
+    while (QTAILQ_EMPTY(&queue->jobs)) {
         qemu_cond_wait(&queue->cond, &queue->mutex);
     }
-    /* Here job can only be NULL if queue->exit is true */
     job = QTAILQ_FIRST(&queue->jobs);
     vnc_unlock_queue(queue);
 
-    if (queue->exit) {
-        return -1;
-    }
-
     assert(job->vs->magic == VNC_MAGIC);
     vc = container_of(job->vs, VncConnection, vs);
 

-- 
2.53.0



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

* [PATCH 07/60] ui/vnc-jobs: remove vnc_queue_clear()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (5 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 06/60] ui/vnc-jobs: remove dead VncJobQueue.exit Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 13:51   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 08/60] ui/vnc-jobs: narrow taking the lock when pushing empty jobs Marc-André Lureau
                   ` (53 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

The function was never called, since the worker thread was never
exiting. Also it was incomplete (not clearing pending job list) and
mixing global queue and argument. Let's remove it.

Note: maybe the worker thread could be torn down when vnc_jobs_join()
realizes there is no job left.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vnc-jobs.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 9e536b07b90..28a4738f1ec 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -345,14 +345,6 @@ static VncJobQueue *vnc_queue_init(void)
     return queue;
 }
 
-static void vnc_queue_clear(VncJobQueue *q)
-{
-    qemu_cond_destroy(&queue->cond);
-    qemu_mutex_destroy(&queue->mutex);
-    g_free(q);
-    queue = NULL; /* Unset global queue */
-}
-
 static void *vnc_worker_thread(void *arg)
 {
     VncJobQueue *queue = arg;
@@ -360,7 +352,7 @@ static void *vnc_worker_thread(void *arg)
     qemu_thread_get_self(&queue->thread);
 
     while (!vnc_worker_thread_loop(queue)) ;
-    vnc_queue_clear(queue);
+    g_assert_not_reached();
     return NULL;
 }
 

-- 
2.53.0



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

* [PATCH 08/60] ui/vnc-jobs: narrow taking the lock when pushing empty jobs
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (6 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 07/60] ui/vnc-jobs: remove vnc_queue_clear() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 13:53   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 09/60] ui/vnc-jobs: drop redundant (and needless) qemu_thread_get_self() Marc-André Lureau
                   ` (52 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

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

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 28a4738f1ec..ae925171fae 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -122,14 +122,14 @@ static void vnc_job_free(VncJob *job)
 
 void vnc_job_push(VncJob *job)
 {
-    vnc_lock_queue(queue);
     if (QLIST_EMPTY(&job->rectangles)) {
         vnc_job_free(job);
     } else {
+        vnc_lock_queue(queue);
         QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
         qemu_cond_broadcast(&queue->cond);
+        vnc_unlock_queue(queue);
     }
-    vnc_unlock_queue(queue);
 }
 
 static bool vnc_has_job_locked(VncState *vs)

-- 
2.53.0



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

* [PATCH 09/60] ui/vnc-jobs: drop redundant (and needless) qemu_thread_get_self()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (7 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 08/60] ui/vnc-jobs: narrow taking the lock when pushing empty jobs Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:00   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 10/60] ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen) Marc-André Lureau
                   ` (51 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

The call is unnecessary, since "thread" is already set at creation time.
Furthermore, the "thread" field is mostly useless as the thread is
created DETACHED and isn't used for anything but perhaps debugging.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vnc-jobs.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index ae925171fae..0523e52cbd2 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -349,8 +349,6 @@ static void *vnc_worker_thread(void *arg)
 {
     VncJobQueue *queue = arg;
 
-    qemu_thread_get_self(&queue->thread);
-
     while (!vnc_worker_thread_loop(queue)) ;
     g_assert_not_reached();
     return NULL;

-- 
2.53.0



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

* [PATCH 10/60] ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen)
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (8 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 09/60] ui/vnc-jobs: drop redundant (and needless) qemu_thread_get_self() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:03   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering Marc-André Lureau
                   ` (50 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

The loop condition used `y <= s->height` instead of `y < s->height`,
causing vc_clear_xy() to be called with y == s->height. This clears
a row in the scrollback buffer beyond the visible screen.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index f22806fed79..8dee1f9bd01 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -899,7 +899,7 @@ static void vc_putchar(VCChardev *vc, int ch)
                     break;
                 case 2:
                     /* clear entire screen */
-                    for (y = 0; y <= s->height; y++) {
+                    for (y = 0; y < s->height; y++) {
                         for (x = 0; x < s->width; x++) {
                             vc_clear_xy(vc, x, y);
                         }

-- 
2.53.0



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

* [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (9 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 10/60] ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen) Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:07   ` Daniel P. Berrangé
  2026-03-25  5:35   ` Markus Armbruster
  2026-03-17  8:50 ` [PATCH 12/60] ui/console-vc: ignore string-type escape sequences Marc-André Lureau
                   ` (49 subsequent siblings)
  60 siblings, 2 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

The text console receives bytes that may be UTF-8 encoded (e.g. from
a guest running a modern distro), but currently treats each byte as a
raw character index into the VGA/CP437 font, producing garbled output
for any multi-byte sequence.

Add a proper UTF-8 decoder using Bjoern Hoehrmann's DFA.
The DFA inherently rejects overlong encodings, surrogates, and
codepoints above U+10FFFF.  Completed codepoints are then mapped to
CP437, unmappable characters are displayed as '?'.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/cp437.h      |  13 ++++
 ui/console-vc.c |  62 +++++++++++++++++
 ui/cp437.c      | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ui/meson.build  |   2 +-
 4 files changed, 281 insertions(+), 1 deletion(-)

diff --git a/ui/cp437.h b/ui/cp437.h
new file mode 100644
index 00000000000..81ace8317c7
--- /dev/null
+++ b/ui/cp437.h
@@ -0,0 +1,13 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) QEMU contributors
+ */
+#ifndef QEMU_CP437_H
+#define QEMU_CP437_H
+
+#include <stdint.h>
+
+int unicode_to_cp437(uint32_t codepoint);
+
+#endif /* QEMU_CP437_H */
diff --git a/ui/console-vc.c b/ui/console-vc.c
index 8dee1f9bd01..7bbd65dea27 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -9,6 +9,7 @@
 #include "qemu/fifo8.h"
 #include "qemu/option.h"
 #include "ui/console.h"
+#include "ui/cp437.h"
 
 #include "trace.h"
 #include "console-priv.h"
@@ -89,6 +90,8 @@ struct VCChardev {
     enum TTYState state;
     int esc_params[MAX_ESC_PARAMS];
     int nb_esc_params;
+    uint32_t utf8_state;     /* UTF-8 DFA decoder state */
+    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
     TextAttributes t_attrib; /* currently active text attributes */
     TextAttributes t_attrib_saved;
     int x_saved, y_saved;
@@ -598,6 +601,47 @@ static void vc_clear_xy(VCChardev *vc, int x, int y)
     vc_update_xy(vc, x, y);
 }
 
+/*
+ * UTF-8 DFA decoder by Bjoern Hoehrmann.
+ * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ * See https://github.com/polijan/utf8_decode for details.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 12
+
+static const uint8_t utf8d[] = {
+    /* character class lookup */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+    8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+   10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+    /* state transition lookup */
+     0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+    12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+    12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+    12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+    12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static uint32_t utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
+{
+    uint32_t type = utf8d[byte];
+
+    *codep = (*state != UTF8_ACCEPT) ?
+        (byte & 0x3fu) | (*codep << 6) :
+        (0xffu >> type) & (byte);
+
+    *state = utf8d[256 + *state + type];
+    return *state;
+}
+
 static void vc_put_one(VCChardev *vc, int ch)
 {
     QemuTextConsole *s = vc->console;
@@ -761,6 +805,24 @@ static void vc_putchar(VCChardev *vc, int ch)
 
     switch(vc->state) {
     case TTY_STATE_NORM:
+        /* Feed byte through the UTF-8 DFA decoder */
+        if (ch >= 0x80) {
+            switch (utf8_decode(&vc->utf8_state, &vc->utf8_codepoint, ch)) {
+            case UTF8_ACCEPT:
+                vc_put_one(vc, unicode_to_cp437(vc->utf8_codepoint));
+                break;
+            case UTF8_REJECT:
+                /* Reset state so the decoder can resync */
+                vc->utf8_state = UTF8_ACCEPT;
+                break;
+            default:
+                /* Need more bytes */
+                break;
+            }
+            break;
+        }
+        /* ASCII byte: abort any pending UTF-8 sequence */
+        vc->utf8_state = UTF8_ACCEPT;
         switch(ch) {
         case '\r':  /* carriage return */
             s->x = 0;
diff --git a/ui/cp437.c b/ui/cp437.c
new file mode 100644
index 00000000000..8ec38b73419
--- /dev/null
+++ b/ui/cp437.c
@@ -0,0 +1,205 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) QEMU contributors
+ */
+#include "qemu/osdep.h"
+#include "cp437.h"
+
+/*
+ * Unicode to CP437 page tables.
+ *
+ * Borrowed from the Linux kernel (fs/nls/nls_cp437.c, "Dual BSD/GPL"),
+ * generated from the Unicode Organization tables (www.unicode.org).
+ */
+static const unsigned char uni2cp437_page00[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+    0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, /* 0xa0-0xa7 */
+    0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+    0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, /* 0xb0-0xb7 */
+    0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8, /* 0xb8-0xbf */
+    0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
+    0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+    0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, /* 0xd0-0xd7 */
+    0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
+    0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
+    0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, /* 0xe8-0xef */
+    0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, /* 0xf0-0xf7 */
+    0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98, /* 0xf8-0xff */
+};
+
+static const unsigned char uni2cp437_page01[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+    0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static const unsigned char uni2cp437_page03[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+    0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+    0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+    0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
+    0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+    0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+    0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
+};
+
+static const unsigned char uni2cp437_page20[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xa0-0xa7 */
+};
+
+static const unsigned char uni2cp437_page22[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+    0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+    0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+    0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+    0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static const unsigned char uni2cp437_page23[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+    0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+    0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static const unsigned char uni2cp437_page25[256] = {
+    0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+    0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+    0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+    0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+    0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+    0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+    0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+    0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+    0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+    0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+    0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+    0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+    0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+    0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+    0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+    0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static const unsigned char *const uni2cp437_page[256] = {
+    [0x00] = uni2cp437_page00, [0x01] = uni2cp437_page01,
+    [0x03] = uni2cp437_page03, [0x20] = uni2cp437_page20,
+    [0x22] = uni2cp437_page22, [0x23] = uni2cp437_page23,
+    [0x25] = uni2cp437_page25,
+};
+
+/*
+ * Convert a Unicode code point to its CP437 equivalent for
+ * rendering with the VGA font.
+ * Returns '?' for characters that cannot be mapped.
+ */
+int unicode_to_cp437(uint32_t codepoint)
+{
+    const unsigned char *page;
+    unsigned char hi = (codepoint >> 8) & 0xff;
+    unsigned char lo = codepoint & 0xff;
+
+    if (codepoint > 0xffff) {
+        return '?';
+    }
+
+    page = uni2cp437_page[hi];
+    if (page && page[lo]) {
+        return page[lo];
+    }
+
+    return '?';
+}
diff --git a/ui/meson.build b/ui/meson.build
index 69404bca71a..d4d9312b98c 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -16,7 +16,7 @@ system_ss.add(files(
   'ui-qmp-cmds.c',
   'util.c',
 ))
-system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c'))
+system_ss.add(when: pixman, if_true: files('console-vc.c', 'cp437.c'), if_false: files('console-vc-stubs.c'))
 if dbus_display
   system_ss.add(files('dbus-module.c'))
 endif

-- 
2.53.0



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

* [PATCH 12/60] ui/console-vc: ignore string-type escape sequences
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (10 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 13/60] ui/console-vc: fix comment shift-out/in comments Marc-André Lureau
                   ` (48 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Modern terminals and applications emit OSC (Operating System Command),
DCS, SOS, PM, and APC escape sequences (e.g. for setting window
titles).  The text console currently does not recognise these
string-type introducers, so each byte of the payload is interpreted as
a normal character or a new escape, producing garbage on screen.

Add a TTY_STATE_OSC state that silently consumes all bytes until the
sequence is terminated by BEL or ST (ESC \).

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 7bbd65dea27..5eca9a5c004 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -45,6 +45,7 @@ enum TTYState {
     TTY_STATE_CSI,
     TTY_STATE_G0,
     TTY_STATE_G1,
+    TTY_STATE_OSC,
 };
 
 typedef struct QemuTextConsole {
@@ -869,6 +870,10 @@ static void vc_putchar(VCChardev *vc, int ch)
             vc->state = TTY_STATE_G0;
         } else if (ch == ')') {
             vc->state = TTY_STATE_G1;
+        } else if (ch == ']' || ch == 'P' || ch == 'X'
+                   || ch == '^' || ch == '_') {
+            /* String sequences: OSC, DCS, SOS, PM, APC */
+            vc->state = TTY_STATE_OSC;
         } else if (ch == '7') {
             vc_save_cursor(vc);
             vc->state = TTY_STATE_NORM;
@@ -1027,6 +1032,16 @@ static void vc_putchar(VCChardev *vc, int ch)
             break;
         }
         break;
+    case TTY_STATE_OSC: /* Operating System Command: ESC ] ... BEL/ST */
+        if (ch == '\a') {
+            /* BEL terminates OSC */
+            vc->state = TTY_STATE_NORM;
+        } else if (ch == 27) {
+            /* ESC might start ST (ESC \) */
+            vc->state = TTY_STATE_ESC;
+        }
+        /* All other bytes are silently consumed */
+        break;
     case TTY_STATE_G0: /* set character sets */
     case TTY_STATE_G1: /* set character sets */
         switch (ch) {

-- 
2.53.0



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

* [PATCH 13/60] ui/console-vc: fix comment shift-out/in comments
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (11 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 12/60] ui/console-vc: ignore string-type escape sequences Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:11   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 14/60] ui/console: dispatch get_label() through QOM virtual method Marc-André Lureau
                   ` (47 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

14 is shift-out
15 is shift-in

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 5eca9a5c004..fae8c8ce768 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -847,10 +847,10 @@ static void vc_putchar(VCChardev *vc, int ch)
             /* TODO: has to be implemented */
             break;
         case 14:
-            /* SI (shift in), character set 0 (ignored) */
+            /* SO (shift out), character set 1 (ignored) */
             break;
         case 15:
-            /* SO (shift out), character set 1 (ignored) */
+            /* SI (shift in), character set 0 (ignored) */
             break;
         case 27:    /* esc (introducing an escape sequence) */
             vc->state = TTY_STATE_ESC;

-- 
2.53.0



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

* [PATCH 14/60] ui/console: dispatch get_label() through QOM virtual method
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (12 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 13/60] ui/console-vc: fix comment shift-out/in comments Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:14   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 15/60] ui/console-vc: introduce QemuVT100 Marc-André Lureau
                   ` (46 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Replace the type-checking chain in qemu_console_get_label() (using
QEMU_IS_GRAPHIC_CONSOLE/QEMU_IS_TEXT_CONSOLE) with a QemuConsoleClass
virtual method, allowing each console subclass to provide its own
get_label implementation.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h  |   2 +
 ui/console-priv.h     |   1 -
 ui/console-vc-stubs.c |   6 ---
 ui/console-vc.c       |  12 ++++--
 ui/console.c          | 101 +++++++++++++++++++++++++++-----------------------
 5 files changed, 65 insertions(+), 57 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 3677a9d334d..4896e9e0e04 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -129,6 +129,8 @@ void console_handle_touch_event(QemuConsole *con,
 
 struct QemuConsoleClass {
     ObjectClass parent_class;
+
+    char * (*get_label)(QemuConsole *con);
 };
 
 typedef struct ScanoutTexture {
diff --git a/ui/console-priv.h b/ui/console-priv.h
index 43ceb8122f1..2c2cfd99570 100644
--- a/ui/console-priv.h
+++ b/ui/console-priv.h
@@ -36,7 +36,6 @@ struct QemuConsole {
 };
 
 void qemu_text_console_update_size(QemuTextConsole *c);
-const char * qemu_text_console_get_label(QemuTextConsole *c);
 void qemu_text_console_update_cursor(void);
 void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym);
 
diff --git a/ui/console-vc-stubs.c b/ui/console-vc-stubs.c
index b63e2fb2345..8a7f19c1f44 100644
--- a/ui/console-vc-stubs.c
+++ b/ui/console-vc-stubs.c
@@ -14,12 +14,6 @@ void qemu_text_console_update_size(QemuTextConsole *c)
 {
 }
 
-const char *
-qemu_text_console_get_label(QemuTextConsole *c)
-{
-    return NULL;
-}
-
 void qemu_text_console_update_cursor(void)
 {
 }
diff --git a/ui/console-vc.c b/ui/console-vc.c
index fae8c8ce768..e83a0b04e3f 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -125,10 +125,12 @@ static const pixman_color_t color_table_rgb[2][8] = {
 static bool cursor_visible_phase;
 static QEMUTimer *cursor_timer;
 
-const char *
-qemu_text_console_get_label(QemuTextConsole *c)
+static char *
+qemu_text_console_get_label(QemuConsole *c)
 {
-    return c->chr ? c->chr->label : NULL;
+    QemuTextConsole *tc = QEMU_TEXT_CONSOLE(c);
+
+    return tc->chr ? g_strdup(tc->chr->label) : NULL;
 }
 
 static void qemu_console_fill_rect(QemuConsole *con, int posx, int posy,
@@ -1115,9 +1117,13 @@ qemu_text_console_finalize(Object *obj)
 static void
 qemu_text_console_class_init(ObjectClass *oc, const void *data)
 {
+    QemuConsoleClass *cc = QEMU_CONSOLE_CLASS(oc);
+
     if (!cursor_timer) {
         cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);
     }
+
+    cc->get_label = qemu_text_console_get_label;
 }
 
 static const GraphicHwOps text_console_ops = {
diff --git a/ui/console.c b/ui/console.c
index f445db11389..24e4761e1f9 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -436,9 +436,58 @@ qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name,
     visit_type_uint32(v, name, &c->head, errp);
 }
 
+static bool
+qemu_graphic_console_is_multihead(QemuGraphicConsole *c)
+{
+    QemuConsole *con;
+
+    QTAILQ_FOREACH(con, &consoles, next) {
+        QemuGraphicConsole *candidate;
+
+        if (!QEMU_IS_GRAPHIC_CONSOLE(con)) {
+            continue;
+        }
+
+        candidate = QEMU_GRAPHIC_CONSOLE(con);
+        if (candidate->device != c->device) {
+            continue;
+        }
+
+        if (candidate->head != c->head) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static char *
+qemu_graphic_console_get_label(QemuConsole *con)
+{
+    QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con);
+
+    if (c->device) {
+        DeviceState *dev;
+        bool multihead;
+
+        dev = DEVICE(c->device);
+        multihead = qemu_graphic_console_is_multihead(c);
+        if (multihead) {
+            return g_strdup_printf("%s.%d", dev->id ?
+                                   dev->id :
+                                   object_get_typename(c->device),
+                                   c->head);
+        } else {
+            return g_strdup(dev->id ? : object_get_typename(c->device));
+        }
+    }
+    return g_strdup("VGA");
+}
+
 static void
 qemu_graphic_console_class_init(ObjectClass *oc, const void *data)
 {
+    QemuConsoleClass *cc = QEMU_CONSOLE_CLASS(oc);
+
     object_class_property_add_link(oc, "device", TYPE_DEVICE,
                                    offsetof(QemuGraphicConsole, device),
                                    object_property_allow_set_link,
@@ -446,6 +495,8 @@ qemu_graphic_console_class_init(ObjectClass *oc, const void *data)
     object_class_property_add(oc, "head", "uint32",
                               qemu_graphic_console_prop_get_head,
                               NULL, NULL, NULL);
+
+    cc->get_label = qemu_graphic_console_get_label;
 }
 
 static void
@@ -1347,56 +1398,12 @@ bool qemu_console_is_gl_blocked(QemuConsole *con)
     return con->gl_block;
 }
 
-static bool qemu_graphic_console_is_multihead(QemuGraphicConsole *c)
-{
-    QemuConsole *con;
-
-    QTAILQ_FOREACH(con, &consoles, next) {
-        QemuGraphicConsole *candidate;
-
-        if (!QEMU_IS_GRAPHIC_CONSOLE(con)) {
-            continue;
-        }
-
-        candidate = QEMU_GRAPHIC_CONSOLE(con);
-        if (candidate->device != c->device) {
-            continue;
-        }
-
-        if (candidate->head != c->head) {
-            return true;
-        }
-    }
-    return false;
-}
-
 char *qemu_console_get_label(QemuConsole *con)
 {
-    if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
-        QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con);
-        if (c->device) {
-            DeviceState *dev;
-            bool multihead;
-
-            dev = DEVICE(c->device);
-            multihead = qemu_graphic_console_is_multihead(c);
-            if (multihead) {
-                return g_strdup_printf("%s.%d", dev->id ?
-                                       dev->id :
-                                       object_get_typename(c->device),
-                                       c->head);
-            } else {
-                return g_strdup(dev->id ? : object_get_typename(c->device));
-            }
-        }
-        return g_strdup("VGA");
-    } else if (QEMU_IS_TEXT_CONSOLE(con)) {
-        const char *label = qemu_text_console_get_label(QEMU_TEXT_CONSOLE(con));
-        if (label) {
-            return g_strdup(label);
-        }
+    char *label = QEMU_CONSOLE_GET_CLASS(con)->get_label(con);
+    if (label) {
+        return label;
     }
-
     return g_strdup_printf("vc%d", con->index);
 }
 

-- 
2.53.0



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

* [PATCH 15/60] ui/console-vc: introduce QemuVT100
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (13 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 14/60] ui/console: dispatch get_label() through QOM virtual method Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 16/60] ui/console-vc: set vt100 associated pixman image Marc-André Lureau
                   ` (45 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Start moving VT100 emulation specific code in a different structure.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index e83a0b04e3f..41436a38eb5 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -48,9 +48,7 @@ enum TTYState {
     TTY_STATE_OSC,
 };
 
-typedef struct QemuTextConsole {
-    QemuConsole parent;
-
+typedef struct QemuVT100 {
     int width;
     int height;
     int total_height;
@@ -66,7 +64,12 @@ typedef struct QemuTextConsole {
     int update_y0;
     int update_x1;
     int update_y1;
+} QemuVT100;
+
+typedef struct QemuTextConsole {
+    QemuConsole parent;
 
+    QemuVT100 vt;
     Chardev *chr;
     /* fifo for key pressed */
     Fifo8 out_fifo;
@@ -186,34 +189,34 @@ static void invalidate_xy(QemuTextConsole *s, int x, int y)
     if (!qemu_console_is_visible(QEMU_CONSOLE(s))) {
         return;
     }
-    if (s->update_x0 > x * FONT_WIDTH)
-        s->update_x0 = x * FONT_WIDTH;
-    if (s->update_y0 > y * FONT_HEIGHT)
-        s->update_y0 = y * FONT_HEIGHT;
-    if (s->update_x1 < (x + 1) * FONT_WIDTH)
-        s->update_x1 = (x + 1) * FONT_WIDTH;
-    if (s->update_y1 < (y + 1) * FONT_HEIGHT)
-        s->update_y1 = (y + 1) * FONT_HEIGHT;
+    if (s->vt.update_x0 > x * FONT_WIDTH)
+        s->vt.update_x0 = x * FONT_WIDTH;
+    if (s->vt.update_y0 > y * FONT_HEIGHT)
+        s->vt.update_y0 = y * FONT_HEIGHT;
+    if (s->vt.update_x1 < (x + 1) * FONT_WIDTH)
+        s->vt.update_x1 = (x + 1) * FONT_WIDTH;
+    if (s->vt.update_y1 < (y + 1) * FONT_HEIGHT)
+        s->vt.update_y1 = (y + 1) * FONT_HEIGHT;
 }
 
 static void console_show_cursor(QemuTextConsole *s, int show)
 {
     TextCell *c;
     int y, y1;
-    int x = s->x;
+    int x = s->vt.x;
 
-    s->cursor_invalidate = 1;
+    s->vt.cursor_invalidate = 1;
 
-    if (x >= s->width) {
-        x = s->width - 1;
+    if (x >= s->vt.width) {
+        x = s->vt.width - 1;
     }
-    y1 = (s->y_base + s->y) % s->total_height;
-    y = y1 - s->y_displayed;
+    y1 = (s->vt.y_base + s->vt.y) % s->vt.total_height;
+    y = y1 - s->vt.y_displayed;
     if (y < 0) {
-        y += s->total_height;
+        y += s->vt.total_height;
     }
-    if (y < s->height) {
-        c = &s->cells[y1 * s->width + x];
+    if (y < s->vt.height) {
+        c = &s->vt.cells[y1 * s->vt.width + x];
         if (show && cursor_visible_phase) {
             TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
             t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
@@ -232,23 +235,23 @@ static void console_refresh(QemuTextConsole *s)
     int x, y, y1;
 
     assert(surface);
-    s->text_x[0] = 0;
-    s->text_y[0] = 0;
-    s->text_x[1] = s->width - 1;
-    s->text_y[1] = s->height - 1;
-    s->cursor_invalidate = 1;
+    s->vt.text_x[0] = 0;
+    s->vt.text_y[0] = 0;
+    s->vt.text_x[1] = s->vt.width - 1;
+    s->vt.text_y[1] = s->vt.height - 1;
+    s->vt.cursor_invalidate = 1;
 
     qemu_console_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface),
                            color_table_rgb[0][QEMU_COLOR_BLACK]);
-    y1 = s->y_displayed;
-    for (y = 0; y < s->height; y++) {
-        c = s->cells + y1 * s->width;
-        for (x = 0; x < s->width; x++) {
+    y1 = s->vt.y_displayed;
+    for (y = 0; y < s->vt.height; y++) {
+        c = s->vt.cells + y1 * s->vt.width;
+        for (x = 0; x < s->vt.width; x++) {
             vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch,
                           &(c->t_attrib));
             c++;
         }
-        if (++y1 == s->total_height) {
+        if (++y1 == s->vt.total_height) {
             y1 = 0;
         }
     }
@@ -263,24 +266,24 @@ static void console_scroll(QemuTextConsole *s, int ydelta)
 
     if (ydelta > 0) {
         for(i = 0; i < ydelta; i++) {
-            if (s->y_displayed == s->y_base)
+            if (s->vt.y_displayed == s->vt.y_base)
                 break;
-            if (++s->y_displayed == s->total_height)
-                s->y_displayed = 0;
+            if (++s->vt.y_displayed == s->vt.total_height)
+                s->vt.y_displayed = 0;
         }
     } else {
         ydelta = -ydelta;
-        i = s->backscroll_height;
-        if (i > s->total_height - s->height)
-            i = s->total_height - s->height;
-        y1 = s->y_base - i;
+        i = s->vt.backscroll_height;
+        if (i > s->vt.total_height - s->vt.height)
+            i = s->vt.total_height - s->vt.height;
+        y1 = s->vt.y_base - i;
         if (y1 < 0)
-            y1 += s->total_height;
+            y1 += s->vt.total_height;
         for(i = 0; i < ydelta; i++) {
-            if (s->y_displayed == y1)
+            if (s->vt.y_displayed == y1)
                 break;
-            if (--s->y_displayed < 0)
-                s->y_displayed = s->total_height - 1;
+            if (--s->vt.y_displayed < 0)
+                s->vt.y_displayed = s->vt.total_height - 1;
         }
     }
     console_refresh(s);
@@ -338,13 +341,13 @@ void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
             *q++ = '\033';
             *q++ = '[';
             *q++ = keysym & 0xff;
-        } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
+        } else if (s->vt.echo && (keysym == '\r' || keysym == '\n')) {
             qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
             *q++ = '\n';
         } else {
             *q++ = keysym;
         }
-        if (s->echo) {
+        if (s->vt.echo) {
             qemu_chr_write(s->chr, buf, q - buf, true);
         }
         num_free = fifo8_num_free(&s->out_fifo);
@@ -359,27 +362,27 @@ static void text_console_update(void *opaque, console_ch_t *chardata)
     QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
     int i, j, src;
 
-    if (s->text_x[0] <= s->text_x[1]) {
-        src = (s->y_base + s->text_y[0]) * s->width;
-        chardata += s->text_y[0] * s->width;
-        for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
-            for (j = 0; j < s->width; j++, src++) {
+    if (s->vt.text_x[0] <= s->vt.text_x[1]) {
+        src = (s->vt.y_base + s->vt.text_y[0]) * s->vt.width;
+        chardata += s->vt.text_y[0] * s->vt.width;
+        for (i = s->vt.text_y[0]; i <= s->vt.text_y[1]; i ++)
+            for (j = 0; j < s->vt.width; j++, src++) {
                 console_write_ch(chardata ++,
-                                 ATTR2CHTYPE(s->cells[src].ch,
-                                             s->cells[src].t_attrib.fgcol,
-                                             s->cells[src].t_attrib.bgcol,
-                                             s->cells[src].t_attrib.bold));
+                                 ATTR2CHTYPE(s->vt.cells[src].ch,
+                                             s->vt.cells[src].t_attrib.fgcol,
+                                             s->vt.cells[src].t_attrib.bgcol,
+                                             s->vt.cells[src].t_attrib.bold));
             }
-        dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0],
-                        s->text_x[1] - s->text_x[0], i - s->text_y[0]);
-        s->text_x[0] = s->width;
-        s->text_y[0] = s->height;
-        s->text_x[1] = 0;
-        s->text_y[1] = 0;
+        dpy_text_update(QEMU_CONSOLE(s), s->vt.text_x[0], s->vt.text_y[0],
+                        s->vt.text_x[1] - s->vt.text_x[0], i - s->vt.text_y[0]);
+        s->vt.text_x[0] = s->vt.width;
+        s->vt.text_y[0] = s->vt.height;
+        s->vt.text_x[1] = 0;
+        s->vt.text_y[1] = 0;
     }
-    if (s->cursor_invalidate) {
-        dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y);
-        s->cursor_invalidate = 0;
+    if (s->vt.cursor_invalidate) {
+        dpy_text_cursor(QEMU_CONSOLE(s), s->vt.x, s->vt.y);
+        s->vt.cursor_invalidate = 0;
     }
 }
 
@@ -393,33 +396,33 @@ static void text_console_resize(QemuTextConsole *t)
 
     w = surface_width(s->surface) / FONT_WIDTH;
     h = surface_height(s->surface) / FONT_HEIGHT;
-    if (w == t->width && h == t->height) {
+    if (w == t->vt.width && h == t->vt.height) {
         return;
     }
 
-    last_width = t->width;
-    t->width = w;
-    t->height = h;
+    last_width = t->vt.width;
+    t->vt.width = w;
+    t->vt.height = h;
 
-    w1 = MIN(t->width, last_width);
+    w1 = MIN(t->vt.width, last_width);
 
-    cells = g_new(TextCell, t->width * t->total_height + 1);
-    for (y = 0; y < t->total_height; y++) {
-        c = &cells[y * t->width];
+    cells = g_new(TextCell, t->vt.width * t->vt.total_height + 1);
+    for (y = 0; y < t->vt.total_height; y++) {
+        c = &cells[y * t->vt.width];
         if (w1 > 0) {
-            c1 = &t->cells[y * last_width];
+            c1 = &t->vt.cells[y * last_width];
             for (x = 0; x < w1; x++) {
                 *c++ = *c1++;
             }
         }
-        for (x = w1; x < t->width; x++) {
+        for (x = w1; x < t->vt.width; x++) {
             c->ch = ' ';
             c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
             c++;
         }
     }
-    g_free(t->cells);
-    t->cells = cells;
+    g_free(t->vt.cells);
+    t->vt.cells = cells;
 }
 
 static void vc_put_lf(VCChardev *vc)
@@ -428,41 +431,41 @@ static void vc_put_lf(VCChardev *vc)
     TextCell *c;
     int x, y1;
 
-    s->y++;
-    if (s->y >= s->height) {
-        s->y = s->height - 1;
+    s->vt.y++;
+    if (s->vt.y >= s->vt.height) {
+        s->vt.y = s->vt.height - 1;
 
-        if (s->y_displayed == s->y_base) {
-            if (++s->y_displayed == s->total_height)
-                s->y_displayed = 0;
+        if (s->vt.y_displayed == s->vt.y_base) {
+            if (++s->vt.y_displayed == s->vt.total_height)
+                s->vt.y_displayed = 0;
         }
-        if (++s->y_base == s->total_height)
-            s->y_base = 0;
-        if (s->backscroll_height < s->total_height)
-            s->backscroll_height++;
-        y1 = (s->y_base + s->height - 1) % s->total_height;
-        c = &s->cells[y1 * s->width];
-        for(x = 0; x < s->width; x++) {
+        if (++s->vt.y_base == s->vt.total_height)
+            s->vt.y_base = 0;
+        if (s->vt.backscroll_height < s->vt.total_height)
+            s->vt.backscroll_height++;
+        y1 = (s->vt.y_base + s->vt.height - 1) % s->vt.total_height;
+        c = &s->vt.cells[y1 * s->vt.width];
+        for(x = 0; x < s->vt.width; x++) {
             c->ch = ' ';
             c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
             c++;
         }
-        if (s->y_displayed == s->y_base) {
-            s->text_x[0] = 0;
-            s->text_y[0] = 0;
-            s->text_x[1] = s->width - 1;
-            s->text_y[1] = s->height - 1;
+        if (s->vt.y_displayed == s->vt.y_base) {
+            s->vt.text_x[0] = 0;
+            s->vt.text_y[0] = 0;
+            s->vt.text_x[1] = s->vt.width - 1;
+            s->vt.text_y[1] = s->vt.height - 1;
 
             qemu_console_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0,
-                                s->width * FONT_WIDTH,
-                                (s->height - 1) * FONT_HEIGHT);
-            qemu_console_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT,
-                                   s->width * FONT_WIDTH, FONT_HEIGHT,
+                                s->vt.width * FONT_WIDTH,
+                                (s->vt.height - 1) * FONT_HEIGHT);
+            qemu_console_fill_rect(QEMU_CONSOLE(s), 0, (s->vt.height - 1) * FONT_HEIGHT,
+                                   s->vt.width * FONT_WIDTH, FONT_HEIGHT,
                                    color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
-            s->update_x0 = 0;
-            s->update_y0 = 0;
-            s->update_x1 = s->width * FONT_WIDTH;
-            s->update_y1 = s->height * FONT_HEIGHT;
+            s->vt.update_x0 = 0;
+            s->vt.update_y0 = 0;
+            s->vt.update_x1 = s->vt.width * FONT_WIDTH;
+            s->vt.update_y1 = s->vt.height * FONT_HEIGHT;
         }
     }
 }
@@ -570,21 +573,21 @@ static void vc_update_xy(VCChardev *vc, int x, int y)
     TextCell *c;
     int y1, y2;
 
-    s->text_x[0] = MIN(s->text_x[0], x);
-    s->text_x[1] = MAX(s->text_x[1], x);
-    s->text_y[0] = MIN(s->text_y[0], y);
-    s->text_y[1] = MAX(s->text_y[1], y);
+    s->vt.text_x[0] = MIN(s->vt.text_x[0], x);
+    s->vt.text_x[1] = MAX(s->vt.text_x[1], x);
+    s->vt.text_y[0] = MIN(s->vt.text_y[0], y);
+    s->vt.text_y[1] = MAX(s->vt.text_y[1], y);
 
-    y1 = (s->y_base + y) % s->total_height;
-    y2 = y1 - s->y_displayed;
+    y1 = (s->vt.y_base + y) % s->vt.total_height;
+    y2 = y1 - s->vt.y_displayed;
     if (y2 < 0) {
-        y2 += s->total_height;
+        y2 += s->vt.total_height;
     }
-    if (y2 < s->height) {
-        if (x >= s->width) {
-            x = s->width - 1;
+    if (y2 < s->vt.height) {
+        if (x >= s->vt.width) {
+            x = s->vt.width - 1;
         }
-        c = &s->cells[y1 * s->width + x];
+        c = &s->vt.cells[y1 * s->vt.width + x];
         vga_putcharxy(QEMU_CONSOLE(s), x, y2, c->ch,
                       &(c->t_attrib));
         invalidate_xy(s, x, y2);
@@ -594,11 +597,11 @@ static void vc_update_xy(VCChardev *vc, int x, int y)
 static void vc_clear_xy(VCChardev *vc, int x, int y)
 {
     QemuTextConsole *s = vc->console;
-    int y1 = (s->y_base + y) % s->total_height;
-    if (x >= s->width) {
-        x = s->width - 1;
+    int y1 = (s->vt.y_base + y) % s->vt.total_height;
+    if (x >= s->vt.width) {
+        x = s->vt.width - 1;
     }
-    TextCell *c = &s->cells[y1 * s->width + x];
+    TextCell *c = &s->vt.cells[y1 * s->vt.width + x];
     c->ch = ' ';
     c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
     vc_update_xy(vc, x, y);
@@ -650,17 +653,17 @@ static void vc_put_one(VCChardev *vc, int ch)
     QemuTextConsole *s = vc->console;
     TextCell *c;
     int y1;
-    if (s->x >= s->width) {
+    if (s->vt.x >= s->vt.width) {
         /* line wrap */
-        s->x = 0;
+        s->vt.x = 0;
         vc_put_lf(vc);
     }
-    y1 = (s->y_base + s->y) % s->total_height;
-    c = &s->cells[y1 * s->width + s->x];
+    y1 = (s->vt.y_base + s->vt.y) % s->vt.total_height;
+    c = &s->vt.cells[y1 * s->vt.width + s->vt.x];
     c->ch = ch;
     c->t_attrib = vc->t_attrib;
-    vc_update_xy(vc, s->x, s->y);
-    s->x++;
+    vc_update_xy(vc, s->vt.x, s->vt.y);
+    s->vt.x++;
 }
 
 static void vc_respond_str(VCChardev *vc, const char *buf)
@@ -681,15 +684,15 @@ static void vc_set_cursor(VCChardev *vc, int x, int y)
     if (y < 0) {
         y = 0;
     }
-    if (y >= s->height) {
-        y = s->height - 1;
+    if (y >= s->vt.height) {
+        y = s->vt.height - 1;
     }
-    if (x >= s->width) {
-        x = s->width - 1;
+    if (x >= s->vt.width) {
+        x = s->vt.width - 1;
     }
 
-    s->x = x;
-    s->y = y;
+    s->vt.x = x;
+    s->vt.y = y;
 }
 
 /**
@@ -708,28 +711,28 @@ static void vc_csi_P(struct VCChardev *vc, unsigned int nr)
     if (!nr) {
         nr = 1;
     }
-    if (nr > s->width - s->x) {
-        nr = s->width - s->x;
+    if (nr > s->vt.width - s->vt.x) {
+        nr = s->vt.width - s->vt.x;
         if (!nr) {
             return;
         }
     }
 
-    x1 = s->x;
-    x2 = s->x + nr;
-    len = s->width - x2;
+    x1 = s->vt.x;
+    x2 = s->vt.x + nr;
+    len = s->vt.width - x2;
     if (len) {
-        y = (s->y_base + s->y) % s->total_height;
-        c1 = &s->cells[y * s->width + x1];
-        c2 = &s->cells[y * s->width + x2];
+        y = (s->vt.y_base + s->vt.y) % s->vt.total_height;
+        c1 = &s->vt.cells[y * s->vt.width + x1];
+        c2 = &s->vt.cells[y * s->vt.width + x2];
         memmove(c1, c2, len * sizeof(*c1));
         for (end = x1 + len; x1 < end; x1++) {
-            vc_update_xy(vc, x1, s->y);
+            vc_update_xy(vc, x1, s->vt.y);
         }
     }
     /* Clear the rest */
-    for (; x1 < s->width; x1++) {
-        vc_clear_xy(vc, x1, s->y);
+    for (; x1 < s->vt.width; x1++) {
+        vc_clear_xy(vc, x1, s->vt.y);
     }
 }
 
@@ -749,28 +752,28 @@ static void vc_csi_at(struct VCChardev *vc, unsigned int nr)
     if (!nr) {
         nr = 1;
     }
-    if (nr > s->width - s->x) {
-        nr = s->width - s->x;
+    if (nr > s->vt.width - s->vt.x) {
+        nr = s->vt.width - s->vt.x;
         if (!nr) {
             return;
         }
     }
 
-    x1 = s->x + nr;
-    x2 = s->x;
-    len = s->width - x1;
+    x1 = s->vt.x + nr;
+    x2 = s->vt.x;
+    len = s->vt.width - x1;
     if (len) {
-        y = (s->y_base + s->y) % s->total_height;
-        c1 = &s->cells[y * s->width + x1];
-        c2 = &s->cells[y * s->width + x2];
+        y = (s->vt.y_base + s->vt.y) % s->vt.total_height;
+        c1 = &s->vt.cells[y * s->vt.width + x1];
+        c2 = &s->vt.cells[y * s->vt.width + x2];
         memmove(c1, c2, len * sizeof(*c1));
         for (end = x1 + len; x1 < end; x1++) {
-            vc_update_xy(vc, x1, s->y);
+            vc_update_xy(vc, x1, s->vt.y);
         }
     }
     /* Insert blanks */
-    for (x1 = s->x; x1 < s->x + nr; x1++) {
-        vc_clear_xy(vc, x1, s->y);
+    for (x1 = s->vt.x; x1 < s->vt.x + nr; x1++) {
+        vc_clear_xy(vc, x1, s->vt.y);
     }
 }
 
@@ -781,8 +784,8 @@ static void vc_save_cursor(VCChardev *vc)
 {
     QemuTextConsole *s = vc->console;
 
-    vc->x_saved = s->x;
-    vc->y_saved = s->y;
+    vc->x_saved = s->vt.x;
+    vc->y_saved = s->vt.y;
     vc->t_attrib_saved = vc->t_attrib;
 }
 
@@ -794,8 +797,8 @@ static void vc_restore_cursor(VCChardev *vc)
 {
     QemuTextConsole *s = vc->console;
 
-    s->x = vc->x_saved;
-    s->y = vc->y_saved;
+    s->vt.x = vc->x_saved;
+    s->vt.y = vc->y_saved;
     vc->t_attrib = vc->t_attrib_saved;
 }
 
@@ -828,21 +831,21 @@ static void vc_putchar(VCChardev *vc, int ch)
         vc->utf8_state = UTF8_ACCEPT;
         switch(ch) {
         case '\r':  /* carriage return */
-            s->x = 0;
+            s->vt.x = 0;
             break;
         case '\n':  /* newline */
             vc_put_lf(vc);
             break;
         case '\b':  /* backspace */
-            if (s->x > 0)
-                s->x--;
+            if (s->vt.x > 0)
+                s->vt.x--;
             break;
         case '\t':  /* tabspace */
-            if (s->x + (8 - (s->x % 8)) > s->width) {
-                s->x = 0;
+            if (s->vt.x + (8 - (s->vt.x % 8)) > s->vt.width) {
+                s->vt.x = 0;
                 vc_put_lf(vc);
             } else {
-                s->x = s->x + (8 - (s->x % 8));
+                s->vt.x = s->vt.x + (8 - (s->vt.x % 8));
             }
             break;
         case '\a':  /* alert aka. bell */
@@ -910,32 +913,32 @@ static void vc_putchar(VCChardev *vc, int ch)
                 if (vc->esc_params[0] == 0) {
                     vc->esc_params[0] = 1;
                 }
-                vc_set_cursor(vc, s->x, s->y - vc->esc_params[0]);
+                vc_set_cursor(vc, s->vt.x, s->vt.y - vc->esc_params[0]);
                 break;
             case 'B':
                 /* move cursor down */
                 if (vc->esc_params[0] == 0) {
                     vc->esc_params[0] = 1;
                 }
-                vc_set_cursor(vc, s->x, s->y + vc->esc_params[0]);
+                vc_set_cursor(vc, s->vt.x, s->vt.y + vc->esc_params[0]);
                 break;
             case 'C':
                 /* move cursor right */
                 if (vc->esc_params[0] == 0) {
                     vc->esc_params[0] = 1;
                 }
-                vc_set_cursor(vc, s->x + vc->esc_params[0], s->y);
+                vc_set_cursor(vc, s->vt.x + vc->esc_params[0], s->vt.y);
                 break;
             case 'D':
                 /* move cursor left */
                 if (vc->esc_params[0] == 0) {
                     vc->esc_params[0] = 1;
                 }
-                vc_set_cursor(vc, s->x - vc->esc_params[0], s->y);
+                vc_set_cursor(vc, s->vt.x - vc->esc_params[0], s->vt.y);
                 break;
             case 'G':
                 /* move cursor to column */
-                vc_set_cursor(vc, vc->esc_params[0] - 1, s->y);
+                vc_set_cursor(vc, vc->esc_params[0] - 1, s->vt.y);
                 break;
             case 'f':
             case 'H':
@@ -946,9 +949,9 @@ static void vc_putchar(VCChardev *vc, int ch)
                 switch (vc->esc_params[0]) {
                 case 0:
                     /* clear to end of screen */
-                    for (y = s->y; y < s->height; y++) {
-                        for (x = 0; x < s->width; x++) {
-                            if (y == s->y && x < s->x) {
+                    for (y = s->vt.y; y < s->vt.height; y++) {
+                        for (x = 0; x < s->vt.width; x++) {
+                            if (y == s->vt.y && x < s->vt.x) {
                                 continue;
                             }
                             vc_clear_xy(vc, x, y);
@@ -957,9 +960,9 @@ static void vc_putchar(VCChardev *vc, int ch)
                     break;
                 case 1:
                     /* clear from beginning of screen */
-                    for (y = 0; y <= s->y; y++) {
-                        for (x = 0; x < s->width; x++) {
-                            if (y == s->y && x > s->x) {
+                    for (y = 0; y <= s->vt.y; y++) {
+                        for (x = 0; x < s->vt.width; x++) {
+                            if (y == s->vt.y && x > s->vt.x) {
                                 break;
                             }
                             vc_clear_xy(vc, x, y);
@@ -968,8 +971,8 @@ static void vc_putchar(VCChardev *vc, int ch)
                     break;
                 case 2:
                     /* clear entire screen */
-                    for (y = 0; y < s->height; y++) {
-                        for (x = 0; x < s->width; x++) {
+                    for (y = 0; y < s->vt.height; y++) {
+                        for (x = 0; x < s->vt.width; x++) {
                             vc_clear_xy(vc, x, y);
                         }
                     }
@@ -980,20 +983,20 @@ static void vc_putchar(VCChardev *vc, int ch)
                 switch (vc->esc_params[0]) {
                 case 0:
                     /* clear to eol */
-                    for(x = s->x; x < s->width; x++) {
-                        vc_clear_xy(vc, x, s->y);
+                    for(x = s->vt.x; x < s->vt.width; x++) {
+                        vc_clear_xy(vc, x, s->vt.y);
                     }
                     break;
                 case 1:
                     /* clear from beginning of line */
-                    for (x = 0; x <= s->x && x < s->width; x++) {
-                        vc_clear_xy(vc, x, s->y);
+                    for (x = 0; x <= s->vt.x && x < s->vt.width; x++) {
+                        vc_clear_xy(vc, x, s->vt.y);
                     }
                     break;
                 case 2:
                     /* clear entire line */
-                    for(x = 0; x < s->width; x++) {
-                        vc_clear_xy(vc, x, s->y);
+                    for(x = 0; x < s->vt.width; x++) {
+                        vc_clear_xy(vc, x, s->vt.y);
                     }
                     break;
                 }
@@ -1013,7 +1016,7 @@ static void vc_putchar(VCChardev *vc, int ch)
                 case 6:
                     /* report cursor position */
                     response = g_strdup_printf("\033[%d;%dR",
-                                               s->y + 1, s->x + 1);
+                                               s->vt.y + 1, s->vt.x + 1);
                     vc_respond_str(vc, response);
                     break;
                 }
@@ -1066,19 +1069,19 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
     QemuTextConsole *s = drv->console;
     int i;
 
-    s->update_x0 = s->width * FONT_WIDTH;
-    s->update_y0 = s->height * FONT_HEIGHT;
-    s->update_x1 = 0;
-    s->update_y1 = 0;
+    s->vt.update_x0 = s->vt.width * FONT_WIDTH;
+    s->vt.update_y0 = s->vt.height * FONT_HEIGHT;
+    s->vt.update_x1 = 0;
+    s->vt.update_y1 = 0;
     console_show_cursor(s, 0);
     for(i = 0; i < len; i++) {
         vc_putchar(drv, buf[i]);
     }
     console_show_cursor(s, 1);
-    if (s->update_x0 < s->update_x1) {
-        dpy_gfx_update(QEMU_CONSOLE(s), s->update_x0, s->update_y0,
-                       s->update_x1 - s->update_x0,
-                       s->update_y1 - s->update_y0);
+    if (s->vt.update_x0 < s->vt.update_x1) {
+        dpy_gfx_update(QEMU_CONSOLE(s), s->vt.update_x0, s->vt.update_y0,
+                       s->vt.update_x1 - s->vt.update_x0,
+                       s->vt.update_y1 - s->vt.update_y0);
     }
     return len;
 }
@@ -1137,7 +1140,7 @@ qemu_text_console_init(Object *obj)
     QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
 
     fifo8_create(&c->out_fifo, 16);
-    c->total_height = DEFAULT_BACKSCROLL;
+    c->vt.total_height = DEFAULT_BACKSCROLL;
     QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
     QEMU_CONSOLE(c)->hw = c;
 }
@@ -1168,12 +1171,12 @@ static void vc_chr_set_echo(Chardev *chr, bool echo)
 {
     VCChardev *drv = VC_CHARDEV(chr);
 
-    drv->console->echo = echo;
+    drv->console->vt.echo = echo;
 }
 
 void qemu_text_console_update_size(QemuTextConsole *c)
 {
-    dpy_text_resize(QEMU_CONSOLE(c), c->width, c->height);
+    dpy_text_resize(QEMU_CONSOLE(c), c->vt.width, c->vt.height);
 }
 
 static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)

-- 
2.53.0



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

* [PATCH 16/60] ui/console-vc: set vt100 associated pixman image
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (14 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 15/60] ui/console-vc: introduce QemuVT100 Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 17/60] ui/console-vc: vga_putcharxy()->vt100_putcharxy() Marc-André Lureau
                   ` (44 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Start removing dependency on DisplaySurface for vt100 handling.

Note that before, the rendering is done on the current DisplaySurface.
It's not obvious the QemuTextConsole associated surface isn't changed
over time, in particular if it was doing resize. But
qemu_console_resize() is only implemented for QemuGraphicConsole.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 41436a38eb5..0e0a9ad245d 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -49,6 +49,8 @@ enum TTYState {
 };
 
 typedef struct QemuVT100 {
+    pixman_image_t *image;
+
     int width;
     int height;
     int total_height;
@@ -136,28 +138,22 @@ qemu_text_console_get_label(QemuConsole *c)
     return tc->chr ? g_strdup(tc->chr->label) : NULL;
 }
 
-static void qemu_console_fill_rect(QemuConsole *con, int posx, int posy,
-                                   int width, int height, pixman_color_t color)
+static void image_fill_rect(pixman_image_t *image, int posx, int posy,
+                            int width, int height, pixman_color_t color)
 {
-    DisplaySurface *surface = qemu_console_surface(con);
     pixman_rectangle16_t rect = {
         .x = posx, .y = posy, .width = width, .height = height
     };
 
-    assert(surface);
-    pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
-                                 &color, 1, &rect);
+    pixman_image_fill_rectangles(PIXMAN_OP_SRC, image, &color, 1, &rect);
 }
 
 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
-static void qemu_console_bitblt(QemuConsole *con,
-                                int xs, int ys, int xd, int yd, int w, int h)
+static void image_bitblt(pixman_image_t *image,
+                         int xs, int ys, int xd, int yd, int w, int h)
 {
-    DisplaySurface *surface = qemu_console_surface(con);
-
-    assert(surface);
     pixman_image_composite(PIXMAN_OP_SRC,
-                           surface->image, NULL, surface->image,
+                           image, NULL, image,
                            xs, ys, 0, 0, xd, yd, w, h);
 }
 
@@ -165,10 +161,10 @@ static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
                           TextAttributes *t_attrib)
 {
     static pixman_image_t *glyphs[256];
-    DisplaySurface *surface = qemu_console_surface(s);
+    pixman_image_t *image = QEMU_TEXT_CONSOLE(s)->vt.image;
     pixman_color_t fgcol, bgcol;
 
-    assert(surface);
+    assert(image);
     if (t_attrib->invers) {
         bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
         fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
@@ -180,7 +176,7 @@ static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
     if (!glyphs[ch]) {
         glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
     }
-    qemu_pixman_glyph_render(glyphs[ch], surface->image,
+    qemu_pixman_glyph_render(glyphs[ch], image,
                              &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
 }
 
@@ -230,19 +226,19 @@ static void console_show_cursor(QemuTextConsole *s, int show)
 
 static void console_refresh(QemuTextConsole *s)
 {
-    DisplaySurface *surface = qemu_console_surface(QEMU_CONSOLE(s));
     TextCell *c;
     int x, y, y1;
+    int w = pixman_image_get_width(s->vt.image);
+    int h = pixman_image_get_height(s->vt.image);
 
-    assert(surface);
     s->vt.text_x[0] = 0;
     s->vt.text_y[0] = 0;
     s->vt.text_x[1] = s->vt.width - 1;
     s->vt.text_y[1] = s->vt.height - 1;
     s->vt.cursor_invalidate = 1;
 
-    qemu_console_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface),
-                           color_table_rgb[0][QEMU_COLOR_BLACK]);
+    image_fill_rect(s->vt.image, 0, 0, w, h,
+                    color_table_rgb[0][QEMU_COLOR_BLACK]);
     y1 = s->vt.y_displayed;
     for (y = 0; y < s->vt.height; y++) {
         c = s->vt.cells + y1 * s->vt.width;
@@ -256,8 +252,7 @@ static void console_refresh(QemuTextConsole *s)
         }
     }
     console_show_cursor(s, 1);
-    dpy_gfx_update(QEMU_CONSOLE(s), 0, 0,
-                   surface_width(surface), surface_height(surface));
+    dpy_gfx_update(QEMU_CONSOLE(s), 0, 0, w, h);
 }
 
 static void console_scroll(QemuTextConsole *s, int ydelta)
@@ -394,8 +389,9 @@ static void text_console_resize(QemuTextConsole *t)
 
     assert(s->scanout.kind == SCANOUT_SURFACE);
 
-    w = surface_width(s->surface) / FONT_WIDTH;
-    h = surface_height(s->surface) / FONT_HEIGHT;
+    t->vt.image = s->surface->image;
+    w = pixman_image_get_width(t->vt.image) / FONT_WIDTH;
+    h = pixman_image_get_height(t->vt.image) / FONT_HEIGHT;
     if (w == t->vt.width && h == t->vt.height) {
         return;
     }
@@ -456,12 +452,12 @@ static void vc_put_lf(VCChardev *vc)
             s->vt.text_x[1] = s->vt.width - 1;
             s->vt.text_y[1] = s->vt.height - 1;
 
-            qemu_console_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0,
-                                s->vt.width * FONT_WIDTH,
-                                (s->vt.height - 1) * FONT_HEIGHT);
-            qemu_console_fill_rect(QEMU_CONSOLE(s), 0, (s->vt.height - 1) * FONT_HEIGHT,
-                                   s->vt.width * FONT_WIDTH, FONT_HEIGHT,
-                                   color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
+            image_bitblt(s->vt.image, 0, FONT_HEIGHT, 0, 0,
+                         s->vt.width * FONT_WIDTH,
+                         (s->vt.height - 1) * FONT_HEIGHT);
+            image_fill_rect(s->vt.image, 0, (s->vt.height - 1) * FONT_HEIGHT,
+                            s->vt.width * FONT_WIDTH, FONT_HEIGHT,
+                            color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
             s->vt.update_x0 = 0;
             s->vt.update_y0 = 0;
             s->vt.update_x1 = s->vt.width * FONT_WIDTH;

-- 
2.53.0



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

* [PATCH 17/60] ui/console-vc: vga_putcharxy()->vt100_putcharxy()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (15 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 16/60] ui/console-vc: set vt100 associated pixman image Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 18/60] ui/console-vc: make invalidate_xy() take vt100 Marc-André Lureau
                   ` (43 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Have the character rendering function operate on QemuVT100 directly
instead of taking a QemuConsole and extracting the VT100 state
internally. This decouples glyph rendering from the console object,
continuing the QemuVT100 abstraction introduced in the previous commits.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 0e0a9ad245d..c730a8a52b8 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -157,14 +157,13 @@ static void image_bitblt(pixman_image_t *image,
                            xs, ys, 0, 0, xd, yd, w, h);
 }
 
-static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
-                          TextAttributes *t_attrib)
+static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch,
+                            TextAttributes *t_attrib)
 {
     static pixman_image_t *glyphs[256];
-    pixman_image_t *image = QEMU_TEXT_CONSOLE(s)->vt.image;
     pixman_color_t fgcol, bgcol;
 
-    assert(image);
+    assert(vt->image);
     if (t_attrib->invers) {
         bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
         fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
@@ -176,7 +175,7 @@ static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
     if (!glyphs[ch]) {
         glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
     }
-    qemu_pixman_glyph_render(glyphs[ch], image,
+    qemu_pixman_glyph_render(glyphs[ch], vt->image,
                              &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
 }
 
@@ -216,9 +215,9 @@ static void console_show_cursor(QemuTextConsole *s, int show)
         if (show && cursor_visible_phase) {
             TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
             t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
-            vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &t_attrib);
+            vt100_putcharxy(&s->vt, x, y, c->ch, &t_attrib);
         } else {
-            vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &(c->t_attrib));
+            vt100_putcharxy(&s->vt, x, y, c->ch, &(c->t_attrib));
         }
         invalidate_xy(s, x, y);
     }
@@ -243,7 +242,7 @@ static void console_refresh(QemuTextConsole *s)
     for (y = 0; y < s->vt.height; y++) {
         c = s->vt.cells + y1 * s->vt.width;
         for (x = 0; x < s->vt.width; x++) {
-            vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch,
+            vt100_putcharxy(&s->vt, x, y, c->ch,
                           &(c->t_attrib));
             c++;
         }
@@ -584,7 +583,7 @@ static void vc_update_xy(VCChardev *vc, int x, int y)
             x = s->vt.width - 1;
         }
         c = &s->vt.cells[y1 * s->vt.width + x];
-        vga_putcharxy(QEMU_CONSOLE(s), x, y2, c->ch,
+        vt100_putcharxy(&s->vt, x, y2, c->ch,
                       &(c->t_attrib));
         invalidate_xy(s, x, y2);
     }

-- 
2.53.0



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

* [PATCH 18/60] ui/console-vc: make invalidate_xy() take vt100
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (16 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 17/60] ui/console-vc: vga_putcharxy()->vt100_putcharxy() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 19/60] ui/console-vc: make show_cursor() " Marc-André Lureau
                   ` (42 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

This decouples glyph rendering from the console object, continuing the
QemuVT100 abstraction introduced in the previous commits.

Style fixes.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index c730a8a52b8..4117429a508 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -179,19 +179,20 @@ static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch,
                              &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
 }
 
-static void invalidate_xy(QemuTextConsole *s, int x, int y)
+static void vt100_invalidate_xy(QemuVT100 *vt, int x, int y)
 {
-    if (!qemu_console_is_visible(QEMU_CONSOLE(s))) {
-        return;
+    if (vt->update_x0 > x * FONT_WIDTH) {
+        vt->update_x0 = x * FONT_WIDTH;
+    }
+    if (vt->update_y0 > y * FONT_HEIGHT) {
+        vt->update_y0 = y * FONT_HEIGHT;
+    }
+    if (vt->update_x1 < (x + 1) * FONT_WIDTH) {
+        vt->update_x1 = (x + 1) * FONT_WIDTH;
+    }
+    if (vt->update_y1 < (y + 1) * FONT_HEIGHT) {
+        vt->update_y1 = (y + 1) * FONT_HEIGHT;
     }
-    if (s->vt.update_x0 > x * FONT_WIDTH)
-        s->vt.update_x0 = x * FONT_WIDTH;
-    if (s->vt.update_y0 > y * FONT_HEIGHT)
-        s->vt.update_y0 = y * FONT_HEIGHT;
-    if (s->vt.update_x1 < (x + 1) * FONT_WIDTH)
-        s->vt.update_x1 = (x + 1) * FONT_WIDTH;
-    if (s->vt.update_y1 < (y + 1) * FONT_HEIGHT)
-        s->vt.update_y1 = (y + 1) * FONT_HEIGHT;
 }
 
 static void console_show_cursor(QemuTextConsole *s, int show)
@@ -219,7 +220,7 @@ static void console_show_cursor(QemuTextConsole *s, int show)
         } else {
             vt100_putcharxy(&s->vt, x, y, c->ch, &(c->t_attrib));
         }
-        invalidate_xy(s, x, y);
+        vt100_invalidate_xy(&s->vt, x, y);
     }
 }
 
@@ -585,7 +586,7 @@ static void vc_update_xy(VCChardev *vc, int x, int y)
         c = &s->vt.cells[y1 * s->vt.width + x];
         vt100_putcharxy(&s->vt, x, y2, c->ch,
                       &(c->t_attrib));
-        invalidate_xy(s, x, y2);
+        vt100_invalidate_xy(&s->vt, x, y2);
     }
 }
 

-- 
2.53.0



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

* [PATCH 19/60] ui/console-vc: make show_cursor() take vt100
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (17 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 18/60] ui/console-vc: make invalidate_xy() take vt100 Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 20/60] ui/console-vc: decouple VT100 display updates via function pointer Marc-André Lureau
                   ` (41 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Decouples glyph rendering from the console object,
continuing the QemuVT100 abstraction introduced in the previous commits.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 4117429a508..56e2527c7a2 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -195,32 +195,32 @@ static void vt100_invalidate_xy(QemuVT100 *vt, int x, int y)
     }
 }
 
-static void console_show_cursor(QemuTextConsole *s, int show)
+static void vt100_show_cursor(QemuVT100 *vt, int show)
 {
     TextCell *c;
     int y, y1;
-    int x = s->vt.x;
+    int x = vt->x;
 
-    s->vt.cursor_invalidate = 1;
+    vt->cursor_invalidate = 1;
 
-    if (x >= s->vt.width) {
-        x = s->vt.width - 1;
+    if (x >= vt->width) {
+        x = vt->width - 1;
     }
-    y1 = (s->vt.y_base + s->vt.y) % s->vt.total_height;
-    y = y1 - s->vt.y_displayed;
+    y1 = (vt->y_base + vt->y) % vt->total_height;
+    y = y1 - vt->y_displayed;
     if (y < 0) {
-        y += s->vt.total_height;
+        y += vt->total_height;
     }
-    if (y < s->vt.height) {
-        c = &s->vt.cells[y1 * s->vt.width + x];
+    if (y < vt->height) {
+        c = &vt->cells[y1 * vt->width + x];
         if (show && cursor_visible_phase) {
             TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
             t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
-            vt100_putcharxy(&s->vt, x, y, c->ch, &t_attrib);
+            vt100_putcharxy(vt, x, y, c->ch, &t_attrib);
         } else {
-            vt100_putcharxy(&s->vt, x, y, c->ch, &(c->t_attrib));
+            vt100_putcharxy(vt, x, y, c->ch, &(c->t_attrib));
         }
-        vt100_invalidate_xy(&s->vt, x, y);
+        vt100_invalidate_xy(vt, x, y);
     }
 }
 
@@ -251,7 +251,7 @@ static void console_refresh(QemuTextConsole *s)
             y1 = 0;
         }
     }
-    console_show_cursor(s, 1);
+    vt100_show_cursor(&s->vt, 1);
     dpy_gfx_update(QEMU_CONSOLE(s), 0, 0, w, h);
 }
 
@@ -1069,11 +1069,11 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
     s->vt.update_y0 = s->vt.height * FONT_HEIGHT;
     s->vt.update_x1 = 0;
     s->vt.update_y1 = 0;
-    console_show_cursor(s, 0);
+    vt100_show_cursor(&s->vt, 0);
     for(i = 0; i < len; i++) {
         vc_putchar(drv, buf[i]);
     }
-    console_show_cursor(s, 1);
+    vt100_show_cursor(&s->vt, 1);
     if (s->vt.update_x0 < s->vt.update_x1) {
         dpy_gfx_update(QEMU_CONSOLE(s), s->vt.update_x0, s->vt.update_y0,
                        s->vt.update_x1 - s->vt.update_x0,

-- 
2.53.0



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

* [PATCH 20/60] ui/console-vc: decouple VT100 display updates via function pointer
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (18 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 19/60] ui/console-vc: make show_cursor() " Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 21/60] ui/console-vc: console_refresh() -> vt100_refresh() Marc-André Lureau
                   ` (40 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Replace direct dpy_gfx_update() calls from the VT100 emulation code
with an indirect call through a new image_update function pointer in
QemuVT100. This decouples the VT100 terminal emulation from the
QEMU display layer, allowing different backends to provide their own
image update implementation.

The QemuVT100 typedef is changed to a forward-declared struct so the
function pointer signature can reference QemuVT100 itself.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 56e2527c7a2..8a18659036f 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -48,8 +48,11 @@ enum TTYState {
     TTY_STATE_OSC,
 };
 
-typedef struct QemuVT100 {
+typedef struct QemuVT100 QemuVT100;
+
+struct QemuVT100 {
     pixman_image_t *image;
+    void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height);
 
     int width;
     int height;
@@ -66,7 +69,7 @@ typedef struct QemuVT100 {
     int update_y0;
     int update_x1;
     int update_y1;
-} QemuVT100;
+};
 
 typedef struct QemuTextConsole {
     QemuConsole parent;
@@ -224,6 +227,11 @@ static void vt100_show_cursor(QemuVT100 *vt, int show)
     }
 }
 
+static void vt100_image_update(QemuVT100 *vt, int x, int y, int width, int height)
+{
+    vt->image_update(vt, x, y, width, height);
+}
+
 static void console_refresh(QemuTextConsole *s)
 {
     TextCell *c;
@@ -252,7 +260,7 @@ static void console_refresh(QemuTextConsole *s)
         }
     }
     vt100_show_cursor(&s->vt, 1);
-    dpy_gfx_update(QEMU_CONSOLE(s), 0, 0, w, h);
+    vt100_image_update(&s->vt, 0, 0, w, h);
 }
 
 static void console_scroll(QemuTextConsole *s, int ydelta)
@@ -1075,9 +1083,9 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
     }
     vt100_show_cursor(&s->vt, 1);
     if (s->vt.update_x0 < s->vt.update_x1) {
-        dpy_gfx_update(QEMU_CONSOLE(s), s->vt.update_x0, s->vt.update_y0,
-                       s->vt.update_x1 - s->vt.update_x0,
-                       s->vt.update_y1 - s->vt.update_y0);
+        vt100_image_update(&s->vt, s->vt.update_x0, s->vt.update_y0,
+                           s->vt.update_x1 - s->vt.update_x0,
+                           s->vt.update_y1 - s->vt.update_y0);
     }
     return len;
 }
@@ -1175,6 +1183,13 @@ void qemu_text_console_update_size(QemuTextConsole *c)
     dpy_text_resize(QEMU_CONSOLE(c), c->vt.width, c->vt.height);
 }
 
+static void text_console_image_update(QemuVT100 *vt, int x, int y, int width, int height)
+{
+    QemuTextConsole *console = container_of(vt, QemuTextConsole, vt);
+
+    dpy_gfx_update(QEMU_CONSOLE(console), x, y, width, height);
+}
+
 static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
 {
     ChardevVC *vc = backend->u.vc.data;
@@ -1205,6 +1220,7 @@ static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
     }
 
     dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
+    s->vt.image_update = text_console_image_update;
 
     s->chr = chr;
     drv->console = s;

-- 
2.53.0



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

* [PATCH 21/60] ui/console-vc: console_refresh() -> vt100_refresh()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (19 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 20/60] ui/console-vc: decouple VT100 display updates via function pointer Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 22/60] ui/console-vc: move cursor blinking logic into VT100 layer Marc-André Lureau
                   ` (39 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

This decouples glyph rendering from the console object, continuing the
QemuVT100 abstraction introduced in the previous commits.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 8a18659036f..d2a7d527586 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -232,35 +232,35 @@ static void vt100_image_update(QemuVT100 *vt, int x, int y, int width, int heigh
     vt->image_update(vt, x, y, width, height);
 }
 
-static void console_refresh(QemuTextConsole *s)
+static void vt100_refresh(QemuVT100 *vt)
 {
     TextCell *c;
     int x, y, y1;
-    int w = pixman_image_get_width(s->vt.image);
-    int h = pixman_image_get_height(s->vt.image);
+    int w = pixman_image_get_width(vt->image);
+    int h = pixman_image_get_height(vt->image);
 
-    s->vt.text_x[0] = 0;
-    s->vt.text_y[0] = 0;
-    s->vt.text_x[1] = s->vt.width - 1;
-    s->vt.text_y[1] = s->vt.height - 1;
-    s->vt.cursor_invalidate = 1;
+    vt->text_x[0] = 0;
+    vt->text_y[0] = 0;
+    vt->text_x[1] = vt->width - 1;
+    vt->text_y[1] = vt->height - 1;
+    vt->cursor_invalidate = 1;
 
-    image_fill_rect(s->vt.image, 0, 0, w, h,
+    image_fill_rect(vt->image, 0, 0, w, h,
                     color_table_rgb[0][QEMU_COLOR_BLACK]);
-    y1 = s->vt.y_displayed;
-    for (y = 0; y < s->vt.height; y++) {
-        c = s->vt.cells + y1 * s->vt.width;
-        for (x = 0; x < s->vt.width; x++) {
-            vt100_putcharxy(&s->vt, x, y, c->ch,
+    y1 = vt->y_displayed;
+    for (y = 0; y < vt->height; y++) {
+        c = vt->cells + y1 * vt->width;
+        for (x = 0; x < vt->width; x++) {
+            vt100_putcharxy(vt, x, y, c->ch,
                           &(c->t_attrib));
             c++;
         }
-        if (++y1 == s->vt.total_height) {
+        if (++y1 == vt->total_height) {
             y1 = 0;
         }
     }
-    vt100_show_cursor(&s->vt, 1);
-    vt100_image_update(&s->vt, 0, 0, w, h);
+    vt100_show_cursor(vt, 1);
+    vt100_image_update(vt, 0, 0, w, h);
 }
 
 static void console_scroll(QemuTextConsole *s, int ydelta)
@@ -289,7 +289,7 @@ static void console_scroll(QemuTextConsole *s, int ydelta)
                 s->vt.y_displayed = s->vt.total_height - 1;
         }
     }
-    console_refresh(s);
+    vt100_refresh(&s->vt);
 }
 
 static void kbd_send_chars(QemuTextConsole *s)
@@ -1113,7 +1113,7 @@ static void text_console_invalidate(void *opaque)
     if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
         text_console_resize(QEMU_TEXT_CONSOLE(s));
     }
-    console_refresh(s);
+    vt100_refresh(&s->vt);
 }
 
 static void

-- 
2.53.0



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

* [PATCH 22/60] ui/console-vc: move cursor blinking logic into VT100 layer
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (20 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 21/60] ui/console-vc: console_refresh() -> vt100_refresh() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 23/60] ui/console-vc: console_scroll() -> vt100_scroll() Marc-André Lureau
                   ` (38 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Maintain a list of QemuVT100 instances so the cursor timer can directly
iterate over them and call vt100_refresh(), instead of going through
qemu_invalidate_text_consoles() which iterated over all consoles
(including graphic ones) and called back into the generic display layer.

This removes the qemu_invalidate_text_consoles() function from
console.c, further decoupling VT100 text rendering from the console
core.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h  |  1 -
 ui/console-priv.h     |  2 +-
 ui/console-vc-stubs.c |  2 +-
 ui/console-vc.c       | 28 +++++++++++++++++++++++-----
 ui/console.c          | 19 +------------------
 5 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 4896e9e0e04..401e5a010fd 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -416,7 +416,6 @@ void qemu_console_set_window_id(QemuConsole *con, int window_id);
 void qemu_console_resize(QemuConsole *con, int width, int height);
 DisplaySurface *qemu_console_surface(QemuConsole *con);
 void coroutine_fn qemu_console_co_wait_update(QemuConsole *con);
-int qemu_invalidate_text_consoles(void);
 
 /* console-gl.c */
 #ifdef CONFIG_OPENGL
diff --git a/ui/console-priv.h b/ui/console-priv.h
index 2c2cfd99570..8bcdb79d914 100644
--- a/ui/console-priv.h
+++ b/ui/console-priv.h
@@ -36,7 +36,7 @@ struct QemuConsole {
 };
 
 void qemu_text_console_update_size(QemuTextConsole *c);
-void qemu_text_console_update_cursor(void);
+void vt100_update_cursor(void);
 void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym);
 
 #endif
diff --git a/ui/console-vc-stubs.c b/ui/console-vc-stubs.c
index 8a7f19c1f44..d911a82f263 100644
--- a/ui/console-vc-stubs.c
+++ b/ui/console-vc-stubs.c
@@ -14,7 +14,7 @@ void qemu_text_console_update_size(QemuTextConsole *c)
 {
 }
 
-void qemu_text_console_update_cursor(void)
+void vt100_update_cursor(void)
 {
 }
 
diff --git a/ui/console-vc.c b/ui/console-vc.c
index d2a7d527586..22d1128ee6b 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -8,6 +8,7 @@
 #include "qapi/error.h"
 #include "qemu/fifo8.h"
 #include "qemu/option.h"
+#include "qemu/queue.h"
 #include "ui/console.h"
 #include "ui/cp437.h"
 
@@ -69,8 +70,13 @@ struct QemuVT100 {
     int update_y0;
     int update_x1;
     int update_y1;
+
+    QTAILQ_ENTRY(QemuVT100) list;
 };
 
+static QTAILQ_HEAD(QemuVT100Head, QemuVT100) vt100s =
+    QTAILQ_HEAD_INITIALIZER(vt100s);
+
 typedef struct QemuTextConsole {
     QemuConsole parent;
 
@@ -1090,20 +1096,28 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
     return len;
 }
 
-void qemu_text_console_update_cursor(void)
+void vt100_update_cursor(void)
 {
+    QemuVT100 *vt;
+
     cursor_visible_phase = !cursor_visible_phase;
 
-    if (qemu_invalidate_text_consoles()) {
-        timer_mod(cursor_timer,
-                  qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
+    if (QTAILQ_EMPTY(&vt100s)) {
+        return;
+    }
+
+    QTAILQ_FOREACH(vt, &vt100s, list) {
+        vt100_refresh(vt);
     }
+
+    timer_mod(cursor_timer,
+        qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
 }
 
 static void
 cursor_timer_cb(void *opaque)
 {
-    qemu_text_console_update_cursor();
+    vt100_update_cursor();
 }
 
 static void text_console_invalidate(void *opaque)
@@ -1119,6 +1133,9 @@ static void text_console_invalidate(void *opaque)
 static void
 qemu_text_console_finalize(Object *obj)
 {
+    QemuTextConsole *s = QEMU_TEXT_CONSOLE(obj);
+
+    QTAILQ_REMOVE(&vt100s, &s->vt, list);
 }
 
 static void
@@ -1143,6 +1160,7 @@ qemu_text_console_init(Object *obj)
 {
     QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
 
+    QTAILQ_INSERT_HEAD(&vt100s, &c->vt, list);
     fifo8_create(&c->out_fifo, 16);
     c->vt.total_height = DEFAULT_BACKSCROLL;
     QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
diff --git a/ui/console.c b/ui/console.c
index 24e4761e1f9..8af1dee0e22 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -752,7 +752,7 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
     } else if (QEMU_IS_TEXT_CONSOLE(dcl->con)) {
         qemu_text_console_update_size(QEMU_TEXT_CONSOLE(dcl->con));
     }
-    qemu_text_console_update_cursor();
+    vt100_update_cursor();
 }
 
 void update_displaychangelistener(DisplayChangeListener *dcl,
@@ -1457,23 +1457,6 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
     }
 }
 
-int qemu_invalidate_text_consoles(void)
-{
-    QemuConsole *s;
-    int count = 0;
-
-    QTAILQ_FOREACH(s, &consoles, next) {
-        if (qemu_console_is_graphic(s) ||
-            !qemu_console_is_visible(s)) {
-            continue;
-        }
-        count++;
-        graphic_hw_invalidate(s);
-    }
-
-    return count;
-}
-
 void qemu_console_resize(QemuConsole *s, int width, int height)
 {
     DisplaySurface *surface = qemu_console_surface(s);

-- 
2.53.0



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

* [PATCH 23/60] ui/console-vc: console_scroll() -> vt100_scroll()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (21 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 22/60] ui/console-vc: move cursor blinking logic into VT100 layer Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 24/60] ui/console-vc: refactor text_console_resize() into vt100_set_image() Marc-André Lureau
                   ` (37 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

This decouples glyph rendering from the console object, continuing the
QemuVT100 abstraction introduced in the previous commits.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 22d1128ee6b..a45f4bc875b 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -269,33 +269,33 @@ static void vt100_refresh(QemuVT100 *vt)
     vt100_image_update(vt, 0, 0, w, h);
 }
 
-static void console_scroll(QemuTextConsole *s, int ydelta)
+static void vt100_scroll(QemuVT100 *vt, int ydelta)
 {
     int i, y1;
 
     if (ydelta > 0) {
         for(i = 0; i < ydelta; i++) {
-            if (s->vt.y_displayed == s->vt.y_base)
+            if (vt->y_displayed == vt->y_base)
                 break;
-            if (++s->vt.y_displayed == s->vt.total_height)
-                s->vt.y_displayed = 0;
+            if (++vt->y_displayed == vt->total_height)
+                vt->y_displayed = 0;
         }
     } else {
         ydelta = -ydelta;
-        i = s->vt.backscroll_height;
-        if (i > s->vt.total_height - s->vt.height)
-            i = s->vt.total_height - s->vt.height;
-        y1 = s->vt.y_base - i;
+        i = vt->backscroll_height;
+        if (i > vt->total_height - vt->height)
+            i = vt->total_height - vt->height;
+        y1 = vt->y_base - i;
         if (y1 < 0)
-            y1 += s->vt.total_height;
+            y1 += vt->total_height;
         for(i = 0; i < ydelta; i++) {
-            if (s->vt.y_displayed == y1)
+            if (vt->y_displayed == y1)
                 break;
-            if (--s->vt.y_displayed < 0)
-                s->vt.y_displayed = s->vt.total_height - 1;
+            if (--vt->y_displayed < 0)
+                vt->y_displayed = vt->total_height - 1;
         }
     }
-    vt100_refresh(&s->vt);
+    vt100_refresh(vt);
 }
 
 static void kbd_send_chars(QemuTextConsole *s)
@@ -324,16 +324,16 @@ void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
 
     switch(keysym) {
     case QEMU_KEY_CTRL_UP:
-        console_scroll(s, -1);
+        vt100_scroll(&s->vt, -1);
         break;
     case QEMU_KEY_CTRL_DOWN:
-        console_scroll(s, 1);
+        vt100_scroll(&s->vt, 1);
         break;
     case QEMU_KEY_CTRL_PAGEUP:
-        console_scroll(s, -10);
+        vt100_scroll(&s->vt, -10);
         break;
     case QEMU_KEY_CTRL_PAGEDOWN:
-        console_scroll(s, 10);
+        vt100_scroll(&s->vt, 10);
         break;
     default:
         /* convert the QEMU keysym to VT100 key string */

-- 
2.53.0



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

* [PATCH 24/60] ui/console-vc: refactor text_console_resize() into vt100_set_image()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (22 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 23/60] ui/console-vc: console_scroll() -> vt100_scroll() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 25/60] ui/console-vc: move vc_put_lf() to VT100 layer as vt100_put_lf() Marc-André Lureau
                   ` (36 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Decouple the resize logic from QemuTextConsole by operating on
QemuVT100 and taking a pixman_image_t directly, instead of reaching
into the console's scanout surface. The callers now pass the image
explicitly, which makes the VT100 layer independent of the console
object hierarchy.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index a45f4bc875b..efb10c2f8f7 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -12,6 +12,7 @@
 #include "ui/console.h"
 #include "ui/cp437.h"
 
+#include "pixman.h"
 #include "trace.h"
 #include "console-priv.h"
 
@@ -395,44 +396,41 @@ static void text_console_update(void *opaque, console_ch_t *chardata)
     }
 }
 
-static void text_console_resize(QemuTextConsole *t)
+static void vt100_set_image(QemuVT100 *vt, pixman_image_t *image)
 {
-    QemuConsole *s = QEMU_CONSOLE(t);
     TextCell *cells, *c, *c1;
     int w1, x, y, last_width, w, h;
 
-    assert(s->scanout.kind == SCANOUT_SURFACE);
-
-    t->vt.image = s->surface->image;
-    w = pixman_image_get_width(t->vt.image) / FONT_WIDTH;
-    h = pixman_image_get_height(t->vt.image) / FONT_HEIGHT;
-    if (w == t->vt.width && h == t->vt.height) {
+    vt->image = image;
+    w = pixman_image_get_width(image) / FONT_WIDTH;
+    h = pixman_image_get_height(image) / FONT_HEIGHT;
+    if (w == vt->width && h == vt->height) {
         return;
     }
 
-    last_width = t->vt.width;
-    t->vt.width = w;
-    t->vt.height = h;
+    last_width = vt->width;
+    vt->width = w;
+    vt->height = h;
 
-    w1 = MIN(t->vt.width, last_width);
+    w1 = MIN(vt->width, last_width);
 
-    cells = g_new(TextCell, t->vt.width * t->vt.total_height + 1);
-    for (y = 0; y < t->vt.total_height; y++) {
-        c = &cells[y * t->vt.width];
+    cells = g_new(TextCell, vt->width * vt->total_height + 1);
+    for (y = 0; y < vt->total_height; y++) {
+        c = &cells[y * vt->width];
         if (w1 > 0) {
-            c1 = &t->vt.cells[y * last_width];
+            c1 = &vt->cells[y * last_width];
             for (x = 0; x < w1; x++) {
                 *c++ = *c1++;
             }
         }
-        for (x = w1; x < t->vt.width; x++) {
+        for (x = w1; x < vt->width; x++) {
             c->ch = ' ';
             c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
             c++;
         }
     }
-    g_free(t->vt.cells);
-    t->vt.cells = cells;
+    g_free(vt->cells);
+    vt->cells = cells;
 }
 
 static void vc_put_lf(VCChardev *vc)
@@ -1125,7 +1123,7 @@ static void text_console_invalidate(void *opaque)
     QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
 
     if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
-        text_console_resize(QEMU_TEXT_CONSOLE(s));
+        vt100_set_image(&s->vt, QEMU_CONSOLE(s)->surface->image);
     }
     vt100_refresh(&s->vt);
 }
@@ -1245,7 +1243,7 @@ static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
 
     /* set current text attributes to default */
     drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-    text_console_resize(s);
+    vt100_set_image(&s->vt, QEMU_CONSOLE(s)->surface->image);
 
     if (chr->label) {
         char *msg;

-- 
2.53.0



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

* [PATCH 25/60] ui/console-vc: move vc_put_lf() to VT100 layer as vt100_put_lf()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (23 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 24/60] ui/console-vc: refactor text_console_resize() into vt100_set_image() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 26/60] ui/console-vc: unify the write path Marc-André Lureau
                   ` (35 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Decouple the line-feed handling from VCChardev by operating on
QemuVT100 directly. The function no longer needs the chardev or
console pointers — callers pass &s->vt instead. This continues the
effort to make the VT100 terminal emulation self-contained.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index efb10c2f8f7..4ea9f88f55a 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -433,47 +433,46 @@ static void vt100_set_image(QemuVT100 *vt, pixman_image_t *image)
     vt->cells = cells;
 }
 
-static void vc_put_lf(VCChardev *vc)
+static void vt100_put_lf(QemuVT100 *vt)
 {
-    QemuTextConsole *s = vc->console;
     TextCell *c;
     int x, y1;
 
-    s->vt.y++;
-    if (s->vt.y >= s->vt.height) {
-        s->vt.y = s->vt.height - 1;
+    vt->y++;
+    if (vt->y >= vt->height) {
+        vt->y = vt->height - 1;
 
-        if (s->vt.y_displayed == s->vt.y_base) {
-            if (++s->vt.y_displayed == s->vt.total_height)
-                s->vt.y_displayed = 0;
+        if (vt->y_displayed == vt->y_base) {
+            if (++vt->y_displayed == vt->total_height)
+                vt->y_displayed = 0;
         }
-        if (++s->vt.y_base == s->vt.total_height)
-            s->vt.y_base = 0;
-        if (s->vt.backscroll_height < s->vt.total_height)
-            s->vt.backscroll_height++;
-        y1 = (s->vt.y_base + s->vt.height - 1) % s->vt.total_height;
-        c = &s->vt.cells[y1 * s->vt.width];
-        for(x = 0; x < s->vt.width; x++) {
+        if (++vt->y_base == vt->total_height)
+            vt->y_base = 0;
+        if (vt->backscroll_height < vt->total_height)
+            vt->backscroll_height++;
+        y1 = (vt->y_base + vt->height - 1) % vt->total_height;
+        c = &vt->cells[y1 * vt->width];
+        for(x = 0; x < vt->width; x++) {
             c->ch = ' ';
             c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
             c++;
         }
-        if (s->vt.y_displayed == s->vt.y_base) {
-            s->vt.text_x[0] = 0;
-            s->vt.text_y[0] = 0;
-            s->vt.text_x[1] = s->vt.width - 1;
-            s->vt.text_y[1] = s->vt.height - 1;
-
-            image_bitblt(s->vt.image, 0, FONT_HEIGHT, 0, 0,
-                         s->vt.width * FONT_WIDTH,
-                         (s->vt.height - 1) * FONT_HEIGHT);
-            image_fill_rect(s->vt.image, 0, (s->vt.height - 1) * FONT_HEIGHT,
-                            s->vt.width * FONT_WIDTH, FONT_HEIGHT,
+        if (vt->y_displayed == vt->y_base) {
+            vt->text_x[0] = 0;
+            vt->text_y[0] = 0;
+            vt->text_x[1] = vt->width - 1;
+            vt->text_y[1] = vt->height - 1;
+
+            image_bitblt(vt->image, 0, FONT_HEIGHT, 0, 0,
+                         vt->width * FONT_WIDTH,
+                         (vt->height - 1) * FONT_HEIGHT);
+            image_fill_rect(vt->image, 0, (vt->height - 1) * FONT_HEIGHT,
+                            vt->width * FONT_WIDTH, FONT_HEIGHT,
                             color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
-            s->vt.update_x0 = 0;
-            s->vt.update_y0 = 0;
-            s->vt.update_x1 = s->vt.width * FONT_WIDTH;
-            s->vt.update_y1 = s->vt.height * FONT_HEIGHT;
+            vt->update_x0 = 0;
+            vt->update_y0 = 0;
+            vt->update_x1 = vt->width * FONT_WIDTH;
+            vt->update_y1 = vt->height * FONT_HEIGHT;
         }
     }
 }
@@ -664,7 +663,7 @@ static void vc_put_one(VCChardev *vc, int ch)
     if (s->vt.x >= s->vt.width) {
         /* line wrap */
         s->vt.x = 0;
-        vc_put_lf(vc);
+        vt100_put_lf(&s->vt);
     }
     y1 = (s->vt.y_base + s->vt.y) % s->vt.total_height;
     c = &s->vt.cells[y1 * s->vt.width + s->vt.x];
@@ -842,7 +841,7 @@ static void vc_putchar(VCChardev *vc, int ch)
             s->vt.x = 0;
             break;
         case '\n':  /* newline */
-            vc_put_lf(vc);
+            vt100_put_lf(&s->vt);
             break;
         case '\b':  /* backspace */
             if (s->vt.x > 0)
@@ -851,7 +850,7 @@ static void vc_putchar(VCChardev *vc, int ch)
         case '\t':  /* tabspace */
             if (s->vt.x + (8 - (s->vt.x % 8)) > s->vt.width) {
                 s->vt.x = 0;
-                vc_put_lf(vc);
+                vt100_put_lf(&s->vt);
             } else {
                 s->vt.x = s->vt.x + (8 - (s->vt.x % 8));
             }

-- 
2.53.0



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

* [PATCH 26/60] ui/console-vc: unify the write path
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (24 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 25/60] ui/console-vc: move vc_put_lf() to VT100 layer as vt100_put_lf() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 27/60] ui/console-vc: move VT100 state machine and output FIFO into QemuVT100 Marc-André Lureau
                   ` (34 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

VT100 escape responses (DSR) used qemu_chr_be_write() to write directly
to the chardev backend, bypassing the output FIFO, while keyboard input
went through the FIFO and flush path. This inconsistency could lead to
out-of-order delivery when both paths are active.

Introduce qemu_text_console_write() that pushes data into the output
FIFO and flushes it, and use it for both keyboard input and VT100
responses. Remove the now-unnecessary vc_respond_str() helper. Rename
kbd_send_chars() to qemu_text_console_flush() to better reflect its
purpose.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 4ea9f88f55a..8d4178f8cab 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -299,7 +299,7 @@ static void vt100_scroll(QemuVT100 *vt, int ydelta)
     vt100_refresh(vt);
 }
 
-static void kbd_send_chars(QemuTextConsole *s)
+static void qemu_text_console_flush(QemuTextConsole *s)
 {
     uint32_t len, avail;
 
@@ -316,12 +316,20 @@ static void kbd_send_chars(QemuTextConsole *s)
     }
 }
 
+static void qemu_text_console_write(QemuTextConsole *s, const void *buf, size_t len)
+{
+    uint32_t num_free;
+
+    num_free = fifo8_num_free(&s->out_fifo);
+    fifo8_push_all(&s->out_fifo, buf, MIN(num_free, len));
+    qemu_text_console_flush(s);
+}
+
 /* called when an ascii key is pressed */
 void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
 {
     uint8_t buf[16], *q;
     int c;
-    uint32_t num_free;
 
     switch(keysym) {
     case QEMU_KEY_CTRL_UP:
@@ -360,9 +368,7 @@ void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
         if (s->vt.echo) {
             qemu_chr_write(s->chr, buf, q - buf, true);
         }
-        num_free = fifo8_num_free(&s->out_fifo);
-        fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
-        kbd_send_chars(s);
+        qemu_text_console_write(s, buf, q - buf);
         break;
     }
 }
@@ -673,13 +679,6 @@ static void vc_put_one(VCChardev *vc, int ch)
     s->vt.x++;
 }
 
-static void vc_respond_str(VCChardev *vc, const char *buf)
-{
-    QemuTextConsole *s = vc->console;
-
-    qemu_chr_be_write(s->chr, (const uint8_t *)buf, strlen(buf));
-}
-
 /* set cursor, checking bounds */
 static void vc_set_cursor(VCChardev *vc, int x, int y)
 {
@@ -1018,13 +1017,13 @@ static void vc_putchar(VCChardev *vc, int ch)
                 switch (vc->esc_params[0]) {
                 case 5:
                     /* report console status (always succeed)*/
-                    vc_respond_str(vc, "\033[0n");
+                    qemu_text_console_write(s, "\033[0n", 4);
                     break;
                 case 6:
                     /* report cursor position */
                     response = g_strdup_printf("\033[%d;%dR",
                                                s->vt.y + 1, s->vt.x + 1);
-                    vc_respond_str(vc, response);
+                    qemu_text_console_write(s, response, strlen(response));
                     break;
                 }
                 break;
@@ -1183,7 +1182,7 @@ static void vc_chr_accept_input(Chardev *chr)
 {
     VCChardev *drv = VC_CHARDEV(chr);
 
-    kbd_send_chars(drv->console);
+    qemu_text_console_flush(drv->console);
 }
 
 static void vc_chr_set_echo(Chardev *chr, bool echo)

-- 
2.53.0



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

* [PATCH 27/60] ui/console-vc: move VT100 state machine and output FIFO into QemuVT100
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (25 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 26/60] ui/console-vc: unify the write path Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 28/60] ui/console-vc: extract vt100_input() from vc_chr_write() Marc-André Lureau
                   ` (33 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Move the terminal escape sequence parser state (TTYState, esc_params,
text attributes, saved cursor position) and the output FIFO from
VCChardev/QemuTextConsole into QemuVT100. Rename the corresponding
functions from vc_* to vt100_* to reflect they now operate on the VT100
layer directly, removing the indirection through vc->console->vt.

Add an out_flush callback to QemuVT100 so vt100_write() can flush
output without knowing about QemuTextConsole, and move FIFO/VT100
initialization from qemu_text_console_init() to vc_chr_open() where
the callback can be wired up.

This continues the decoupling of VT100 terminal emulation from the
chardev layer, making QemuVT100 a self-contained terminal emulator.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 8d4178f8cab..87881c072ee 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -72,6 +72,18 @@ struct QemuVT100 {
     int update_x1;
     int update_y1;
 
+    enum TTYState state;
+    int esc_params[MAX_ESC_PARAMS];
+    int nb_esc_params;
+    uint32_t utf8_state;     /* UTF-8 DFA decoder state */
+    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
+    TextAttributes t_attrib; /* currently active text attributes */
+    TextAttributes t_attrib_saved;
+    int x_saved, y_saved;
+    /* fifo for key pressed */
+    Fifo8 out_fifo;
+    void (*out_flush)(QemuVT100 *vt);
+
     QTAILQ_ENTRY(QemuVT100) list;
 };
 
@@ -83,8 +95,6 @@ typedef struct QemuTextConsole {
 
     QemuVT100 vt;
     Chardev *chr;
-    /* fifo for key pressed */
-    Fifo8 out_fifo;
 } QemuTextConsole;
 
 typedef QemuConsoleClass QemuTextConsoleClass;
@@ -102,15 +112,6 @@ OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEX
 struct VCChardev {
     Chardev parent;
     QemuTextConsole *console;
-
-    enum TTYState state;
-    int esc_params[MAX_ESC_PARAMS];
-    int nb_esc_params;
-    uint32_t utf8_state;     /* UTF-8 DFA decoder state */
-    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
-    TextAttributes t_attrib; /* currently active text attributes */
-    TextAttributes t_attrib_saved;
-    int x_saved, y_saved;
 };
 typedef struct VCChardev VCChardev;
 
@@ -299,30 +300,30 @@ static void vt100_scroll(QemuVT100 *vt, int ydelta)
     vt100_refresh(vt);
 }
 
-static void qemu_text_console_flush(QemuTextConsole *s)
+static void qemu_text_console_out_flush(QemuTextConsole *s)
 {
     uint32_t len, avail;
 
     len = qemu_chr_be_can_write(s->chr);
-    avail = fifo8_num_used(&s->out_fifo);
+    avail = fifo8_num_used(&s->vt.out_fifo);
     while (len > 0 && avail > 0) {
         const uint8_t *buf;
         uint32_t size;
 
-        buf = fifo8_pop_bufptr(&s->out_fifo, MIN(len, avail), &size);
+        buf = fifo8_pop_bufptr(&s->vt.out_fifo, MIN(len, avail), &size);
         qemu_chr_be_write(s->chr, buf, size);
         len = qemu_chr_be_can_write(s->chr);
         avail -= size;
     }
 }
 
-static void qemu_text_console_write(QemuTextConsole *s, const void *buf, size_t len)
+static void vt100_write(QemuVT100 *vt, const void *buf, size_t len)
 {
     uint32_t num_free;
 
-    num_free = fifo8_num_free(&s->out_fifo);
-    fifo8_push_all(&s->out_fifo, buf, MIN(num_free, len));
-    qemu_text_console_flush(s);
+    num_free = fifo8_num_free(&vt->out_fifo);
+    fifo8_push_all(&vt->out_fifo, buf, MIN(num_free, len));
+    vt->out_flush(vt);
 }
 
 /* called when an ascii key is pressed */
@@ -368,7 +369,7 @@ void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
         if (s->vt.echo) {
             qemu_chr_write(s->chr, buf, q - buf, true);
         }
-        qemu_text_console_write(s, buf, q - buf);
+        vt100_write(&s->vt, buf, q - buf);
         break;
     }
 }
@@ -487,137 +488,135 @@ static void vt100_put_lf(QemuVT100 *vt)
  * NOTE: I know this code is not very efficient (checking every color for it
  * self) but it is more readable and better maintainable.
  */
-static void vc_handle_escape(VCChardev *vc)
+static void vt100_handle_escape(QemuVT100 *vt)
 {
     int i;
 
-    for (i = 0; i < vc->nb_esc_params; i++) {
-        switch (vc->esc_params[i]) {
+    for (i = 0; i < vt->nb_esc_params; i++) {
+        switch (vt->esc_params[i]) {
             case 0: /* reset all console attributes to default */
-                vc->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+                vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
                 break;
             case 1:
-                vc->t_attrib.bold = 1;
+                vt->t_attrib.bold = 1;
                 break;
             case 4:
-                vc->t_attrib.uline = 1;
+                vt->t_attrib.uline = 1;
                 break;
             case 5:
-                vc->t_attrib.blink = 1;
+                vt->t_attrib.blink = 1;
                 break;
             case 7:
-                vc->t_attrib.invers = 1;
+                vt->t_attrib.invers = 1;
                 break;
             case 8:
-                vc->t_attrib.unvisible = 1;
+                vt->t_attrib.unvisible = 1;
                 break;
             case 22:
-                vc->t_attrib.bold = 0;
+                vt->t_attrib.bold = 0;
                 break;
             case 24:
-                vc->t_attrib.uline = 0;
+                vt->t_attrib.uline = 0;
                 break;
             case 25:
-                vc->t_attrib.blink = 0;
+                vt->t_attrib.blink = 0;
                 break;
             case 27:
-                vc->t_attrib.invers = 0;
+                vt->t_attrib.invers = 0;
                 break;
             case 28:
-                vc->t_attrib.unvisible = 0;
+                vt->t_attrib.unvisible = 0;
                 break;
             /* set foreground color */
             case 30:
-                vc->t_attrib.fgcol = QEMU_COLOR_BLACK;
+                vt->t_attrib.fgcol = QEMU_COLOR_BLACK;
                 break;
             case 31:
-                vc->t_attrib.fgcol = QEMU_COLOR_RED;
+                vt->t_attrib.fgcol = QEMU_COLOR_RED;
                 break;
             case 32:
-                vc->t_attrib.fgcol = QEMU_COLOR_GREEN;
+                vt->t_attrib.fgcol = QEMU_COLOR_GREEN;
                 break;
             case 33:
-                vc->t_attrib.fgcol = QEMU_COLOR_YELLOW;
+                vt->t_attrib.fgcol = QEMU_COLOR_YELLOW;
                 break;
             case 34:
-                vc->t_attrib.fgcol = QEMU_COLOR_BLUE;
+                vt->t_attrib.fgcol = QEMU_COLOR_BLUE;
                 break;
             case 35:
-                vc->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
+                vt->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
                 break;
             case 36:
-                vc->t_attrib.fgcol = QEMU_COLOR_CYAN;
+                vt->t_attrib.fgcol = QEMU_COLOR_CYAN;
                 break;
             case 37:
-                vc->t_attrib.fgcol = QEMU_COLOR_WHITE;
+                vt->t_attrib.fgcol = QEMU_COLOR_WHITE;
                 break;
             /* set background color */
             case 40:
-                vc->t_attrib.bgcol = QEMU_COLOR_BLACK;
+                vt->t_attrib.bgcol = QEMU_COLOR_BLACK;
                 break;
             case 41:
-                vc->t_attrib.bgcol = QEMU_COLOR_RED;
+                vt->t_attrib.bgcol = QEMU_COLOR_RED;
                 break;
             case 42:
-                vc->t_attrib.bgcol = QEMU_COLOR_GREEN;
+                vt->t_attrib.bgcol = QEMU_COLOR_GREEN;
                 break;
             case 43:
-                vc->t_attrib.bgcol = QEMU_COLOR_YELLOW;
+                vt->t_attrib.bgcol = QEMU_COLOR_YELLOW;
                 break;
             case 44:
-                vc->t_attrib.bgcol = QEMU_COLOR_BLUE;
+                vt->t_attrib.bgcol = QEMU_COLOR_BLUE;
                 break;
             case 45:
-                vc->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
+                vt->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
                 break;
             case 46:
-                vc->t_attrib.bgcol = QEMU_COLOR_CYAN;
+                vt->t_attrib.bgcol = QEMU_COLOR_CYAN;
                 break;
             case 47:
-                vc->t_attrib.bgcol = QEMU_COLOR_WHITE;
+                vt->t_attrib.bgcol = QEMU_COLOR_WHITE;
                 break;
         }
     }
 }
 
-static void vc_update_xy(VCChardev *vc, int x, int y)
+static void vt100_update_xy(QemuVT100 *vt, int x, int y)
 {
-    QemuTextConsole *s = vc->console;
     TextCell *c;
     int y1, y2;
 
-    s->vt.text_x[0] = MIN(s->vt.text_x[0], x);
-    s->vt.text_x[1] = MAX(s->vt.text_x[1], x);
-    s->vt.text_y[0] = MIN(s->vt.text_y[0], y);
-    s->vt.text_y[1] = MAX(s->vt.text_y[1], y);
+    vt->text_x[0] = MIN(vt->text_x[0], x);
+    vt->text_x[1] = MAX(vt->text_x[1], x);
+    vt->text_y[0] = MIN(vt->text_y[0], y);
+    vt->text_y[1] = MAX(vt->text_y[1], y);
 
-    y1 = (s->vt.y_base + y) % s->vt.total_height;
-    y2 = y1 - s->vt.y_displayed;
+    y1 = (vt->y_base + y) % vt->total_height;
+    y2 = y1 - vt->y_displayed;
     if (y2 < 0) {
-        y2 += s->vt.total_height;
+        y2 += vt->total_height;
     }
-    if (y2 < s->vt.height) {
-        if (x >= s->vt.width) {
-            x = s->vt.width - 1;
+    if (y2 < vt->height) {
+        if (x >= vt->width) {
+            x = vt->width - 1;
         }
-        c = &s->vt.cells[y1 * s->vt.width + x];
-        vt100_putcharxy(&s->vt, x, y2, c->ch,
+        c = &vt->cells[y1 * vt->width + x];
+        vt100_putcharxy(vt, x, y2, c->ch,
                       &(c->t_attrib));
-        vt100_invalidate_xy(&s->vt, x, y2);
+        vt100_invalidate_xy(vt, x, y2);
     }
 }
 
-static void vc_clear_xy(VCChardev *vc, int x, int y)
+static void vt100_clear_xy(QemuVT100 *vt, int x, int y)
 {
-    QemuTextConsole *s = vc->console;
-    int y1 = (s->vt.y_base + y) % s->vt.total_height;
-    if (x >= s->vt.width) {
-        x = s->vt.width - 1;
+    int y1 = (vt->y_base + y) % vt->total_height;
+    if (x >= vt->width) {
+        x = vt->width - 1;
     }
-    TextCell *c = &s->vt.cells[y1 * s->vt.width + x];
+    TextCell *c = &vt->cells[y1 * vt->width + x];
     c->ch = ' ';
     c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-    vc_update_xy(vc, x, y);
+    vt100_update_xy(vt, x, y);
 }
 
 /*
@@ -661,44 +660,41 @@ static uint32_t utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
     return *state;
 }
 
-static void vc_put_one(VCChardev *vc, int ch)
+static void vt100_put_one(QemuVT100 *vt, int ch)
 {
-    QemuTextConsole *s = vc->console;
     TextCell *c;
     int y1;
-    if (s->vt.x >= s->vt.width) {
+    if (vt->x >= vt->width) {
         /* line wrap */
-        s->vt.x = 0;
-        vt100_put_lf(&s->vt);
+        vt->x = 0;
+        vt100_put_lf(vt);
     }
-    y1 = (s->vt.y_base + s->vt.y) % s->vt.total_height;
-    c = &s->vt.cells[y1 * s->vt.width + s->vt.x];
+    y1 = (vt->y_base + vt->y) % vt->total_height;
+    c = &vt->cells[y1 * vt->width + vt->x];
     c->ch = ch;
-    c->t_attrib = vc->t_attrib;
-    vc_update_xy(vc, s->vt.x, s->vt.y);
-    s->vt.x++;
+    c->t_attrib = vt->t_attrib;
+    vt100_update_xy(vt, vt->x, vt->y);
+    vt->x++;
 }
 
 /* set cursor, checking bounds */
-static void vc_set_cursor(VCChardev *vc, int x, int y)
+static void vt100_set_cursor(QemuVT100 *vt, int x, int y)
 {
-    QemuTextConsole *s = vc->console;
-
     if (x < 0) {
         x = 0;
     }
     if (y < 0) {
         y = 0;
     }
-    if (y >= s->vt.height) {
-        y = s->vt.height - 1;
+    if (y >= vt->height) {
+        y = vt->height - 1;
     }
-    if (x >= s->vt.width) {
-        x = s->vt.width - 1;
+    if (x >= vt->width) {
+        x = vt->width - 1;
     }
 
-    s->vt.x = x;
-    s->vt.y = y;
+    vt->x = x;
+    vt->y = y;
 }
 
 /**
@@ -707,9 +703,8 @@ static void vc_set_cursor(VCChardev *vc, int x, int y)
  * characters between the cursor and right margin move to the
  * left. Character attributes move with the characters.
  */
-static void vc_csi_P(struct VCChardev *vc, unsigned int nr)
+static void vt100_csi_P(QemuVT100 *vt, unsigned int nr)
 {
-    QemuTextConsole *s = vc->console;
     TextCell *c1, *c2;
     unsigned int x1, x2, y;
     unsigned int end, len;
@@ -717,28 +712,28 @@ static void vc_csi_P(struct VCChardev *vc, unsigned int nr)
     if (!nr) {
         nr = 1;
     }
-    if (nr > s->vt.width - s->vt.x) {
-        nr = s->vt.width - s->vt.x;
+    if (nr > vt->width - vt->x) {
+        nr = vt->width - vt->x;
         if (!nr) {
             return;
         }
     }
 
-    x1 = s->vt.x;
-    x2 = s->vt.x + nr;
-    len = s->vt.width - x2;
+    x1 = vt->x;
+    x2 = vt->x + nr;
+    len = vt->width - x2;
     if (len) {
-        y = (s->vt.y_base + s->vt.y) % s->vt.total_height;
-        c1 = &s->vt.cells[y * s->vt.width + x1];
-        c2 = &s->vt.cells[y * s->vt.width + x2];
+        y = (vt->y_base + vt->y) % vt->total_height;
+        c1 = &vt->cells[y * vt->width + x1];
+        c2 = &vt->cells[y * vt->width + x2];
         memmove(c1, c2, len * sizeof(*c1));
         for (end = x1 + len; x1 < end; x1++) {
-            vc_update_xy(vc, x1, s->vt.y);
+            vt100_update_xy(vt, x1, vt->y);
         }
     }
     /* Clear the rest */
-    for (; x1 < s->vt.width; x1++) {
-        vc_clear_xy(vc, x1, s->vt.y);
+    for (; x1 < vt->width; x1++) {
+        vt100_clear_xy(vt, x1, vt->y);
     }
 }
 
@@ -748,9 +743,8 @@ static void vc_csi_P(struct VCChardev *vc, unsigned int nr)
  * blank characters. Text between the cursor and right margin moves to
  * the right. Characters scrolled past the right margin are lost.
  */
-static void vc_csi_at(struct VCChardev *vc, unsigned int nr)
+static void vt100_csi_at(QemuVT100 *vt, unsigned int nr)
 {
-    QemuTextConsole *s = vc->console;
     TextCell *c1, *c2;
     unsigned int x1, x2, y;
     unsigned int end, len;
@@ -758,74 +752,69 @@ static void vc_csi_at(struct VCChardev *vc, unsigned int nr)
     if (!nr) {
         nr = 1;
     }
-    if (nr > s->vt.width - s->vt.x) {
-        nr = s->vt.width - s->vt.x;
+    if (nr > vt->width - vt->x) {
+        nr = vt->width - vt->x;
         if (!nr) {
             return;
         }
     }
 
-    x1 = s->vt.x + nr;
-    x2 = s->vt.x;
-    len = s->vt.width - x1;
+    x1 = vt->x + nr;
+    x2 = vt->x;
+    len = vt->width - x1;
     if (len) {
-        y = (s->vt.y_base + s->vt.y) % s->vt.total_height;
-        c1 = &s->vt.cells[y * s->vt.width + x1];
-        c2 = &s->vt.cells[y * s->vt.width + x2];
+        y = (vt->y_base + vt->y) % vt->total_height;
+        c1 = &vt->cells[y * vt->width + x1];
+        c2 = &vt->cells[y * vt->width + x2];
         memmove(c1, c2, len * sizeof(*c1));
         for (end = x1 + len; x1 < end; x1++) {
-            vc_update_xy(vc, x1, s->vt.y);
+            vt100_update_xy(vt, x1, vt->y);
         }
     }
     /* Insert blanks */
-    for (x1 = s->vt.x; x1 < s->vt.x + nr; x1++) {
-        vc_clear_xy(vc, x1, s->vt.y);
+    for (x1 = vt->x; x1 < vt->x + nr; x1++) {
+        vt100_clear_xy(vt, x1, vt->y);
     }
 }
 
 /**
- * vc_save_cursor() - saves cursor position and character attributes.
+ * vt100_save_cursor() - saves cursor position and character attributes.
  */
-static void vc_save_cursor(VCChardev *vc)
+static void vt100_save_cursor(QemuVT100 *vt)
 {
-    QemuTextConsole *s = vc->console;
-
-    vc->x_saved = s->vt.x;
-    vc->y_saved = s->vt.y;
-    vc->t_attrib_saved = vc->t_attrib;
+    vt->x_saved = vt->x;
+    vt->y_saved = vt->y;
+    vt->t_attrib_saved = vt->t_attrib;
 }
 
 /**
- * vc_restore_cursor() - restores cursor position and character
+ * vt100_restore_cursor() - restores cursor position and character
  * attributes from saved state.
  */
-static void vc_restore_cursor(VCChardev *vc)
+static void vt100_restore_cursor(QemuVT100 *vt)
 {
-    QemuTextConsole *s = vc->console;
-
-    s->vt.x = vc->x_saved;
-    s->vt.y = vc->y_saved;
-    vc->t_attrib = vc->t_attrib_saved;
+    vt->x = vt->x_saved;
+    vt->y = vt->y_saved;
+    vt->t_attrib = vt->t_attrib_saved;
 }
 
-static void vc_putchar(VCChardev *vc, int ch)
+static void vt100_putchar(QemuVT100 *vt, int ch)
 {
-    QemuTextConsole *s = vc->console;
     int i;
     int x, y;
     g_autofree char *response = NULL;
 
-    switch(vc->state) {
+    switch (vt->state) {
     case TTY_STATE_NORM:
         /* Feed byte through the UTF-8 DFA decoder */
         if (ch >= 0x80) {
-            switch (utf8_decode(&vc->utf8_state, &vc->utf8_codepoint, ch)) {
+            switch (utf8_decode(&vt->utf8_state, &vt->utf8_codepoint, ch)) {
             case UTF8_ACCEPT:
-                vc_put_one(vc, unicode_to_cp437(vc->utf8_codepoint));
+                vt100_put_one(vt, unicode_to_cp437(vt->utf8_codepoint));
                 break;
             case UTF8_REJECT:
                 /* Reset state so the decoder can resync */
-                vc->utf8_state = UTF8_ACCEPT;
+                vt->utf8_state = UTF8_ACCEPT;
                 break;
             default:
                 /* Need more bytes */
@@ -834,24 +823,24 @@ static void vc_putchar(VCChardev *vc, int ch)
             break;
         }
         /* ASCII byte: abort any pending UTF-8 sequence */
-        vc->utf8_state = UTF8_ACCEPT;
+        vt->utf8_state = UTF8_ACCEPT;
         switch(ch) {
         case '\r':  /* carriage return */
-            s->vt.x = 0;
+            vt->x = 0;
             break;
         case '\n':  /* newline */
-            vt100_put_lf(&s->vt);
+            vt100_put_lf(vt);
             break;
         case '\b':  /* backspace */
-            if (s->vt.x > 0)
-                s->vt.x--;
+            if (vt->x > 0)
+                vt->x--;
             break;
         case '\t':  /* tabspace */
-            if (s->vt.x + (8 - (s->vt.x % 8)) > s->vt.width) {
-                s->vt.x = 0;
-                vt100_put_lf(&s->vt);
+            if (vt->x + (8 - (vt->x % 8)) > vt->width) {
+                vt->x = 0;
+                vt100_put_lf(vt);
             } else {
-                s->vt.x = s->vt.x + (8 - (s->vt.x % 8));
+                vt->x = vt->x + (8 - (vt->x % 8));
             }
             break;
         case '\a':  /* alert aka. bell */
@@ -864,177 +853,177 @@ static void vc_putchar(VCChardev *vc, int ch)
             /* SI (shift in), character set 0 (ignored) */
             break;
         case 27:    /* esc (introducing an escape sequence) */
-            vc->state = TTY_STATE_ESC;
+            vt->state = TTY_STATE_ESC;
             break;
         default:
-            vc_put_one(vc, ch);
+            vt100_put_one(vt, ch);
             break;
         }
         break;
     case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
         if (ch == '[') {
             for(i=0;i<MAX_ESC_PARAMS;i++)
-                vc->esc_params[i] = 0;
-            vc->nb_esc_params = 0;
-            vc->state = TTY_STATE_CSI;
+                vt->esc_params[i] = 0;
+            vt->nb_esc_params = 0;
+            vt->state = TTY_STATE_CSI;
         } else if (ch == '(') {
-            vc->state = TTY_STATE_G0;
+            vt->state = TTY_STATE_G0;
         } else if (ch == ')') {
-            vc->state = TTY_STATE_G1;
+            vt->state = TTY_STATE_G1;
         } else if (ch == ']' || ch == 'P' || ch == 'X'
                    || ch == '^' || ch == '_') {
             /* String sequences: OSC, DCS, SOS, PM, APC */
-            vc->state = TTY_STATE_OSC;
+            vt->state = TTY_STATE_OSC;
         } else if (ch == '7') {
-            vc_save_cursor(vc);
-            vc->state = TTY_STATE_NORM;
+            vt100_save_cursor(vt);
+            vt->state = TTY_STATE_NORM;
         } else if (ch == '8') {
-            vc_restore_cursor(vc);
-            vc->state = TTY_STATE_NORM;
+            vt100_restore_cursor(vt);
+            vt->state = TTY_STATE_NORM;
         } else {
-            vc->state = TTY_STATE_NORM;
+            vt->state = TTY_STATE_NORM;
         }
         break;
     case TTY_STATE_CSI: /* handle escape sequence parameters */
         if (ch >= '0' && ch <= '9') {
-            if (vc->nb_esc_params < MAX_ESC_PARAMS) {
-                int *param = &vc->esc_params[vc->nb_esc_params];
+            if (vt->nb_esc_params < MAX_ESC_PARAMS) {
+                int *param = &vt->esc_params[vt->nb_esc_params];
                 int digit = (ch - '0');
 
                 *param = (*param <= (INT_MAX - digit) / 10) ?
                          *param * 10 + digit : INT_MAX;
             }
         } else {
-            if (vc->nb_esc_params < MAX_ESC_PARAMS)
-                vc->nb_esc_params++;
+            if (vt->nb_esc_params < MAX_ESC_PARAMS)
+                vt->nb_esc_params++;
             if (ch == ';' || ch == '?') {
                 break;
             }
-            trace_console_putchar_csi(vc->esc_params[0], vc->esc_params[1],
-                                      ch, vc->nb_esc_params);
-            vc->state = TTY_STATE_NORM;
+            trace_console_putchar_csi(vt->esc_params[0], vt->esc_params[1],
+                                      ch, vt->nb_esc_params);
+            vt->state = TTY_STATE_NORM;
             switch(ch) {
             case 'A':
                 /* move cursor up */
-                if (vc->esc_params[0] == 0) {
-                    vc->esc_params[0] = 1;
+                if (vt->esc_params[0] == 0) {
+                    vt->esc_params[0] = 1;
                 }
-                vc_set_cursor(vc, s->vt.x, s->vt.y - vc->esc_params[0]);
+                vt100_set_cursor(vt, vt->x, vt->y - vt->esc_params[0]);
                 break;
             case 'B':
                 /* move cursor down */
-                if (vc->esc_params[0] == 0) {
-                    vc->esc_params[0] = 1;
+                if (vt->esc_params[0] == 0) {
+                    vt->esc_params[0] = 1;
                 }
-                vc_set_cursor(vc, s->vt.x, s->vt.y + vc->esc_params[0]);
+                vt100_set_cursor(vt, vt->x, vt->y + vt->esc_params[0]);
                 break;
             case 'C':
                 /* move cursor right */
-                if (vc->esc_params[0] == 0) {
-                    vc->esc_params[0] = 1;
+                if (vt->esc_params[0] == 0) {
+                    vt->esc_params[0] = 1;
                 }
-                vc_set_cursor(vc, s->vt.x + vc->esc_params[0], s->vt.y);
+                vt100_set_cursor(vt, vt->x + vt->esc_params[0], vt->y);
                 break;
             case 'D':
                 /* move cursor left */
-                if (vc->esc_params[0] == 0) {
-                    vc->esc_params[0] = 1;
+                if (vt->esc_params[0] == 0) {
+                    vt->esc_params[0] = 1;
                 }
-                vc_set_cursor(vc, s->vt.x - vc->esc_params[0], s->vt.y);
+                vt100_set_cursor(vt, vt->x - vt->esc_params[0], vt->y);
                 break;
             case 'G':
                 /* move cursor to column */
-                vc_set_cursor(vc, vc->esc_params[0] - 1, s->vt.y);
+                vt100_set_cursor(vt, vt->esc_params[0] - 1, vt->y);
                 break;
             case 'f':
             case 'H':
                 /* move cursor to row, column */
-                vc_set_cursor(vc, vc->esc_params[1] - 1, vc->esc_params[0] - 1);
+                vt100_set_cursor(vt, vt->esc_params[1] - 1, vt->esc_params[0] - 1);
                 break;
             case 'J':
-                switch (vc->esc_params[0]) {
+                switch (vt->esc_params[0]) {
                 case 0:
                     /* clear to end of screen */
-                    for (y = s->vt.y; y < s->vt.height; y++) {
-                        for (x = 0; x < s->vt.width; x++) {
-                            if (y == s->vt.y && x < s->vt.x) {
+                    for (y = vt->y; y < vt->height; y++) {
+                        for (x = 0; x < vt->width; x++) {
+                            if (y == vt->y && x < vt->x) {
                                 continue;
                             }
-                            vc_clear_xy(vc, x, y);
+                            vt100_clear_xy(vt, x, y);
                         }
                     }
                     break;
                 case 1:
                     /* clear from beginning of screen */
-                    for (y = 0; y <= s->vt.y; y++) {
-                        for (x = 0; x < s->vt.width; x++) {
-                            if (y == s->vt.y && x > s->vt.x) {
+                    for (y = 0; y <= vt->y; y++) {
+                        for (x = 0; x < vt->width; x++) {
+                            if (y == vt->y && x > vt->x) {
                                 break;
                             }
-                            vc_clear_xy(vc, x, y);
+                            vt100_clear_xy(vt, x, y);
                         }
                     }
                     break;
                 case 2:
                     /* clear entire screen */
-                    for (y = 0; y < s->vt.height; y++) {
-                        for (x = 0; x < s->vt.width; x++) {
-                            vc_clear_xy(vc, x, y);
+                    for (y = 0; y < vt->height; y++) {
+                        for (x = 0; x < vt->width; x++) {
+                            vt100_clear_xy(vt, x, y);
                         }
                     }
                     break;
                 }
                 break;
             case 'K':
-                switch (vc->esc_params[0]) {
+                switch (vt->esc_params[0]) {
                 case 0:
                     /* clear to eol */
-                    for(x = s->vt.x; x < s->vt.width; x++) {
-                        vc_clear_xy(vc, x, s->vt.y);
+                    for(x = vt->x; x < vt->width; x++) {
+                        vt100_clear_xy(vt, x, vt->y);
                     }
                     break;
                 case 1:
                     /* clear from beginning of line */
-                    for (x = 0; x <= s->vt.x && x < s->vt.width; x++) {
-                        vc_clear_xy(vc, x, s->vt.y);
+                    for (x = 0; x <= vt->x && x < vt->width; x++) {
+                        vt100_clear_xy(vt, x, vt->y);
                     }
                     break;
                 case 2:
                     /* clear entire line */
-                    for(x = 0; x < s->vt.width; x++) {
-                        vc_clear_xy(vc, x, s->vt.y);
+                    for(x = 0; x < vt->width; x++) {
+                        vt100_clear_xy(vt, x, vt->y);
                     }
                     break;
                 }
                 break;
             case 'P':
-                vc_csi_P(vc, vc->esc_params[0]);
+                vt100_csi_P(vt, vt->esc_params[0]);
                 break;
             case 'm':
-                vc_handle_escape(vc);
+                vt100_handle_escape(vt);
                 break;
             case 'n':
-                switch (vc->esc_params[0]) {
+                switch (vt->esc_params[0]) {
                 case 5:
                     /* report console status (always succeed)*/
-                    qemu_text_console_write(s, "\033[0n", 4);
+                    vt100_write(vt, "\033[0n", 4);
                     break;
                 case 6:
                     /* report cursor position */
                     response = g_strdup_printf("\033[%d;%dR",
-                                               s->vt.y + 1, s->vt.x + 1);
-                    qemu_text_console_write(s, response, strlen(response));
+                                               vt->y + 1, vt->x + 1);
+                    vt100_write(vt, response, strlen(response));
                     break;
                 }
                 break;
             case 's':
-                vc_save_cursor(vc);
+                vt100_save_cursor(vt);
                 break;
             case 'u':
-                vc_restore_cursor(vc);
+                vt100_restore_cursor(vt);
                 break;
             case '@':
-                vc_csi_at(vc, vc->esc_params[0]);
+                vt100_csi_at(vt, vt->esc_params[0]);
                 break;
             default:
                 trace_console_putchar_unhandled(ch);
@@ -1046,10 +1035,10 @@ static void vc_putchar(VCChardev *vc, int ch)
     case TTY_STATE_OSC: /* Operating System Command: ESC ] ... BEL/ST */
         if (ch == '\a') {
             /* BEL terminates OSC */
-            vc->state = TTY_STATE_NORM;
+            vt->state = TTY_STATE_NORM;
         } else if (ch == 27) {
             /* ESC might start ST (ESC \) */
-            vc->state = TTY_STATE_ESC;
+            vt->state = TTY_STATE_ESC;
         }
         /* All other bytes are silently consumed */
         break;
@@ -1060,7 +1049,7 @@ static void vc_putchar(VCChardev *vc, int ch)
             /* Latin-1 map */
             break;
         }
-        vc->state = TTY_STATE_NORM;
+        vt->state = TTY_STATE_NORM;
         break;
     }
 }
@@ -1081,7 +1070,7 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
     s->vt.update_y1 = 0;
     vt100_show_cursor(&s->vt, 0);
     for(i = 0; i < len; i++) {
-        vc_putchar(drv, buf[i]);
+        vt100_putchar(&s->vt, buf[i]);
     }
     vt100_show_cursor(&s->vt, 1);
     if (s->vt.update_x0 < s->vt.update_x1) {
@@ -1156,9 +1145,6 @@ qemu_text_console_init(Object *obj)
 {
     QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
 
-    QTAILQ_INSERT_HEAD(&vt100s, &c->vt, list);
-    fifo8_create(&c->out_fifo, 16);
-    c->vt.total_height = DEFAULT_BACKSCROLL;
     QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
     QEMU_CONSOLE(c)->hw = c;
 }
@@ -1182,7 +1168,7 @@ static void vc_chr_accept_input(Chardev *chr)
 {
     VCChardev *drv = VC_CHARDEV(chr);
 
-    qemu_text_console_flush(drv->console);
+    qemu_text_console_out_flush(drv->console);
 }
 
 static void vc_chr_set_echo(Chardev *chr, bool echo)
@@ -1204,6 +1190,13 @@ static void text_console_image_update(QemuVT100 *vt, int x, int y, int width, in
     dpy_gfx_update(QEMU_CONSOLE(console), x, y, width, height);
 }
 
+static void text_console_out_flush(QemuVT100 *vt)
+{
+    QemuTextConsole *console = container_of(vt, QemuTextConsole, vt);
+
+    qemu_text_console_out_flush(console);
+}
+
 static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
 {
     ChardevVC *vc = backend->u.vc.data;
@@ -1233,24 +1226,28 @@ static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
         s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
     }
 
+    QTAILQ_INSERT_HEAD(&vt100s, &s->vt, list);
+    fifo8_create(&s->vt.out_fifo, 16);
+    s->vt.total_height = DEFAULT_BACKSCROLL;
     dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
     s->vt.image_update = text_console_image_update;
+    s->vt.out_flush = text_console_out_flush;
 
     s->chr = chr;
     drv->console = s;
 
     /* set current text attributes to default */
-    drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+    s->vt.t_attrib = TEXT_ATTRIBUTES_DEFAULT;
     vt100_set_image(&s->vt, QEMU_CONSOLE(s)->surface->image);
 
     if (chr->label) {
         char *msg;
 
-        drv->t_attrib.bgcol = QEMU_COLOR_BLUE;
+        s->vt.t_attrib.bgcol = QEMU_COLOR_BLUE;
         msg = g_strdup_printf("%s console\r\n", chr->label);
         qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
         g_free(msg);
-        drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+        s->vt.t_attrib = TEXT_ATTRIBUTES_DEFAULT;
     }
 
     qemu_chr_be_event(chr, CHR_EVENT_OPENED);

-- 
2.53.0



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

* [PATCH 28/60] ui/console-vc: extract vt100_input() from vc_chr_write()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (26 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 27/60] ui/console-vc: move VT100 state machine and output FIFO into QemuVT100 Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 29/60] ui/console-vc: extract vt100_keysym() from qemu_text_console_handle_keysym() Marc-André Lureau
                   ` (32 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Move the VT100 input processing logic out of vc_chr_write() into a new
vt100_input() function that operates on QemuVT100 directly, rather than
going through the Chardev/VCChardev layers. This continues the effort
to decouple the VT100 emulation from the chardev backend, making the
VT100 layer self-contained and reusable.

vc_chr_write() becomes a thin wrapper that extracts the QemuVT100 from
the chardev and delegates to vt100_input().

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 87881c072ee..3ef492c2d4c 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -1058,29 +1058,35 @@ static void vt100_putchar(QemuVT100 *vt, int ch)
 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
                          TYPE_CHARDEV_VC)
 
-static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
+static int vt100_input(QemuVT100 *vt, const uint8_t *buf, int len)
 {
-    VCChardev *drv = VC_CHARDEV(chr);
-    QemuTextConsole *s = drv->console;
     int i;
 
-    s->vt.update_x0 = s->vt.width * FONT_WIDTH;
-    s->vt.update_y0 = s->vt.height * FONT_HEIGHT;
-    s->vt.update_x1 = 0;
-    s->vt.update_y1 = 0;
-    vt100_show_cursor(&s->vt, 0);
+    vt->update_x0 = vt->width * FONT_WIDTH;
+    vt->update_y0 = vt->height * FONT_HEIGHT;
+    vt->update_x1 = 0;
+    vt->update_y1 = 0;
+    vt100_show_cursor(vt, 0);
     for(i = 0; i < len; i++) {
-        vt100_putchar(&s->vt, buf[i]);
+        vt100_putchar(vt, buf[i]);
     }
-    vt100_show_cursor(&s->vt, 1);
-    if (s->vt.update_x0 < s->vt.update_x1) {
-        vt100_image_update(&s->vt, s->vt.update_x0, s->vt.update_y0,
-                           s->vt.update_x1 - s->vt.update_x0,
-                           s->vt.update_y1 - s->vt.update_y0);
+    vt100_show_cursor(vt, 1);
+    if (vt->update_x0 < vt->update_x1) {
+        vt100_image_update(vt, vt->update_x0, vt->update_y0,
+                           vt->update_x1 - vt->update_x0,
+                           vt->update_y1 - vt->update_y0);
     }
     return len;
 }
 
+static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    VCChardev *drv = VC_CHARDEV(chr);
+    QemuTextConsole *s = drv->console;
+
+    return vt100_input(&s->vt, buf, len);
+}
+
 void vt100_update_cursor(void)
 {
     QemuVT100 *vt;

-- 
2.53.0



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

* [PATCH 29/60] ui/console-vc: extract vt100_keysym() from qemu_text_console_handle_keysym()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (27 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 28/60] ui/console-vc: extract vt100_input() from vc_chr_write() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 30/60] ui/console-vc: extract vt100_init() and vt100_fini() Marc-André Lureau
                   ` (31 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Move the keysym handling logic out of qemu_text_console_handle_keysym()
into a new vt100_keysym() helper that operates on QemuVT100 directly,
continuing the effort to decouple the VT100 layer from the console layer.

The echo path is updated to call vt100_input() instead of
qemu_chr_write(), since the function no longer has direct access
to the chardev.

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index 3ef492c2d4c..dcd6445a1c3 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -326,24 +326,25 @@ static void vt100_write(QemuVT100 *vt, const void *buf, size_t len)
     vt->out_flush(vt);
 }
 
-/* called when an ascii key is pressed */
-void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
+static int vt100_input(QemuVT100 *vt, const uint8_t *buf, int len);
+
+static void vt100_keysym(QemuVT100 *vt, int keysym)
 {
     uint8_t buf[16], *q;
     int c;
 
     switch(keysym) {
     case QEMU_KEY_CTRL_UP:
-        vt100_scroll(&s->vt, -1);
+        vt100_scroll(vt, -1);
         break;
     case QEMU_KEY_CTRL_DOWN:
-        vt100_scroll(&s->vt, 1);
+        vt100_scroll(vt, 1);
         break;
     case QEMU_KEY_CTRL_PAGEUP:
-        vt100_scroll(&s->vt, -10);
+        vt100_scroll(vt, -10);
         break;
     case QEMU_KEY_CTRL_PAGEDOWN:
-        vt100_scroll(&s->vt, 10);
+        vt100_scroll(vt, 10);
         break;
     default:
         /* convert the QEMU keysym to VT100 key string */
@@ -360,18 +361,24 @@ void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
             *q++ = '\033';
             *q++ = '[';
             *q++ = keysym & 0xff;
-        } else if (s->vt.echo && (keysym == '\r' || keysym == '\n')) {
-            qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
+        } else if (vt->echo && (keysym == '\r' || keysym == '\n')) {
+            vt100_input(vt, (uint8_t *)"\r", 1);
             *q++ = '\n';
         } else {
             *q++ = keysym;
         }
-        if (s->vt.echo) {
-            qemu_chr_write(s->chr, buf, q - buf, true);
+        if (vt->echo) {
+            vt100_input(vt, buf, q - buf);
         }
-        vt100_write(&s->vt, buf, q - buf);
+        vt100_write(vt, buf, q - buf);
         break;
     }
+
+}
+/* called when an ascii key is pressed */
+void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
+{
+    vt100_keysym(&s->vt, keysym);
 }
 
 static void text_console_update(void *opaque, console_ch_t *chardata)

-- 
2.53.0



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

* [PATCH 30/60] ui/console-vc: extract vt100_init() and vt100_fini()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (28 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 29/60] ui/console-vc: extract vt100_keysym() from qemu_text_console_handle_keysym() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 31/60] ui/console: remove console_ch_t typedef and console_write_ch() Marc-André Lureau
                   ` (30 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Consolidate VT100 initialization and finalization into dedicated
functions, continuing the extraction of the VT100 layer from the
console/chardev code.

vt100_init() gathers the scattered setup (cursor timer, list insertion,
FIFO creation, default attributes, and image) that was previously spread
across vc_chr_open() and qemu_text_console_class_init().

vt100_fini() pairs with it by handling list removal, FIFO destruction,
and cells cleanup, replacing the open-coded QTAILQ_REMOVE in
qemu_text_console_finalize().

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

diff --git a/ui/console-vc.c b/ui/console-vc.c
index dcd6445a1c3..b30adac83ac 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -1128,12 +1128,19 @@ static void text_console_invalidate(void *opaque)
     vt100_refresh(&s->vt);
 }
 
+static void vt100_fini(QemuVT100 *vt)
+{
+    QTAILQ_REMOVE(&vt100s, vt, list);
+    fifo8_destroy(&vt->out_fifo);
+    g_free(vt->cells);
+}
+
 static void
 qemu_text_console_finalize(Object *obj)
 {
     QemuTextConsole *s = QEMU_TEXT_CONSOLE(obj);
 
-    QTAILQ_REMOVE(&vt100s, &s->vt, list);
+    vt100_fini(&s->vt);
 }
 
 static void
@@ -1141,10 +1148,6 @@ qemu_text_console_class_init(ObjectClass *oc, const void *data)
 {
     QemuConsoleClass *cc = QEMU_CONSOLE_CLASS(oc);
 
-    if (!cursor_timer) {
-        cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);
-    }
-
     cc->get_label = qemu_text_console_get_label;
 }
 
@@ -1210,6 +1213,25 @@ static void text_console_out_flush(QemuVT100 *vt)
     qemu_text_console_out_flush(console);
 }
 
+static void vt100_init(QemuVT100 *vt,
+                       pixman_image_t *image,
+                       void (*image_update)(QemuVT100 *vt, int x, int y, int w, int h),
+                       void (*out_flush)(QemuVT100 *vt))
+{
+    if (!cursor_timer) {
+        cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);
+    }
+
+    QTAILQ_INSERT_HEAD(&vt100s, vt, list);
+    fifo8_create(&vt->out_fifo, 16);
+    vt->total_height = DEFAULT_BACKSCROLL;
+    vt->image_update = image_update;
+    vt->out_flush = out_flush;
+    /* set current text attributes to default */
+    vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+    vt100_set_image(vt, image);
+}
+
 static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
 {
     ChardevVC *vc = backend->u.vc.data;
@@ -1239,20 +1261,14 @@ static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
         s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
     }
 
-    QTAILQ_INSERT_HEAD(&vt100s, &s->vt, list);
-    fifo8_create(&s->vt.out_fifo, 16);
-    s->vt.total_height = DEFAULT_BACKSCROLL;
     dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
-    s->vt.image_update = text_console_image_update;
-    s->vt.out_flush = text_console_out_flush;
+    vt100_init(&s->vt, QEMU_CONSOLE(s)->surface->image,
+               text_console_image_update,
+               text_console_out_flush);
 
     s->chr = chr;
     drv->console = s;
 
-    /* set current text attributes to default */
-    s->vt.t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-    vt100_set_image(&s->vt, QEMU_CONSOLE(s)->surface->image);
-
     if (chr->label) {
         char *msg;
 

-- 
2.53.0



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

* [PATCH 31/60] ui/console: remove console_ch_t typedef and console_write_ch()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (29 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 30/60] ui/console-vc: extract vt100_init() and vt100_fini() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 32/60] ui: avoid duplicating vgafont16 in each translation unit Marc-André Lureau
                   ` (29 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Since commit e2f82e924d05 ("console: purge curses bits from
console.h"), console_ch_t is a plain uint32_t typedef and
console_write_ch() is a trivial assignment (*dest = ch). These
abstractions were originally needed because console_ch_t was the
curses chtype when CONFIG_CURSES was enabled, and console_write_ch()
handled VGA-to-curses character translation. That commit moved the
curses logic into curses_update(), making the typedef and helper
dead abstractions.

Replace console_ch_t with uint32_t and console_write_ch() calls
with direct assignments.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h         | 11 ++---------
 hw/display/jazz_led.c        | 10 +++++-----
 hw/display/vga.c             | 16 ++++++++--------
 hw/display/virtio-gpu-base.c |  2 +-
 hw/display/virtio-vga.c      |  2 +-
 hw/display/vmware_vga.c      |  2 +-
 ui/console-vc.c              | 11 +++++------
 ui/console.c                 |  2 +-
 ui/curses.c                  |  6 +++---
 9 files changed, 27 insertions(+), 35 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 401e5a010fd..152333d60fc 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -350,13 +350,6 @@ int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx);
 
 bool console_has_gl(QemuConsole *con);
 
-typedef uint32_t console_ch_t;
-
-static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
-{
-    *dest = ch;
-}
-
 enum {
     GRAPHIC_FLAGS_NONE     = 0,
     /* require a console/display with GL callbacks */
@@ -370,7 +363,7 @@ typedef struct GraphicHwOps {
     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);
+    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;
@@ -386,7 +379,7 @@ void graphic_console_close(QemuConsole *con);
 void graphic_hw_update(QemuConsole *con);
 void graphic_hw_update_done(QemuConsole *con);
 void graphic_hw_invalidate(QemuConsole *con);
-void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
+void graphic_hw_text_update(QemuConsole *con, uint32_t *chardata);
 void graphic_hw_gl_block(QemuConsole *con, bool block);
 
 void qemu_console_early_init(void);
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
index 9d62e51bed9..d5783982950 100644
--- a/hw/display/jazz_led.c
+++ b/hw/display/jazz_led.c
@@ -226,7 +226,7 @@ static void jazz_led_invalidate_display(void *opaque)
     s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND;
 }
 
-static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
+static void jazz_led_text_update(void *opaque, uint32_t *chardata)
 {
     LedState *s = opaque;
     char buf[3];
@@ -236,10 +236,10 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
 
     /* TODO: draw the segments */
     snprintf(buf, 3, "%02hhx", s->segments);
-    console_write_ch(chardata++, ATTR2CHTYPE(buf[0], QEMU_COLOR_BLUE,
-                                             QEMU_COLOR_BLACK, 1));
-    console_write_ch(chardata++, ATTR2CHTYPE(buf[1], QEMU_COLOR_BLUE,
-                                             QEMU_COLOR_BLACK, 1));
+    *chardata++ = ATTR2CHTYPE(buf[0], QEMU_COLOR_BLUE,
+                              QEMU_COLOR_BLACK, 1);
+    *chardata++ = ATTR2CHTYPE(buf[1], QEMU_COLOR_BLUE,
+                              QEMU_COLOR_BLACK, 1);
 
     dpy_text_update(s->con, 0, 0, 2, 1);
 }
diff --git a/hw/display/vga.c b/hw/display/vga.c
index ee7d97b5c21..36cfc59a74e 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1899,13 +1899,13 @@ static void vga_reset(void *opaque)
         ((v & 0x00000800) << 10) | ((v & 0x00007000) >> 1))
 /* relay text rendering to the display driver
  * instead of doing a full vga_update_display() */
-static void vga_update_text(void *opaque, console_ch_t *chardata)
+static void vga_update_text(void *opaque, uint32_t *chardata)
 {
     VGACommonState *s =  opaque;
     int graphic_mode, i, cursor_offset, cursor_visible;
     int cw, cheight, width, height, size, c_min, c_max;
     uint32_t *src;
-    console_ch_t *dst, val;
+    uint32_t *dst, val;
     char msg_buffer[80];
     int full_update = 0;
 
@@ -2005,14 +2005,14 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
 
         if (full_update) {
             for (i = 0; i < size; src ++, dst ++, i ++)
-                console_write_ch(dst, VMEM2CHTYPE(le32_to_cpu(*src)));
+                *dst = VMEM2CHTYPE(le32_to_cpu(*src));
 
             dpy_text_update(s->con, 0, 0, width, height);
         } else {
             c_max = 0;
 
             for (i = 0; i < size; src ++, dst ++, i ++) {
-                console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src)));
+                val = VMEM2CHTYPE(le32_to_cpu(*src));
                 if (*dst != val) {
                     *dst = val;
                     c_max = i;
@@ -2021,7 +2021,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
             }
             c_min = i;
             for (; i < size; src ++, dst ++, i ++) {
-                console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src)));
+                val = VMEM2CHTYPE(le32_to_cpu(*src));
                 if (*dst != val) {
                     *dst = val;
                     c_max = i;
@@ -2059,14 +2059,14 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
     dpy_text_resize(s->con, s->last_width, height);
 
     for (dst = chardata, i = 0; i < s->last_width * height; i ++)
-        console_write_ch(dst ++, ' ');
+        *dst++ = ' ';
 
     size = strlen(msg_buffer);
     width = (s->last_width - size) / 2;
     dst = chardata + s->last_width + width;
     for (i = 0; i < size; i ++)
-        console_write_ch(dst ++, ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
-                                             QEMU_COLOR_BLACK, 1));
+        *dst++ = ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
+                             QEMU_COLOR_BLACK, 1);
 
     dpy_text_update(s->con, 0, 0, s->last_width, height);
 }
diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index cb76302e2d8..7b107509510 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -87,7 +87,7 @@ static void virtio_gpu_update_display(void *opaque)
 {
 }
 
-static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
+static void virtio_gpu_text_update(void *opaque, uint32_t *chardata)
 {
 }
 
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index 5e087169f2f..02fb36b31fc 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -31,7 +31,7 @@ static void virtio_vga_base_update_display(void *opaque)
     }
 }
 
-static void virtio_vga_base_text_update(void *opaque, console_ch_t *chardata)
+static void virtio_vga_base_text_update(void *opaque, uint32_t *chardata)
 {
     VirtIOVGABase *vvga = opaque;
     VirtIOGPUBase *g = vvga->vgpu;
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index c2c6bc76e90..1e154e7f99e 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -1183,7 +1183,7 @@ static void vmsvga_invalidate_display(void *opaque)
     s->invalidated = 1;
 }
 
-static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
+static void vmsvga_text_update(void *opaque, uint32_t *chardata)
 {
     struct vmsvga_state_s *s = opaque;
 
diff --git a/ui/console-vc.c b/ui/console-vc.c
index b30adac83ac..ba440c50744 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -381,7 +381,7 @@ void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
     vt100_keysym(&s->vt, keysym);
 }
 
-static void text_console_update(void *opaque, console_ch_t *chardata)
+static void text_console_update(void *opaque, uint32_t *chardata)
 {
     QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
     int i, j, src;
@@ -391,11 +391,10 @@ static void text_console_update(void *opaque, console_ch_t *chardata)
         chardata += s->vt.text_y[0] * s->vt.width;
         for (i = s->vt.text_y[0]; i <= s->vt.text_y[1]; i ++)
             for (j = 0; j < s->vt.width; j++, src++) {
-                console_write_ch(chardata ++,
-                                 ATTR2CHTYPE(s->vt.cells[src].ch,
-                                             s->vt.cells[src].t_attrib.fgcol,
-                                             s->vt.cells[src].t_attrib.bgcol,
-                                             s->vt.cells[src].t_attrib.bold));
+                *chardata++ = ATTR2CHTYPE(s->vt.cells[src].ch,
+                                          s->vt.cells[src].t_attrib.fgcol,
+                                          s->vt.cells[src].t_attrib.bgcol,
+                                          s->vt.cells[src].t_attrib.bold);
             }
         dpy_text_update(QEMU_CONSOLE(s), s->vt.text_x[0], s->vt.text_y[0],
                         s->vt.text_x[1] - s->vt.text_x[0], i - s->vt.text_y[0]);
diff --git a/ui/console.c b/ui/console.c
index 8af1dee0e22..b2b879e8533 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -214,7 +214,7 @@ void graphic_hw_invalidate(QemuConsole *con)
     }
 }
 
-void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
+void graphic_hw_text_update(QemuConsole *con, uint32_t *chardata)
 {
     if (con && con->hw_ops->text_update) {
         con->hw_ops->text_update(con->hw, chardata);
diff --git a/ui/curses.c b/ui/curses.c
index 161f78c35c3..78f21d940e3 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -57,7 +57,7 @@ enum maybe_keycode {
 };
 
 static DisplayChangeListener *dcl;
-static console_ch_t *screen;
+static uint32_t *screen;
 static WINDOW *screenpad = NULL;
 static int width, height, gwidth, gheight, invalidate;
 static int px, py, sminx, sminy, smaxx, smaxy;
@@ -68,7 +68,7 @@ static cchar_t *vga_to_curses;
 static void curses_update(DisplayChangeListener *dcl,
                           int x, int y, int w, int h)
 {
-    console_ch_t *line;
+    uint32_t *line;
     g_autofree cchar_t *curses_line = g_new(cchar_t, width);
     wchar_t wch[CCHARW_MAX];
     attr_t attrs;
@@ -796,7 +796,7 @@ static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
     if (opts->u.curses.charset) {
         font_charset = opts->u.curses.charset;
     }
-    screen = g_new0(console_ch_t, 160 * 100);
+    screen = g_new0(uint32_t, 160 * 100);
     vga_to_curses = g_new0(cchar_t, 256);
     curses_setup();
     curses_keyboard_setup();

-- 
2.53.0



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

* [PATCH 32/60] ui: avoid duplicating vgafont16 in each translation unit
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (30 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 31/60] ui/console: remove console_ch_t typedef and console_write_ch() Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:22   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 33/60] ui/vgafont: add SPDX license header Marc-André Lureau
                   ` (28 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

vgafont.h defined vgafont16 as a static const array, so every .c file
that included it (via console-priv.h) got its own 4 KiB copy, that
the linker may or not deduplicate?

Move the array definition into a new vgafont.c compilation unit and
turn the header into a proper extern declaration with an include guard.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vgafont.h   | 4613 +-------------------------------------------------------
 ui/vgafont.c   | 4613 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ui/meson.build |    1 +
 3 files changed, 4619 insertions(+), 4608 deletions(-)

diff --git a/ui/vgafont.h b/ui/vgafont.h
index 7e1fc473f75..4498ac4e07b 100644
--- a/ui/vgafont.h
+++ b/ui/vgafont.h
@@ -1,4611 +1,8 @@
-static const uint8_t vgafont16[256 * 16] = {
+#ifndef VGAFONT_H
+#define VGAFONT_H
 
-    /* 0 0x00 '^@' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
+#include <stdint.h>
 
-    /* 1 0x01 '^A' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x81, /* 10000001 */
-    0xa5, /* 10100101 */
-    0x81, /* 10000001 */
-    0x81, /* 10000001 */
-    0xbd, /* 10111101 */
-    0x99, /* 10011001 */
-    0x81, /* 10000001 */
-    0x81, /* 10000001 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
+extern const uint8_t vgafont16[256 * 16];
 
-    /* 2 0x02 '^B' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0xff, /* 11111111 */
-    0xdb, /* 11011011 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xc3, /* 11000011 */
-    0xe7, /* 11100111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 3 0x03 '^C' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x6c, /* 01101100 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0x7c, /* 01111100 */
-    0x38, /* 00111000 */
-    0x10, /* 00010000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 4 0x04 '^D' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x7c, /* 01111100 */
-    0xfe, /* 11111110 */
-    0x7c, /* 01111100 */
-    0x38, /* 00111000 */
-    0x10, /* 00010000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 5 0x05 '^E' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x3c, /* 00111100 */
-    0xe7, /* 11100111 */
-    0xe7, /* 11100111 */
-    0xe7, /* 11100111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 6 0x06 '^F' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x7e, /* 01111110 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 7 0x07 '^G' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 8 0x08 '^H' */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xe7, /* 11100111 */
-    0xc3, /* 11000011 */
-    0xc3, /* 11000011 */
-    0xe7, /* 11100111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-
-    /* 9 0x09 '^I' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0x42, /* 01000010 */
-    0x42, /* 01000010 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 10 0x0a '^J' */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xc3, /* 11000011 */
-    0x99, /* 10011001 */
-    0xbd, /* 10111101 */
-    0xbd, /* 10111101 */
-    0x99, /* 10011001 */
-    0xc3, /* 11000011 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-
-    /* 11 0x0b '^K' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x1e, /* 00011110 */
-    0x0e, /* 00001110 */
-    0x1a, /* 00011010 */
-    0x32, /* 00110010 */
-    0x78, /* 01111000 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x78, /* 01111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 12 0x0c '^L' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 13 0x0d '^M' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3f, /* 00111111 */
-    0x33, /* 00110011 */
-    0x3f, /* 00111111 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x70, /* 01110000 */
-    0xf0, /* 11110000 */
-    0xe0, /* 11100000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 14 0x0e '^N' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7f, /* 01111111 */
-    0x63, /* 01100011 */
-    0x7f, /* 01111111 */
-    0x63, /* 01100011 */
-    0x63, /* 01100011 */
-    0x63, /* 01100011 */
-    0x63, /* 01100011 */
-    0x67, /* 01100111 */
-    0xe7, /* 11100111 */
-    0xe6, /* 11100110 */
-    0xc0, /* 11000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 15 0x0f '^O' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xdb, /* 11011011 */
-    0x3c, /* 00111100 */
-    0xe7, /* 11100111 */
-    0x3c, /* 00111100 */
-    0xdb, /* 11011011 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 16 0x10 '^P' */
-    0x00, /* 00000000 */
-    0x80, /* 10000000 */
-    0xc0, /* 11000000 */
-    0xe0, /* 11100000 */
-    0xf0, /* 11110000 */
-    0xf8, /* 11111000 */
-    0xfe, /* 11111110 */
-    0xf8, /* 11111000 */
-    0xf0, /* 11110000 */
-    0xe0, /* 11100000 */
-    0xc0, /* 11000000 */
-    0x80, /* 10000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 17 0x11 '^Q' */
-    0x00, /* 00000000 */
-    0x02, /* 00000010 */
-    0x06, /* 00000110 */
-    0x0e, /* 00001110 */
-    0x1e, /* 00011110 */
-    0x3e, /* 00111110 */
-    0xfe, /* 11111110 */
-    0x3e, /* 00111110 */
-    0x1e, /* 00011110 */
-    0x0e, /* 00001110 */
-    0x06, /* 00000110 */
-    0x02, /* 00000010 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 18 0x12 '^R' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 19 0x13 '^S' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x00, /* 00000000 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 20 0x14 '^T' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7f, /* 01111111 */
-    0xdb, /* 11011011 */
-    0xdb, /* 11011011 */
-    0xdb, /* 11011011 */
-    0x7b, /* 01111011 */
-    0x1b, /* 00011011 */
-    0x1b, /* 00011011 */
-    0x1b, /* 00011011 */
-    0x1b, /* 00011011 */
-    0x1b, /* 00011011 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 21 0x15 '^U' */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0x60, /* 01100000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x0c, /* 00001100 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 22 0x16 '^V' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 23 0x17 '^W' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 24 0x18 '^X' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 25 0x19 '^Y' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 26 0x1a '^Z' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0xfe, /* 11111110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 27 0x1b '^[' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xfe, /* 11111110 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 28 0x1c '^\' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 29 0x1d '^]' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x28, /* 00101000 */
-    0x6c, /* 01101100 */
-    0xfe, /* 11111110 */
-    0x6c, /* 01101100 */
-    0x28, /* 00101000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 30 0x1e '^^' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x38, /* 00111000 */
-    0x7c, /* 01111100 */
-    0x7c, /* 01111100 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 31 0x1f '^_' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0x7c, /* 01111100 */
-    0x7c, /* 01111100 */
-    0x38, /* 00111000 */
-    0x38, /* 00111000 */
-    0x10, /* 00010000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 32 0x20 ' ' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 33 0x21 '!' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x3c, /* 00111100 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 34 0x22 '"' */
-    0x00, /* 00000000 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x24, /* 00100100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 35 0x23 '#' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0xfe, /* 11111110 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0xfe, /* 11111110 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 36 0x24 '$' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc2, /* 11000010 */
-    0xc0, /* 11000000 */
-    0x7c, /* 01111100 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x86, /* 10000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 37 0x25 '%' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc2, /* 11000010 */
-    0xc6, /* 11000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xc6, /* 11000110 */
-    0x86, /* 10000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 38 0x26 '&' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x76, /* 01110110 */
-    0xdc, /* 11011100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 39 0x27 ''' */
-    0x00, /* 00000000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 40 0x28 '(' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 41 0x29 ')' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 42 0x2a '*' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0xff, /* 11111111 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 43 0x2b '+' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 44 0x2c ',' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 45 0x2d '-' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 46 0x2e '.' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 47 0x2f '/' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x02, /* 00000010 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xc0, /* 11000000 */
-    0x80, /* 10000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 48 0x30 '0' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xd6, /* 11010110 */
-    0xd6, /* 11010110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 49 0x31 '1' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x38, /* 00111000 */
-    0x78, /* 01111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 50 0x32 '2' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 51 0x33 '3' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x3c, /* 00111100 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 52 0x34 '4' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x0c, /* 00001100 */
-    0x1c, /* 00011100 */
-    0x3c, /* 00111100 */
-    0x6c, /* 01101100 */
-    0xcc, /* 11001100 */
-    0xfe, /* 11111110 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x1e, /* 00011110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 53 0x35 '5' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xfc, /* 11111100 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 54 0x36 '6' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x60, /* 01100000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xfc, /* 11111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 55 0x37 '7' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 56 0x38 '8' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 57 0x39 '9' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7e, /* 01111110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0x78, /* 01111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 58 0x3a ':' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 59 0x3b ';' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 60 0x3c '<' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0x06, /* 00000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 61 0x3d '=' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 62 0x3e '>' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 63 0x3f '?' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 64 0x40 '@' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xde, /* 11011110 */
-    0xde, /* 11011110 */
-    0xde, /* 11011110 */
-    0xdc, /* 11011100 */
-    0xc0, /* 11000000 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 65 0x41 'A' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 66 0x42 'B' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfc, /* 11111100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x7c, /* 01111100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0xfc, /* 11111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 67 0x43 'C' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0xc2, /* 11000010 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc2, /* 11000010 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 68 0x44 'D' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xf8, /* 11111000 */
-    0x6c, /* 01101100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x6c, /* 01101100 */
-    0xf8, /* 11111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 69 0x45 'E' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x66, /* 01100110 */
-    0x62, /* 01100010 */
-    0x68, /* 01101000 */
-    0x78, /* 01111000 */
-    0x68, /* 01101000 */
-    0x60, /* 01100000 */
-    0x62, /* 01100010 */
-    0x66, /* 01100110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 70 0x46 'F' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x66, /* 01100110 */
-    0x62, /* 01100010 */
-    0x68, /* 01101000 */
-    0x78, /* 01111000 */
-    0x68, /* 01101000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0xf0, /* 11110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 71 0x47 'G' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0xc2, /* 11000010 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xde, /* 11011110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x66, /* 01100110 */
-    0x3a, /* 00111010 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 72 0x48 'H' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 73 0x49 'I' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 74 0x4a 'J' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x1e, /* 00011110 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x78, /* 01111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 75 0x4b 'K' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xe6, /* 11100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x6c, /* 01101100 */
-    0x78, /* 01111000 */
-    0x78, /* 01111000 */
-    0x6c, /* 01101100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0xe6, /* 11100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 76 0x4c 'L' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xf0, /* 11110000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x62, /* 01100010 */
-    0x66, /* 01100110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 77 0x4d 'M' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xee, /* 11101110 */
-    0xfe, /* 11111110 */
-    0xfe, /* 11111110 */
-    0xd6, /* 11010110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 78 0x4e 'N' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xe6, /* 11100110 */
-    0xf6, /* 11110110 */
-    0xfe, /* 11111110 */
-    0xde, /* 11011110 */
-    0xce, /* 11001110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 79 0x4f 'O' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 80 0x50 'P' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfc, /* 11111100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x7c, /* 01111100 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0xf0, /* 11110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 81 0x51 'Q' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xd6, /* 11010110 */
-    0xde, /* 11011110 */
-    0x7c, /* 01111100 */
-    0x0c, /* 00001100 */
-    0x0e, /* 00001110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 82 0x52 'R' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfc, /* 11111100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x7c, /* 01111100 */
-    0x6c, /* 01101100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0xe6, /* 11100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 83 0x53 'S' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x60, /* 01100000 */
-    0x38, /* 00111000 */
-    0x0c, /* 00001100 */
-    0x06, /* 00000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 84 0x54 'T' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x7e, /* 01111110 */
-    0x5a, /* 01011010 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 85 0x55 'U' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 86 0x56 'V' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x10, /* 00010000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 87 0x57 'W' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xd6, /* 11010110 */
-    0xd6, /* 11010110 */
-    0xd6, /* 11010110 */
-    0xfe, /* 11111110 */
-    0xee, /* 11101110 */
-    0x6c, /* 01101100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 88 0x58 'X' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x6c, /* 01101100 */
-    0x7c, /* 01111100 */
-    0x38, /* 00111000 */
-    0x38, /* 00111000 */
-    0x7c, /* 01111100 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 89 0x59 'Y' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 90 0x5a 'Z' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0x86, /* 10000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xc2, /* 11000010 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 91 0x5b '[' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 92 0x5c '\' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x80, /* 10000000 */
-    0xc0, /* 11000000 */
-    0xe0, /* 11100000 */
-    0x70, /* 01110000 */
-    0x38, /* 00111000 */
-    0x1c, /* 00011100 */
-    0x0e, /* 00001110 */
-    0x06, /* 00000110 */
-    0x02, /* 00000010 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 93 0x5d ']' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 94 0x5e '^' */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 95 0x5f '_' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 96 0x60 '`' */
-    0x00, /* 00000000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 97 0x61 'a' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x78, /* 01111000 */
-    0x0c, /* 00001100 */
-    0x7c, /* 01111100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 98 0x62 'b' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xe0, /* 11100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x78, /* 01111000 */
-    0x6c, /* 01101100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 99 0x63 'c' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 100 0x64 'd' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x1c, /* 00011100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x3c, /* 00111100 */
-    0x6c, /* 01101100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 101 0x65 'e' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 102 0x66 'f' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x1c, /* 00011100 */
-    0x36, /* 00110110 */
-    0x32, /* 00110010 */
-    0x30, /* 00110000 */
-    0x78, /* 01111000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x78, /* 01111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 103 0x67 'g' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x76, /* 01110110 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x7c, /* 01111100 */
-    0x0c, /* 00001100 */
-    0xcc, /* 11001100 */
-    0x78, /* 01111000 */
-    0x00, /* 00000000 */
-
-    /* 104 0x68 'h' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xe0, /* 11100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x6c, /* 01101100 */
-    0x76, /* 01110110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0xe6, /* 11100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 105 0x69 'i' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 106 0x6a 'j' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x00, /* 00000000 */
-    0x0e, /* 00001110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-
-    /* 107 0x6b 'k' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xe0, /* 11100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x66, /* 01100110 */
-    0x6c, /* 01101100 */
-    0x78, /* 01111000 */
-    0x78, /* 01111000 */
-    0x6c, /* 01101100 */
-    0x66, /* 01100110 */
-    0xe6, /* 11100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 108 0x6c 'l' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 109 0x6d 'm' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xec, /* 11101100 */
-    0xfe, /* 11111110 */
-    0xd6, /* 11010110 */
-    0xd6, /* 11010110 */
-    0xd6, /* 11010110 */
-    0xd6, /* 11010110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 110 0x6e 'n' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xdc, /* 11011100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 111 0x6f 'o' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 112 0x70 'p' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xdc, /* 11011100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x7c, /* 01111100 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0xf0, /* 11110000 */
-    0x00, /* 00000000 */
-
-    /* 113 0x71 'q' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x76, /* 01110110 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x7c, /* 01111100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x1e, /* 00011110 */
-    0x00, /* 00000000 */
-
-    /* 114 0x72 'r' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xdc, /* 11011100 */
-    0x76, /* 01110110 */
-    0x66, /* 01100110 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0xf0, /* 11110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 115 0x73 's' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0x60, /* 01100000 */
-    0x38, /* 00111000 */
-    0x0c, /* 00001100 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 116 0x74 't' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0xfc, /* 11111100 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x36, /* 00110110 */
-    0x1c, /* 00011100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 117 0x75 'u' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 118 0x76 'v' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 119 0x77 'w' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xd6, /* 11010110 */
-    0xd6, /* 11010110 */
-    0xd6, /* 11010110 */
-    0xfe, /* 11111110 */
-    0x6c, /* 01101100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 120 0x78 'x' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x38, /* 00111000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 121 0x79 'y' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7e, /* 01111110 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0xf8, /* 11111000 */
-    0x00, /* 00000000 */
-
-    /* 122 0x7a 'z' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xcc, /* 11001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 123 0x7b '{' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x0e, /* 00001110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x70, /* 01110000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x0e, /* 00001110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 124 0x7c '|' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 125 0x7d '}' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x70, /* 01110000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x0e, /* 00001110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x70, /* 01110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 126 0x7e '~' */
-    0x00, /* 00000000 */
-    0x76, /* 01110110 */
-    0xdc, /* 11011100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 127 0x7f '\x7f' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 128 0x80 '€' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0xc2, /* 11000010 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc2, /* 11000010 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x70, /* 01110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 129 0x81 '' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xcc, /* 11001100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 130 0x82 '‚' */
-    0x00, /* 00000000 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 131 0x83 'ƒ' */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x00, /* 00000000 */
-    0x78, /* 01111000 */
-    0x0c, /* 00001100 */
-    0x7c, /* 01111100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 132 0x84 '„' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xcc, /* 11001100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x78, /* 01111000 */
-    0x0c, /* 00001100 */
-    0x7c, /* 01111100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 133 0x85 '…' */
-    0x00, /* 00000000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x78, /* 01111000 */
-    0x0c, /* 00001100 */
-    0x7c, /* 01111100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 134 0x86 '†' */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x00, /* 00000000 */
-    0x78, /* 01111000 */
-    0x0c, /* 00001100 */
-    0x7c, /* 01111100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 135 0x87 '‡' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x18, /* 00011000 */
-    0x70, /* 01110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 136 0x88 'ˆ' */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 137 0x89 '‰' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 138 0x8a 'Š' */
-    0x00, /* 00000000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 139 0x8b '‹' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x66, /* 01100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 140 0x8c 'Œ' */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 141 0x8d '' */
-    0x00, /* 00000000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 142 0x8e 'Ž' */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 143 0x8f '' */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 144 0x90 '' */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x66, /* 01100110 */
-    0x62, /* 01100010 */
-    0x68, /* 01101000 */
-    0x78, /* 01111000 */
-    0x68, /* 01101000 */
-    0x62, /* 01100010 */
-    0x66, /* 01100110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 145 0x91 '‘' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xec, /* 11101100 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x7e, /* 01111110 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0x6e, /* 01101110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 146 0x92 '’' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3e, /* 00111110 */
-    0x6c, /* 01101100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xfe, /* 11111110 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xce, /* 11001110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 147 0x93 '“' */
-    0x00, /* 00000000 */
-    0x10, /* 00010000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 148 0x94 '”' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 149 0x95 '•' */
-    0x00, /* 00000000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 150 0x96 '–' */
-    0x00, /* 00000000 */
-    0x30, /* 00110000 */
-    0x78, /* 01111000 */
-    0xcc, /* 11001100 */
-    0x00, /* 00000000 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 151 0x97 '—' */
-    0x00, /* 00000000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 152 0x98 '˜' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7e, /* 01111110 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0x78, /* 01111000 */
-    0x00, /* 00000000 */
-
-    /* 153 0x99 '™' */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 154 0x9a 'š' */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 155 0x9b '›' */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 156 0x9c 'œ' */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x64, /* 01100100 */
-    0x60, /* 01100000 */
-    0xf0, /* 11110000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0xe6, /* 11100110 */
-    0xfc, /* 11111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 157 0x9d '' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 158 0x9e 'ž' */
-    0x00, /* 00000000 */
-    0xf8, /* 11111000 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xf8, /* 11111000 */
-    0xc4, /* 11000100 */
-    0xcc, /* 11001100 */
-    0xde, /* 11011110 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 159 0x9f 'Ÿ' */
-    0x00, /* 00000000 */
-    0x0e, /* 00001110 */
-    0x1b, /* 00011011 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xd8, /* 11011000 */
-    0x70, /* 01110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 160 0xa0 ' ' */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0x00, /* 00000000 */
-    0x78, /* 01111000 */
-    0x0c, /* 00001100 */
-    0x7c, /* 01111100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 161 0xa1 '¡' */
-    0x00, /* 00000000 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 162 0xa2 '¢' */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 163 0xa3 '£' */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0x00, /* 00000000 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 164 0xa4 '¤' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x76, /* 01110110 */
-    0xdc, /* 11011100 */
-    0x00, /* 00000000 */
-    0xdc, /* 11011100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 165 0xa5 '¥' */
-    0x76, /* 01110110 */
-    0xdc, /* 11011100 */
-    0x00, /* 00000000 */
-    0xc6, /* 11000110 */
-    0xe6, /* 11100110 */
-    0xf6, /* 11110110 */
-    0xfe, /* 11111110 */
-    0xde, /* 11011110 */
-    0xce, /* 11001110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 166 0xa6 '¦' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x3e, /* 00111110 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 167 0xa7 '§' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 168 0xa8 '¨' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x30, /* 00110000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xc0, /* 11000000 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x7c, /* 01111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 169 0xa9 '©' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 170 0xaa 'ª' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 171 0xab '«' */
-    0x00, /* 00000000 */
-    0x60, /* 01100000 */
-    0xe0, /* 11100000 */
-    0x62, /* 01100010 */
-    0x66, /* 01100110 */
-    0x6c, /* 01101100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xdc, /* 11011100 */
-    0x86, /* 10000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x3e, /* 00111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 172 0xac '¬' */
-    0x00, /* 00000000 */
-    0x60, /* 01100000 */
-    0xe0, /* 11100000 */
-    0x62, /* 01100010 */
-    0x66, /* 01100110 */
-    0x6c, /* 01101100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x66, /* 01100110 */
-    0xce, /* 11001110 */
-    0x9a, /* 10011010 */
-    0x3f, /* 00111111 */
-    0x06, /* 00000110 */
-    0x06, /* 00000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 173 0xad '­' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x3c, /* 00111100 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 174 0xae '®' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x36, /* 00110110 */
-    0x6c, /* 01101100 */
-    0xd8, /* 11011000 */
-    0x6c, /* 01101100 */
-    0x36, /* 00110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 175 0xaf '¯' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xd8, /* 11011000 */
-    0x6c, /* 01101100 */
-    0x36, /* 00110110 */
-    0x6c, /* 01101100 */
-    0xd8, /* 11011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 176 0xb0 '°' */
-    0x11, /* 00010001 */
-    0x44, /* 01000100 */
-    0x11, /* 00010001 */
-    0x44, /* 01000100 */
-    0x11, /* 00010001 */
-    0x44, /* 01000100 */
-    0x11, /* 00010001 */
-    0x44, /* 01000100 */
-    0x11, /* 00010001 */
-    0x44, /* 01000100 */
-    0x11, /* 00010001 */
-    0x44, /* 01000100 */
-    0x11, /* 00010001 */
-    0x44, /* 01000100 */
-    0x11, /* 00010001 */
-    0x44, /* 01000100 */
-
-    /* 177 0xb1 '±' */
-    0x55, /* 01010101 */
-    0xaa, /* 10101010 */
-    0x55, /* 01010101 */
-    0xaa, /* 10101010 */
-    0x55, /* 01010101 */
-    0xaa, /* 10101010 */
-    0x55, /* 01010101 */
-    0xaa, /* 10101010 */
-    0x55, /* 01010101 */
-    0xaa, /* 10101010 */
-    0x55, /* 01010101 */
-    0xaa, /* 10101010 */
-    0x55, /* 01010101 */
-    0xaa, /* 10101010 */
-    0x55, /* 01010101 */
-    0xaa, /* 10101010 */
-
-    /* 178 0xb2 '²' */
-    0xdd, /* 11011101 */
-    0x77, /* 01110111 */
-    0xdd, /* 11011101 */
-    0x77, /* 01110111 */
-    0xdd, /* 11011101 */
-    0x77, /* 01110111 */
-    0xdd, /* 11011101 */
-    0x77, /* 01110111 */
-    0xdd, /* 11011101 */
-    0x77, /* 01110111 */
-    0xdd, /* 11011101 */
-    0x77, /* 01110111 */
-    0xdd, /* 11011101 */
-    0x77, /* 01110111 */
-    0xdd, /* 11011101 */
-    0x77, /* 01110111 */
-
-    /* 179 0xb3 '³' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 180 0xb4 '´' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xf8, /* 11111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 181 0xb5 'µ' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xf8, /* 11111000 */
-    0x18, /* 00011000 */
-    0xf8, /* 11111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 182 0xb6 '¶' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0xf6, /* 11110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 183 0xb7 '·' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 184 0xb8 '¸' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xf8, /* 11111000 */
-    0x18, /* 00011000 */
-    0xf8, /* 11111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 185 0xb9 '¹' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0xf6, /* 11110110 */
-    0x06, /* 00000110 */
-    0xf6, /* 11110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 186 0xba 'º' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 187 0xbb '»' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x06, /* 00000110 */
-    0xf6, /* 11110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 188 0xbc '¼' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0xf6, /* 11110110 */
-    0x06, /* 00000110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 189 0xbd '½' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 190 0xbe '¾' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xf8, /* 11111000 */
-    0x18, /* 00011000 */
-    0xf8, /* 11111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 191 0xbf '¿' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xf8, /* 11111000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 192 0xc0 'À' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x1f, /* 00011111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 193 0xc1 'Á' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 194 0xc2 'Â' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 195 0xc3 'Ã' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x1f, /* 00011111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 196 0xc4 'Ä' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 197 0xc5 'Å' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xff, /* 11111111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 198 0xc6 'Æ' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x1f, /* 00011111 */
-    0x18, /* 00011000 */
-    0x1f, /* 00011111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 199 0xc7 'Ç' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x37, /* 00110111 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 200 0xc8 'È' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x37, /* 00110111 */
-    0x30, /* 00110000 */
-    0x3f, /* 00111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 201 0xc9 'É' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3f, /* 00111111 */
-    0x30, /* 00110000 */
-    0x37, /* 00110111 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 202 0xca 'Ê' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0xf7, /* 11110111 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 203 0xcb 'Ë' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0xf7, /* 11110111 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 204 0xcc 'Ì' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x37, /* 00110111 */
-    0x30, /* 00110000 */
-    0x37, /* 00110111 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 205 0xcd 'Í' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 206 0xce 'Î' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0xf7, /* 11110111 */
-    0x00, /* 00000000 */
-    0xf7, /* 11110111 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 207 0xcf 'Ï' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 208 0xd0 'Ð' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 209 0xd1 'Ñ' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 210 0xd2 'Ò' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 211 0xd3 'Ó' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x3f, /* 00111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 212 0xd4 'Ô' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x1f, /* 00011111 */
-    0x18, /* 00011000 */
-    0x1f, /* 00011111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 213 0xd5 'Õ' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x1f, /* 00011111 */
-    0x18, /* 00011000 */
-    0x1f, /* 00011111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 214 0xd6 'Ö' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x3f, /* 00111111 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 215 0xd7 '×' */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0xff, /* 11111111 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-
-    /* 216 0xd8 'Ø' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xff, /* 11111111 */
-    0x18, /* 00011000 */
-    0xff, /* 11111111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 217 0xd9 'Ù' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xf8, /* 11111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 218 0xda 'Ú' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x1f, /* 00011111 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 219 0xdb 'Û' */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-
-    /* 220 0xdc 'Ü' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-
-    /* 221 0xdd 'Ý' */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-    0xf0, /* 11110000 */
-
-    /* 222 0xde 'Þ' */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-    0x0f, /* 00001111 */
-
-    /* 223 0xdf 'ß' */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0xff, /* 11111111 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 224 0xe0 'à' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x76, /* 01110110 */
-    0xdc, /* 11011100 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0xdc, /* 11011100 */
-    0x76, /* 01110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 225 0xe1 'á' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x78, /* 01111000 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xcc, /* 11001100 */
-    0xd8, /* 11011000 */
-    0xcc, /* 11001100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xcc, /* 11001100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 226 0xe2 'â' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0xc0, /* 11000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 227 0xe3 'ã' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 228 0xe4 'ä' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 229 0xe5 'å' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0x70, /* 01110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 230 0xe6 'æ' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x7c, /* 01111100 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0xc0, /* 11000000 */
-    0x00, /* 00000000 */
-
-    /* 231 0xe7 'ç' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x76, /* 01110110 */
-    0xdc, /* 11011100 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 232 0xe8 'è' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 233 0xe9 'é' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xfe, /* 11111110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 234 0xea 'ê' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0xee, /* 11101110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 235 0xeb 'ë' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x1e, /* 00011110 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0x3e, /* 00111110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x66, /* 01100110 */
-    0x3c, /* 00111100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 236 0xec 'ì' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0xdb, /* 11011011 */
-    0xdb, /* 11011011 */
-    0xdb, /* 11011011 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 237 0xed 'í' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x03, /* 00000011 */
-    0x06, /* 00000110 */
-    0x7e, /* 01111110 */
-    0xdb, /* 11011011 */
-    0xdb, /* 11011011 */
-    0xf3, /* 11110011 */
-    0x7e, /* 01111110 */
-    0x60, /* 01100000 */
-    0xc0, /* 11000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 238 0xee 'î' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x1c, /* 00011100 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x7c, /* 01111100 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x1c, /* 00011100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 239 0xef 'ï' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7c, /* 01111100 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0xc6, /* 11000110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 240 0xf0 'ð' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0xfe, /* 11111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 241 0xf1 'ñ' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x7e, /* 01111110 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 242 0xf2 'ò' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0x06, /* 00000110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 243 0xf3 'ó' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x30, /* 00110000 */
-    0x60, /* 01100000 */
-    0x30, /* 00110000 */
-    0x18, /* 00011000 */
-    0x0c, /* 00001100 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 244 0xf4 'ô' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x0e, /* 00001110 */
-    0x1b, /* 00011011 */
-    0x1b, /* 00011011 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-
-    /* 245 0xf5 'õ' */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0xd8, /* 11011000 */
-    0x70, /* 01110000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 246 0xf6 'ö' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 247 0xf7 '÷' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x76, /* 01110110 */
-    0xdc, /* 11011100 */
-    0x00, /* 00000000 */
-    0x76, /* 01110110 */
-    0xdc, /* 11011100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 248 0xf8 'ø' */
-    0x00, /* 00000000 */
-    0x38, /* 00111000 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x38, /* 00111000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 249 0xf9 'ù' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 250 0xfa 'ú' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x18, /* 00011000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 251 0xfb 'û' */
-    0x00, /* 00000000 */
-    0x0f, /* 00001111 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0x0c, /* 00001100 */
-    0xec, /* 11101100 */
-    0x6c, /* 01101100 */
-    0x6c, /* 01101100 */
-    0x3c, /* 00111100 */
-    0x1c, /* 00011100 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 252 0xfc 'ü' */
-    0x00, /* 00000000 */
-    0x6c, /* 01101100 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x36, /* 00110110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 253 0xfd 'ý' */
-    0x00, /* 00000000 */
-    0x3c, /* 00111100 */
-    0x66, /* 01100110 */
-    0x0c, /* 00001100 */
-    0x18, /* 00011000 */
-    0x32, /* 00110010 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 254 0xfe 'þ' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x7e, /* 01111110 */
-    0x7e, /* 01111110 */
-    0x7e, /* 01111110 */
-    0x7e, /* 01111110 */
-    0x7e, /* 01111110 */
-    0x7e, /* 01111110 */
-    0x7e, /* 01111110 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-    /* 255 0xff 'ÿ' */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-    0x00, /* 00000000 */
-
-};
+#endif
diff --git a/ui/vgafont.c b/ui/vgafont.c
new file mode 100644
index 00000000000..708c845a6bf
--- /dev/null
+++ b/ui/vgafont.c
@@ -0,0 +1,4613 @@
+#include "vgafont.h"
+
+const uint8_t vgafont16[256 * 16] = {
+
+    /* 0 0x00 '^@' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 1 0x01 '^A' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x81, /* 10000001 */
+    0xa5, /* 10100101 */
+    0x81, /* 10000001 */
+    0x81, /* 10000001 */
+    0xbd, /* 10111101 */
+    0x99, /* 10011001 */
+    0x81, /* 10000001 */
+    0x81, /* 10000001 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 2 0x02 '^B' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0xff, /* 11111111 */
+    0xdb, /* 11011011 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xc3, /* 11000011 */
+    0xe7, /* 11100111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 3 0x03 '^C' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x6c, /* 01101100 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0x7c, /* 01111100 */
+    0x38, /* 00111000 */
+    0x10, /* 00010000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 4 0x04 '^D' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x7c, /* 01111100 */
+    0xfe, /* 11111110 */
+    0x7c, /* 01111100 */
+    0x38, /* 00111000 */
+    0x10, /* 00010000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 5 0x05 '^E' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x3c, /* 00111100 */
+    0xe7, /* 11100111 */
+    0xe7, /* 11100111 */
+    0xe7, /* 11100111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 6 0x06 '^F' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x7e, /* 01111110 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 7 0x07 '^G' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 8 0x08 '^H' */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xe7, /* 11100111 */
+    0xc3, /* 11000011 */
+    0xc3, /* 11000011 */
+    0xe7, /* 11100111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+
+    /* 9 0x09 '^I' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0x42, /* 01000010 */
+    0x42, /* 01000010 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 10 0x0a '^J' */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xc3, /* 11000011 */
+    0x99, /* 10011001 */
+    0xbd, /* 10111101 */
+    0xbd, /* 10111101 */
+    0x99, /* 10011001 */
+    0xc3, /* 11000011 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+
+    /* 11 0x0b '^K' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x1e, /* 00011110 */
+    0x0e, /* 00001110 */
+    0x1a, /* 00011010 */
+    0x32, /* 00110010 */
+    0x78, /* 01111000 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x78, /* 01111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 12 0x0c '^L' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 13 0x0d '^M' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3f, /* 00111111 */
+    0x33, /* 00110011 */
+    0x3f, /* 00111111 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x70, /* 01110000 */
+    0xf0, /* 11110000 */
+    0xe0, /* 11100000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 14 0x0e '^N' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7f, /* 01111111 */
+    0x63, /* 01100011 */
+    0x7f, /* 01111111 */
+    0x63, /* 01100011 */
+    0x63, /* 01100011 */
+    0x63, /* 01100011 */
+    0x63, /* 01100011 */
+    0x67, /* 01100111 */
+    0xe7, /* 11100111 */
+    0xe6, /* 11100110 */
+    0xc0, /* 11000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 15 0x0f '^O' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xdb, /* 11011011 */
+    0x3c, /* 00111100 */
+    0xe7, /* 11100111 */
+    0x3c, /* 00111100 */
+    0xdb, /* 11011011 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 16 0x10 '^P' */
+    0x00, /* 00000000 */
+    0x80, /* 10000000 */
+    0xc0, /* 11000000 */
+    0xe0, /* 11100000 */
+    0xf0, /* 11110000 */
+    0xf8, /* 11111000 */
+    0xfe, /* 11111110 */
+    0xf8, /* 11111000 */
+    0xf0, /* 11110000 */
+    0xe0, /* 11100000 */
+    0xc0, /* 11000000 */
+    0x80, /* 10000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 17 0x11 '^Q' */
+    0x00, /* 00000000 */
+    0x02, /* 00000010 */
+    0x06, /* 00000110 */
+    0x0e, /* 00001110 */
+    0x1e, /* 00011110 */
+    0x3e, /* 00111110 */
+    0xfe, /* 11111110 */
+    0x3e, /* 00111110 */
+    0x1e, /* 00011110 */
+    0x0e, /* 00001110 */
+    0x06, /* 00000110 */
+    0x02, /* 00000010 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 18 0x12 '^R' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 19 0x13 '^S' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x00, /* 00000000 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 20 0x14 '^T' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7f, /* 01111111 */
+    0xdb, /* 11011011 */
+    0xdb, /* 11011011 */
+    0xdb, /* 11011011 */
+    0x7b, /* 01111011 */
+    0x1b, /* 00011011 */
+    0x1b, /* 00011011 */
+    0x1b, /* 00011011 */
+    0x1b, /* 00011011 */
+    0x1b, /* 00011011 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 21 0x15 '^U' */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0x60, /* 01100000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x0c, /* 00001100 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 22 0x16 '^V' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 23 0x17 '^W' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 24 0x18 '^X' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 25 0x19 '^Y' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 26 0x1a '^Z' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0xfe, /* 11111110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 27 0x1b '^[' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xfe, /* 11111110 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 28 0x1c '^\' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 29 0x1d '^]' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x28, /* 00101000 */
+    0x6c, /* 01101100 */
+    0xfe, /* 11111110 */
+    0x6c, /* 01101100 */
+    0x28, /* 00101000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 30 0x1e '^^' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x38, /* 00111000 */
+    0x7c, /* 01111100 */
+    0x7c, /* 01111100 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 31 0x1f '^_' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0x7c, /* 01111100 */
+    0x7c, /* 01111100 */
+    0x38, /* 00111000 */
+    0x38, /* 00111000 */
+    0x10, /* 00010000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 32 0x20 ' ' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 33 0x21 '!' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x3c, /* 00111100 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 34 0x22 '"' */
+    0x00, /* 00000000 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x24, /* 00100100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 35 0x23 '#' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0xfe, /* 11111110 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0xfe, /* 11111110 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 36 0x24 '$' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc2, /* 11000010 */
+    0xc0, /* 11000000 */
+    0x7c, /* 01111100 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x86, /* 10000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 37 0x25 '%' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc2, /* 11000010 */
+    0xc6, /* 11000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xc6, /* 11000110 */
+    0x86, /* 10000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 38 0x26 '&' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x76, /* 01110110 */
+    0xdc, /* 11011100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 39 0x27 ''' */
+    0x00, /* 00000000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 40 0x28 '(' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 41 0x29 ')' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 42 0x2a '*' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0xff, /* 11111111 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 43 0x2b '+' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 44 0x2c ',' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 45 0x2d '-' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 46 0x2e '.' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 47 0x2f '/' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x02, /* 00000010 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xc0, /* 11000000 */
+    0x80, /* 10000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 48 0x30 '0' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xd6, /* 11010110 */
+    0xd6, /* 11010110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 49 0x31 '1' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x38, /* 00111000 */
+    0x78, /* 01111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 50 0x32 '2' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 51 0x33 '3' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x3c, /* 00111100 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 52 0x34 '4' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x0c, /* 00001100 */
+    0x1c, /* 00011100 */
+    0x3c, /* 00111100 */
+    0x6c, /* 01101100 */
+    0xcc, /* 11001100 */
+    0xfe, /* 11111110 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x1e, /* 00011110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 53 0x35 '5' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xfc, /* 11111100 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 54 0x36 '6' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x60, /* 01100000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xfc, /* 11111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 55 0x37 '7' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 56 0x38 '8' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 57 0x39 '9' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7e, /* 01111110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0x78, /* 01111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 58 0x3a ':' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 59 0x3b ';' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 60 0x3c '<' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0x06, /* 00000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 61 0x3d '=' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 62 0x3e '>' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 63 0x3f '?' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 64 0x40 '@' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xde, /* 11011110 */
+    0xde, /* 11011110 */
+    0xde, /* 11011110 */
+    0xdc, /* 11011100 */
+    0xc0, /* 11000000 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 65 0x41 'A' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 66 0x42 'B' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfc, /* 11111100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x7c, /* 01111100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0xfc, /* 11111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 67 0x43 'C' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0xc2, /* 11000010 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc2, /* 11000010 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 68 0x44 'D' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xf8, /* 11111000 */
+    0x6c, /* 01101100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x6c, /* 01101100 */
+    0xf8, /* 11111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 69 0x45 'E' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x66, /* 01100110 */
+    0x62, /* 01100010 */
+    0x68, /* 01101000 */
+    0x78, /* 01111000 */
+    0x68, /* 01101000 */
+    0x60, /* 01100000 */
+    0x62, /* 01100010 */
+    0x66, /* 01100110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 70 0x46 'F' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x66, /* 01100110 */
+    0x62, /* 01100010 */
+    0x68, /* 01101000 */
+    0x78, /* 01111000 */
+    0x68, /* 01101000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0xf0, /* 11110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 71 0x47 'G' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0xc2, /* 11000010 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xde, /* 11011110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x66, /* 01100110 */
+    0x3a, /* 00111010 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 72 0x48 'H' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 73 0x49 'I' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 74 0x4a 'J' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x1e, /* 00011110 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x78, /* 01111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 75 0x4b 'K' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xe6, /* 11100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x6c, /* 01101100 */
+    0x78, /* 01111000 */
+    0x78, /* 01111000 */
+    0x6c, /* 01101100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0xe6, /* 11100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 76 0x4c 'L' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xf0, /* 11110000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x62, /* 01100010 */
+    0x66, /* 01100110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 77 0x4d 'M' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xee, /* 11101110 */
+    0xfe, /* 11111110 */
+    0xfe, /* 11111110 */
+    0xd6, /* 11010110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 78 0x4e 'N' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xe6, /* 11100110 */
+    0xf6, /* 11110110 */
+    0xfe, /* 11111110 */
+    0xde, /* 11011110 */
+    0xce, /* 11001110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 79 0x4f 'O' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 80 0x50 'P' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfc, /* 11111100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x7c, /* 01111100 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0xf0, /* 11110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 81 0x51 'Q' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xd6, /* 11010110 */
+    0xde, /* 11011110 */
+    0x7c, /* 01111100 */
+    0x0c, /* 00001100 */
+    0x0e, /* 00001110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 82 0x52 'R' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfc, /* 11111100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x7c, /* 01111100 */
+    0x6c, /* 01101100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0xe6, /* 11100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 83 0x53 'S' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x60, /* 01100000 */
+    0x38, /* 00111000 */
+    0x0c, /* 00001100 */
+    0x06, /* 00000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 84 0x54 'T' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x7e, /* 01111110 */
+    0x5a, /* 01011010 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 85 0x55 'U' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 86 0x56 'V' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x10, /* 00010000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 87 0x57 'W' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xd6, /* 11010110 */
+    0xd6, /* 11010110 */
+    0xd6, /* 11010110 */
+    0xfe, /* 11111110 */
+    0xee, /* 11101110 */
+    0x6c, /* 01101100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 88 0x58 'X' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x6c, /* 01101100 */
+    0x7c, /* 01111100 */
+    0x38, /* 00111000 */
+    0x38, /* 00111000 */
+    0x7c, /* 01111100 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 89 0x59 'Y' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 90 0x5a 'Z' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0x86, /* 10000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xc2, /* 11000010 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 91 0x5b '[' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 92 0x5c '\' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x80, /* 10000000 */
+    0xc0, /* 11000000 */
+    0xe0, /* 11100000 */
+    0x70, /* 01110000 */
+    0x38, /* 00111000 */
+    0x1c, /* 00011100 */
+    0x0e, /* 00001110 */
+    0x06, /* 00000110 */
+    0x02, /* 00000010 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 93 0x5d ']' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 94 0x5e '^' */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 95 0x5f '_' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 96 0x60 '`' */
+    0x00, /* 00000000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 97 0x61 'a' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x78, /* 01111000 */
+    0x0c, /* 00001100 */
+    0x7c, /* 01111100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 98 0x62 'b' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xe0, /* 11100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x78, /* 01111000 */
+    0x6c, /* 01101100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 99 0x63 'c' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 100 0x64 'd' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x1c, /* 00011100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x3c, /* 00111100 */
+    0x6c, /* 01101100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 101 0x65 'e' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 102 0x66 'f' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x1c, /* 00011100 */
+    0x36, /* 00110110 */
+    0x32, /* 00110010 */
+    0x30, /* 00110000 */
+    0x78, /* 01111000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x78, /* 01111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 103 0x67 'g' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x76, /* 01110110 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x7c, /* 01111100 */
+    0x0c, /* 00001100 */
+    0xcc, /* 11001100 */
+    0x78, /* 01111000 */
+    0x00, /* 00000000 */
+
+    /* 104 0x68 'h' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xe0, /* 11100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x6c, /* 01101100 */
+    0x76, /* 01110110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0xe6, /* 11100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 105 0x69 'i' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 106 0x6a 'j' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x00, /* 00000000 */
+    0x0e, /* 00001110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+
+    /* 107 0x6b 'k' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xe0, /* 11100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x66, /* 01100110 */
+    0x6c, /* 01101100 */
+    0x78, /* 01111000 */
+    0x78, /* 01111000 */
+    0x6c, /* 01101100 */
+    0x66, /* 01100110 */
+    0xe6, /* 11100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 108 0x6c 'l' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 109 0x6d 'm' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xec, /* 11101100 */
+    0xfe, /* 11111110 */
+    0xd6, /* 11010110 */
+    0xd6, /* 11010110 */
+    0xd6, /* 11010110 */
+    0xd6, /* 11010110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 110 0x6e 'n' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xdc, /* 11011100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 111 0x6f 'o' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 112 0x70 'p' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xdc, /* 11011100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x7c, /* 01111100 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0xf0, /* 11110000 */
+    0x00, /* 00000000 */
+
+    /* 113 0x71 'q' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x76, /* 01110110 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x7c, /* 01111100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x1e, /* 00011110 */
+    0x00, /* 00000000 */
+
+    /* 114 0x72 'r' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xdc, /* 11011100 */
+    0x76, /* 01110110 */
+    0x66, /* 01100110 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0xf0, /* 11110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 115 0x73 's' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0x60, /* 01100000 */
+    0x38, /* 00111000 */
+    0x0c, /* 00001100 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 116 0x74 't' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0xfc, /* 11111100 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x36, /* 00110110 */
+    0x1c, /* 00011100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 117 0x75 'u' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 118 0x76 'v' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 119 0x77 'w' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xd6, /* 11010110 */
+    0xd6, /* 11010110 */
+    0xd6, /* 11010110 */
+    0xfe, /* 11111110 */
+    0x6c, /* 01101100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 120 0x78 'x' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x38, /* 00111000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 121 0x79 'y' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7e, /* 01111110 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0xf8, /* 11111000 */
+    0x00, /* 00000000 */
+
+    /* 122 0x7a 'z' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xcc, /* 11001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 123 0x7b '{' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x0e, /* 00001110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x70, /* 01110000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x0e, /* 00001110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 124 0x7c '|' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 125 0x7d '}' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x70, /* 01110000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x0e, /* 00001110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x70, /* 01110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 126 0x7e '~' */
+    0x00, /* 00000000 */
+    0x76, /* 01110110 */
+    0xdc, /* 11011100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 127 0x7f '\x7f' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 128 0x80 '€' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0xc2, /* 11000010 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc2, /* 11000010 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x70, /* 01110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 129 0x81 '' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xcc, /* 11001100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 130 0x82 '‚' */
+    0x00, /* 00000000 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 131 0x83 'ƒ' */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x00, /* 00000000 */
+    0x78, /* 01111000 */
+    0x0c, /* 00001100 */
+    0x7c, /* 01111100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 132 0x84 '„' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xcc, /* 11001100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x78, /* 01111000 */
+    0x0c, /* 00001100 */
+    0x7c, /* 01111100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 133 0x85 '…' */
+    0x00, /* 00000000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x78, /* 01111000 */
+    0x0c, /* 00001100 */
+    0x7c, /* 01111100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 134 0x86 '†' */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x00, /* 00000000 */
+    0x78, /* 01111000 */
+    0x0c, /* 00001100 */
+    0x7c, /* 01111100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 135 0x87 '‡' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x18, /* 00011000 */
+    0x70, /* 01110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 136 0x88 'ˆ' */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 137 0x89 '‰' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 138 0x8a 'Š' */
+    0x00, /* 00000000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 139 0x8b '‹' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x66, /* 01100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 140 0x8c 'Œ' */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 141 0x8d '' */
+    0x00, /* 00000000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 142 0x8e 'Ž' */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 143 0x8f '' */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 144 0x90 '' */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x66, /* 01100110 */
+    0x62, /* 01100010 */
+    0x68, /* 01101000 */
+    0x78, /* 01111000 */
+    0x68, /* 01101000 */
+    0x62, /* 01100010 */
+    0x66, /* 01100110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 145 0x91 '‘' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xec, /* 11101100 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x7e, /* 01111110 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0x6e, /* 01101110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 146 0x92 '’' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3e, /* 00111110 */
+    0x6c, /* 01101100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xfe, /* 11111110 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xce, /* 11001110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 147 0x93 '“' */
+    0x00, /* 00000000 */
+    0x10, /* 00010000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 148 0x94 '”' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 149 0x95 '•' */
+    0x00, /* 00000000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 150 0x96 '–' */
+    0x00, /* 00000000 */
+    0x30, /* 00110000 */
+    0x78, /* 01111000 */
+    0xcc, /* 11001100 */
+    0x00, /* 00000000 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 151 0x97 '—' */
+    0x00, /* 00000000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 152 0x98 '˜' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7e, /* 01111110 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0x78, /* 01111000 */
+    0x00, /* 00000000 */
+
+    /* 153 0x99 '™' */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 154 0x9a 'š' */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 155 0x9b '›' */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 156 0x9c 'œ' */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x64, /* 01100100 */
+    0x60, /* 01100000 */
+    0xf0, /* 11110000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0xe6, /* 11100110 */
+    0xfc, /* 11111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 157 0x9d '' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 158 0x9e 'ž' */
+    0x00, /* 00000000 */
+    0xf8, /* 11111000 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xf8, /* 11111000 */
+    0xc4, /* 11000100 */
+    0xcc, /* 11001100 */
+    0xde, /* 11011110 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 159 0x9f 'Ÿ' */
+    0x00, /* 00000000 */
+    0x0e, /* 00001110 */
+    0x1b, /* 00011011 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xd8, /* 11011000 */
+    0x70, /* 01110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 160 0xa0 ' ' */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0x00, /* 00000000 */
+    0x78, /* 01111000 */
+    0x0c, /* 00001100 */
+    0x7c, /* 01111100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 161 0xa1 '¡' */
+    0x00, /* 00000000 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 162 0xa2 '¢' */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 163 0xa3 '£' */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0x00, /* 00000000 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 164 0xa4 '¤' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x76, /* 01110110 */
+    0xdc, /* 11011100 */
+    0x00, /* 00000000 */
+    0xdc, /* 11011100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 165 0xa5 '¥' */
+    0x76, /* 01110110 */
+    0xdc, /* 11011100 */
+    0x00, /* 00000000 */
+    0xc6, /* 11000110 */
+    0xe6, /* 11100110 */
+    0xf6, /* 11110110 */
+    0xfe, /* 11111110 */
+    0xde, /* 11011110 */
+    0xce, /* 11001110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 166 0xa6 '¦' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x3e, /* 00111110 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 167 0xa7 '§' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 168 0xa8 '¨' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x30, /* 00110000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xc0, /* 11000000 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x7c, /* 01111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 169 0xa9 '©' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 170 0xaa 'ª' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 171 0xab '«' */
+    0x00, /* 00000000 */
+    0x60, /* 01100000 */
+    0xe0, /* 11100000 */
+    0x62, /* 01100010 */
+    0x66, /* 01100110 */
+    0x6c, /* 01101100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xdc, /* 11011100 */
+    0x86, /* 10000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x3e, /* 00111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 172 0xac '¬' */
+    0x00, /* 00000000 */
+    0x60, /* 01100000 */
+    0xe0, /* 11100000 */
+    0x62, /* 01100010 */
+    0x66, /* 01100110 */
+    0x6c, /* 01101100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x66, /* 01100110 */
+    0xce, /* 11001110 */
+    0x9a, /* 10011010 */
+    0x3f, /* 00111111 */
+    0x06, /* 00000110 */
+    0x06, /* 00000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 173 0xad '­' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x3c, /* 00111100 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 174 0xae '®' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x36, /* 00110110 */
+    0x6c, /* 01101100 */
+    0xd8, /* 11011000 */
+    0x6c, /* 01101100 */
+    0x36, /* 00110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 175 0xaf '¯' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xd8, /* 11011000 */
+    0x6c, /* 01101100 */
+    0x36, /* 00110110 */
+    0x6c, /* 01101100 */
+    0xd8, /* 11011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 176 0xb0 '°' */
+    0x11, /* 00010001 */
+    0x44, /* 01000100 */
+    0x11, /* 00010001 */
+    0x44, /* 01000100 */
+    0x11, /* 00010001 */
+    0x44, /* 01000100 */
+    0x11, /* 00010001 */
+    0x44, /* 01000100 */
+    0x11, /* 00010001 */
+    0x44, /* 01000100 */
+    0x11, /* 00010001 */
+    0x44, /* 01000100 */
+    0x11, /* 00010001 */
+    0x44, /* 01000100 */
+    0x11, /* 00010001 */
+    0x44, /* 01000100 */
+
+    /* 177 0xb1 '±' */
+    0x55, /* 01010101 */
+    0xaa, /* 10101010 */
+    0x55, /* 01010101 */
+    0xaa, /* 10101010 */
+    0x55, /* 01010101 */
+    0xaa, /* 10101010 */
+    0x55, /* 01010101 */
+    0xaa, /* 10101010 */
+    0x55, /* 01010101 */
+    0xaa, /* 10101010 */
+    0x55, /* 01010101 */
+    0xaa, /* 10101010 */
+    0x55, /* 01010101 */
+    0xaa, /* 10101010 */
+    0x55, /* 01010101 */
+    0xaa, /* 10101010 */
+
+    /* 178 0xb2 '²' */
+    0xdd, /* 11011101 */
+    0x77, /* 01110111 */
+    0xdd, /* 11011101 */
+    0x77, /* 01110111 */
+    0xdd, /* 11011101 */
+    0x77, /* 01110111 */
+    0xdd, /* 11011101 */
+    0x77, /* 01110111 */
+    0xdd, /* 11011101 */
+    0x77, /* 01110111 */
+    0xdd, /* 11011101 */
+    0x77, /* 01110111 */
+    0xdd, /* 11011101 */
+    0x77, /* 01110111 */
+    0xdd, /* 11011101 */
+    0x77, /* 01110111 */
+
+    /* 179 0xb3 '³' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 180 0xb4 '´' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xf8, /* 11111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 181 0xb5 'µ' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xf8, /* 11111000 */
+    0x18, /* 00011000 */
+    0xf8, /* 11111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 182 0xb6 '¶' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0xf6, /* 11110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 183 0xb7 '·' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 184 0xb8 '¸' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xf8, /* 11111000 */
+    0x18, /* 00011000 */
+    0xf8, /* 11111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 185 0xb9 '¹' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0xf6, /* 11110110 */
+    0x06, /* 00000110 */
+    0xf6, /* 11110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 186 0xba 'º' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 187 0xbb '»' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x06, /* 00000110 */
+    0xf6, /* 11110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 188 0xbc '¼' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0xf6, /* 11110110 */
+    0x06, /* 00000110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 189 0xbd '½' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 190 0xbe '¾' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xf8, /* 11111000 */
+    0x18, /* 00011000 */
+    0xf8, /* 11111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 191 0xbf '¿' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xf8, /* 11111000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 192 0xc0 'À' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x1f, /* 00011111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 193 0xc1 'Á' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 194 0xc2 'Â' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 195 0xc3 'Ã' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x1f, /* 00011111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 196 0xc4 'Ä' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 197 0xc5 'Å' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xff, /* 11111111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 198 0xc6 'Æ' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x1f, /* 00011111 */
+    0x18, /* 00011000 */
+    0x1f, /* 00011111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 199 0xc7 'Ç' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x37, /* 00110111 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 200 0xc8 'È' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x37, /* 00110111 */
+    0x30, /* 00110000 */
+    0x3f, /* 00111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 201 0xc9 'É' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3f, /* 00111111 */
+    0x30, /* 00110000 */
+    0x37, /* 00110111 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 202 0xca 'Ê' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0xf7, /* 11110111 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 203 0xcb 'Ë' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0xf7, /* 11110111 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 204 0xcc 'Ì' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x37, /* 00110111 */
+    0x30, /* 00110000 */
+    0x37, /* 00110111 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 205 0xcd 'Í' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 206 0xce 'Î' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0xf7, /* 11110111 */
+    0x00, /* 00000000 */
+    0xf7, /* 11110111 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 207 0xcf 'Ï' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 208 0xd0 'Ð' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 209 0xd1 'Ñ' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 210 0xd2 'Ò' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 211 0xd3 'Ó' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x3f, /* 00111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 212 0xd4 'Ô' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x1f, /* 00011111 */
+    0x18, /* 00011000 */
+    0x1f, /* 00011111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 213 0xd5 'Õ' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x1f, /* 00011111 */
+    0x18, /* 00011000 */
+    0x1f, /* 00011111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 214 0xd6 'Ö' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x3f, /* 00111111 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 215 0xd7 '×' */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0xff, /* 11111111 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+
+    /* 216 0xd8 'Ø' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xff, /* 11111111 */
+    0x18, /* 00011000 */
+    0xff, /* 11111111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 217 0xd9 'Ù' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xf8, /* 11111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 218 0xda 'Ú' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x1f, /* 00011111 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 219 0xdb 'Û' */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+
+    /* 220 0xdc 'Ü' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+
+    /* 221 0xdd 'Ý' */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+    0xf0, /* 11110000 */
+
+    /* 222 0xde 'Þ' */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+    0x0f, /* 00001111 */
+
+    /* 223 0xdf 'ß' */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0xff, /* 11111111 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 224 0xe0 'à' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x76, /* 01110110 */
+    0xdc, /* 11011100 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0xdc, /* 11011100 */
+    0x76, /* 01110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 225 0xe1 'á' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x78, /* 01111000 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xcc, /* 11001100 */
+    0xd8, /* 11011000 */
+    0xcc, /* 11001100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xcc, /* 11001100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 226 0xe2 'â' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0xc0, /* 11000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 227 0xe3 'ã' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 228 0xe4 'ä' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 229 0xe5 'å' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0x70, /* 01110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 230 0xe6 'æ' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x7c, /* 01111100 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0xc0, /* 11000000 */
+    0x00, /* 00000000 */
+
+    /* 231 0xe7 'ç' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x76, /* 01110110 */
+    0xdc, /* 11011100 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 232 0xe8 'è' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 233 0xe9 'é' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xfe, /* 11111110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 234 0xea 'ê' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0xee, /* 11101110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 235 0xeb 'ë' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x1e, /* 00011110 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0x3e, /* 00111110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x66, /* 01100110 */
+    0x3c, /* 00111100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 236 0xec 'ì' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0xdb, /* 11011011 */
+    0xdb, /* 11011011 */
+    0xdb, /* 11011011 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 237 0xed 'í' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x03, /* 00000011 */
+    0x06, /* 00000110 */
+    0x7e, /* 01111110 */
+    0xdb, /* 11011011 */
+    0xdb, /* 11011011 */
+    0xf3, /* 11110011 */
+    0x7e, /* 01111110 */
+    0x60, /* 01100000 */
+    0xc0, /* 11000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 238 0xee 'î' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x1c, /* 00011100 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x7c, /* 01111100 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x1c, /* 00011100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 239 0xef 'ï' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7c, /* 01111100 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0xc6, /* 11000110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 240 0xf0 'ð' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0xfe, /* 11111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 241 0xf1 'ñ' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x7e, /* 01111110 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 242 0xf2 'ò' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0x06, /* 00000110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 243 0xf3 'ó' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x30, /* 00110000 */
+    0x60, /* 01100000 */
+    0x30, /* 00110000 */
+    0x18, /* 00011000 */
+    0x0c, /* 00001100 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 244 0xf4 'ô' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x0e, /* 00001110 */
+    0x1b, /* 00011011 */
+    0x1b, /* 00011011 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+
+    /* 245 0xf5 'õ' */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0xd8, /* 11011000 */
+    0x70, /* 01110000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 246 0xf6 'ö' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 247 0xf7 '÷' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x76, /* 01110110 */
+    0xdc, /* 11011100 */
+    0x00, /* 00000000 */
+    0x76, /* 01110110 */
+    0xdc, /* 11011100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 248 0xf8 'ø' */
+    0x00, /* 00000000 */
+    0x38, /* 00111000 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x38, /* 00111000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 249 0xf9 'ù' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 250 0xfa 'ú' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x18, /* 00011000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 251 0xfb 'û' */
+    0x00, /* 00000000 */
+    0x0f, /* 00001111 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0x0c, /* 00001100 */
+    0xec, /* 11101100 */
+    0x6c, /* 01101100 */
+    0x6c, /* 01101100 */
+    0x3c, /* 00111100 */
+    0x1c, /* 00011100 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 252 0xfc 'ü' */
+    0x00, /* 00000000 */
+    0x6c, /* 01101100 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x36, /* 00110110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 253 0xfd 'ý' */
+    0x00, /* 00000000 */
+    0x3c, /* 00111100 */
+    0x66, /* 01100110 */
+    0x0c, /* 00001100 */
+    0x18, /* 00011000 */
+    0x32, /* 00110010 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 254 0xfe 'þ' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x7e, /* 01111110 */
+    0x7e, /* 01111110 */
+    0x7e, /* 01111110 */
+    0x7e, /* 01111110 */
+    0x7e, /* 01111110 */
+    0x7e, /* 01111110 */
+    0x7e, /* 01111110 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+    /* 255 0xff 'ÿ' */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+    0x00, /* 00000000 */
+
+};
diff --git a/ui/meson.build b/ui/meson.build
index d4d9312b98c..25657af50e7 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -15,6 +15,7 @@ system_ss.add(files(
   'ui-hmp-cmds.c',
   'ui-qmp-cmds.c',
   'util.c',
+  'vgafont.c',
 ))
 system_ss.add(when: pixman, if_true: files('console-vc.c', 'cp437.c'), if_false: files('console-vc-stubs.c'))
 if dbus_display

-- 
2.53.0



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

* [PATCH 33/60] ui/vgafont: add SPDX license header
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (31 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 32/60] ui: avoid duplicating vgafont16 in each translation unit Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:24   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 34/60] ui: move FONT_WIDTH/HEIGHT to vgafont.h Marc-André Lureau
                   ` (27 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

The vgafont was added without source origin, but it can be traced back
to Linux kernel, which used
GPL-2 (https://github.com/mpe/linux-fullhistory/blob/master/lib/fonts/font_8x16.c).

commit c6f37d0e4feeb264a699eda289d3cc69405100b0
Author: Fabrice Bellard <fabrice@bellard.org>
Date:   Wed Jul 14 17:39:50 2004 +0000

    virtual console

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vgafont.h | 3 +++
 ui/vgafont.c | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/ui/vgafont.h b/ui/vgafont.h
index 4498ac4e07b..54aeeb7d192 100644
--- a/ui/vgafont.h
+++ b/ui/vgafont.h
@@ -1,3 +1,6 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
 #ifndef VGAFONT_H
 #define VGAFONT_H
 
diff --git a/ui/vgafont.c b/ui/vgafont.c
index 708c845a6bf..b9b9a7016f0 100644
--- a/ui/vgafont.c
+++ b/ui/vgafont.c
@@ -1,3 +1,6 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
 #include "vgafont.h"
 
 const uint8_t vgafont16[256 * 16] = {

-- 
2.53.0



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

* [PATCH 34/60] ui: move FONT_WIDTH/HEIGHT to vgafont.h
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (32 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 33/60] ui/vgafont: add SPDX license header Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:25   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 35/60] ui/console-vc: move VT100 emulation into separate unit Marc-André Lureau
                   ` (26 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

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

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 ba440c50744..8e785cde94c 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -11,6 +11,7 @@
 #include "qemu/queue.h"
 #include "ui/console.h"
 #include "ui/cp437.h"
+#include "ui/vgafont.h"
 
 #include "pixman.h"
 #include "trace.h"
diff --git a/ui/console.c b/ui/console.c
index b2b879e8533..c997e8df572 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.53.0



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

* [PATCH 35/60] ui/console-vc: move VT100 emulation into separate unit
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (33 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 34/60] ui: move FONT_WIDTH/HEIGHT to vgafont.h Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 36/60] util: move datadir.c from system/ Marc-André Lureau
                   ` (25 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Move the VT100 terminal emulation code into dedicated ui/vt100.c and
ui/vt100.h files, completing the extraction from console-vc.c started
in the previous patches. This makes the VT100 layer a self-contained
module that can be reused independently of the chardev/console
infrastructure.

The code is moved as-is, with minor coding style fixes (adding missing
braces, fixing whitespace) applied during the move.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/console-priv.h     |    1 -
 ui/vt100.h            |   92 +++++
 ui/console-vc-stubs.c |    1 +
 ui/console-vc.c       | 1035 +------------------------------------------------
 ui/console.c          |    2 +
 ui/vt100.c            |  987 ++++++++++++++++++++++++++++++++++++++++++++++
 ui/meson.build        |    4 +-
 7 files changed, 1086 insertions(+), 1036 deletions(-)

diff --git a/ui/console-priv.h b/ui/console-priv.h
index 39798c3e9d7..f8855753e30 100644
--- a/ui/console-priv.h
+++ b/ui/console-priv.h
@@ -31,7 +31,6 @@ struct QemuConsole {
 };
 
 void qemu_text_console_update_size(QemuTextConsole *c);
-void vt100_update_cursor(void);
 void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym);
 
 #endif
diff --git a/ui/vt100.h b/ui/vt100.h
new file mode 100644
index 00000000000..18e5320766b
--- /dev/null
+++ b/ui/vt100.h
@@ -0,0 +1,92 @@
+/*
+ * SPDX-License-Identifier: MIT
+ * QEMU vt100
+ */
+#ifndef VT100_H
+#define VT100_H
+
+#include "ui/console.h"
+#include "qemu/fifo8.h"
+#include "qemu/queue.h"
+
+typedef struct TextAttributes {
+    uint8_t fgcol:4;
+    uint8_t bgcol:4;
+    uint8_t bold:1;
+    uint8_t uline:1;
+    uint8_t blink:1;
+    uint8_t invers:1;
+    uint8_t unvisible:1;
+} TextAttributes;
+
+#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
+    .fgcol = QEMU_COLOR_WHITE,                      \
+    .bgcol = QEMU_COLOR_BLACK                       \
+})
+
+typedef struct TextCell {
+    uint8_t ch;
+    TextAttributes t_attrib;
+} TextCell;
+
+#define MAX_ESC_PARAMS 3
+
+enum TTYState {
+    TTY_STATE_NORM,
+    TTY_STATE_ESC,
+    TTY_STATE_CSI,
+    TTY_STATE_G0,
+    TTY_STATE_G1,
+    TTY_STATE_OSC,
+};
+
+typedef struct QemuVT100 QemuVT100;
+
+struct QemuVT100 {
+    pixman_image_t *image;
+    void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height);
+
+    int width;
+    int height;
+    int total_height;
+    int backscroll_height;
+    int x, y;
+    int y_displayed;
+    int y_base;
+    TextCell *cells;
+    int text_x[2], text_y[2], cursor_invalidate;
+    int echo;
+
+    int update_x0;
+    int update_y0;
+    int update_x1;
+    int update_y1;
+
+    enum TTYState state;
+    int esc_params[MAX_ESC_PARAMS];
+    int nb_esc_params;
+    uint32_t utf8_state;     /* UTF-8 DFA decoder state */
+    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
+    TextAttributes t_attrib; /* currently active text attributes */
+    TextAttributes t_attrib_saved;
+    int x_saved, y_saved;
+    /* fifo for key pressed */
+    Fifo8 out_fifo;
+    void (*out_flush)(QemuVT100 *vt);
+
+    QTAILQ_ENTRY(QemuVT100) list;
+};
+
+void vt100_init(QemuVT100 *vt,
+                pixman_image_t *image,
+                void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height),
+                void (*out_flush)(QemuVT100 *vt));
+void vt100_fini(QemuVT100 *vt);
+
+void vt100_update_cursor(void);
+int vt100_input(QemuVT100 *vt, const uint8_t *buf, int len);
+void vt100_keysym(QemuVT100 *vt, int keysym);
+void vt100_set_image(QemuVT100 *vt, pixman_image_t *image);
+void vt100_refresh(QemuVT100 *vt);
+
+#endif
diff --git a/ui/console-vc-stubs.c b/ui/console-vc-stubs.c
index d911a82f263..30e4d101197 100644
--- a/ui/console-vc-stubs.c
+++ b/ui/console-vc-stubs.c
@@ -9,6 +9,7 @@
 #include "qemu/option.h"
 #include "chardev/char.h"
 #include "ui/console-priv.h"
+#include "vt100.h"
 
 void qemu_text_console_update_size(QemuTextConsole *c)
 {
diff --git a/ui/console-vc.c b/ui/console-vc.c
index 8e785cde94c..6e8f2552e41 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -6,91 +6,15 @@
 
 #include "chardev/char.h"
 #include "qapi/error.h"
-#include "qemu/fifo8.h"
 #include "qemu/option.h"
-#include "qemu/queue.h"
 #include "ui/console.h"
-#include "ui/cp437.h"
 #include "ui/vgafont.h"
+#include "ui/vt100.h"
 
 #include "pixman.h"
 #include "trace.h"
 #include "console-priv.h"
 
-#define DEFAULT_BACKSCROLL 512
-#define CONSOLE_CURSOR_PERIOD 500
-
-typedef struct TextAttributes {
-    uint8_t fgcol:4;
-    uint8_t bgcol:4;
-    uint8_t bold:1;
-    uint8_t uline:1;
-    uint8_t blink:1;
-    uint8_t invers:1;
-    uint8_t unvisible:1;
-} TextAttributes;
-
-#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
-    .fgcol = QEMU_COLOR_WHITE,                      \
-    .bgcol = QEMU_COLOR_BLACK                       \
-})
-
-typedef struct TextCell {
-    uint8_t ch;
-    TextAttributes t_attrib;
-} TextCell;
-
-#define MAX_ESC_PARAMS 3
-
-enum TTYState {
-    TTY_STATE_NORM,
-    TTY_STATE_ESC,
-    TTY_STATE_CSI,
-    TTY_STATE_G0,
-    TTY_STATE_G1,
-    TTY_STATE_OSC,
-};
-
-typedef struct QemuVT100 QemuVT100;
-
-struct QemuVT100 {
-    pixman_image_t *image;
-    void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height);
-
-    int width;
-    int height;
-    int total_height;
-    int backscroll_height;
-    int x, y;
-    int y_displayed;
-    int y_base;
-    TextCell *cells;
-    int text_x[2], text_y[2], cursor_invalidate;
-    int echo;
-
-    int update_x0;
-    int update_y0;
-    int update_x1;
-    int update_y1;
-
-    enum TTYState state;
-    int esc_params[MAX_ESC_PARAMS];
-    int nb_esc_params;
-    uint32_t utf8_state;     /* UTF-8 DFA decoder state */
-    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
-    TextAttributes t_attrib; /* currently active text attributes */
-    TextAttributes t_attrib_saved;
-    int x_saved, y_saved;
-    /* fifo for key pressed */
-    Fifo8 out_fifo;
-    void (*out_flush)(QemuVT100 *vt);
-
-    QTAILQ_ENTRY(QemuVT100) list;
-};
-
-static QTAILQ_HEAD(QemuVT100Head, QemuVT100) vt100s =
-    QTAILQ_HEAD_INITIALIZER(vt100s);
-
 typedef struct QemuTextConsole {
     QemuConsole parent;
 
@@ -116,32 +40,6 @@ struct VCChardev {
 };
 typedef struct VCChardev VCChardev;
 
-static const pixman_color_t color_table_rgb[2][8] = {
-    {   /* dark */
-        [QEMU_COLOR_BLACK]   = QEMU_PIXMAN_COLOR_BLACK,
-        [QEMU_COLOR_BLUE]    = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa),  /* blue */
-        [QEMU_COLOR_GREEN]   = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00),  /* green */
-        [QEMU_COLOR_CYAN]    = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa),  /* cyan */
-        [QEMU_COLOR_RED]     = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00),  /* red */
-        [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa),  /* magenta */
-        [QEMU_COLOR_YELLOW]  = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00),  /* yellow */
-        [QEMU_COLOR_WHITE]   = QEMU_PIXMAN_COLOR_GRAY,
-    },
-    {   /* bright */
-        [QEMU_COLOR_BLACK]   = QEMU_PIXMAN_COLOR_BLACK,
-        [QEMU_COLOR_BLUE]    = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff),  /* blue */
-        [QEMU_COLOR_GREEN]   = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00),  /* green */
-        [QEMU_COLOR_CYAN]    = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff),  /* cyan */
-        [QEMU_COLOR_RED]     = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00),  /* red */
-        [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff),  /* magenta */
-        [QEMU_COLOR_YELLOW]  = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00),  /* yellow */
-        [QEMU_COLOR_WHITE]   = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff),  /* white */
-    }
-};
-
-static bool cursor_visible_phase;
-static QEMUTimer *cursor_timer;
-
 static char *
 qemu_text_console_get_label(QemuConsole *c)
 {
@@ -150,157 +48,6 @@ qemu_text_console_get_label(QemuConsole *c)
     return tc->chr ? g_strdup(tc->chr->label) : NULL;
 }
 
-static void image_fill_rect(pixman_image_t *image, int posx, int posy,
-                            int width, int height, pixman_color_t color)
-{
-    pixman_rectangle16_t rect = {
-        .x = posx, .y = posy, .width = width, .height = height
-    };
-
-    pixman_image_fill_rectangles(PIXMAN_OP_SRC, image, &color, 1, &rect);
-}
-
-/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
-static void image_bitblt(pixman_image_t *image,
-                         int xs, int ys, int xd, int yd, int w, int h)
-{
-    pixman_image_composite(PIXMAN_OP_SRC,
-                           image, NULL, image,
-                           xs, ys, 0, 0, xd, yd, w, h);
-}
-
-static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch,
-                            TextAttributes *t_attrib)
-{
-    static pixman_image_t *glyphs[256];
-    pixman_color_t fgcol, bgcol;
-
-    assert(vt->image);
-    if (t_attrib->invers) {
-        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
-        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
-    } else {
-        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
-        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
-    }
-
-    if (!glyphs[ch]) {
-        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
-    }
-    qemu_pixman_glyph_render(glyphs[ch], vt->image,
-                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
-}
-
-static void vt100_invalidate_xy(QemuVT100 *vt, int x, int y)
-{
-    if (vt->update_x0 > x * FONT_WIDTH) {
-        vt->update_x0 = x * FONT_WIDTH;
-    }
-    if (vt->update_y0 > y * FONT_HEIGHT) {
-        vt->update_y0 = y * FONT_HEIGHT;
-    }
-    if (vt->update_x1 < (x + 1) * FONT_WIDTH) {
-        vt->update_x1 = (x + 1) * FONT_WIDTH;
-    }
-    if (vt->update_y1 < (y + 1) * FONT_HEIGHT) {
-        vt->update_y1 = (y + 1) * FONT_HEIGHT;
-    }
-}
-
-static void vt100_show_cursor(QemuVT100 *vt, int show)
-{
-    TextCell *c;
-    int y, y1;
-    int x = vt->x;
-
-    vt->cursor_invalidate = 1;
-
-    if (x >= vt->width) {
-        x = vt->width - 1;
-    }
-    y1 = (vt->y_base + vt->y) % vt->total_height;
-    y = y1 - vt->y_displayed;
-    if (y < 0) {
-        y += vt->total_height;
-    }
-    if (y < vt->height) {
-        c = &vt->cells[y1 * vt->width + x];
-        if (show && cursor_visible_phase) {
-            TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-            t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
-            vt100_putcharxy(vt, x, y, c->ch, &t_attrib);
-        } else {
-            vt100_putcharxy(vt, x, y, c->ch, &(c->t_attrib));
-        }
-        vt100_invalidate_xy(vt, x, y);
-    }
-}
-
-static void vt100_image_update(QemuVT100 *vt, int x, int y, int width, int height)
-{
-    vt->image_update(vt, x, y, width, height);
-}
-
-static void vt100_refresh(QemuVT100 *vt)
-{
-    TextCell *c;
-    int x, y, y1;
-    int w = pixman_image_get_width(vt->image);
-    int h = pixman_image_get_height(vt->image);
-
-    vt->text_x[0] = 0;
-    vt->text_y[0] = 0;
-    vt->text_x[1] = vt->width - 1;
-    vt->text_y[1] = vt->height - 1;
-    vt->cursor_invalidate = 1;
-
-    image_fill_rect(vt->image, 0, 0, w, h,
-                    color_table_rgb[0][QEMU_COLOR_BLACK]);
-    y1 = vt->y_displayed;
-    for (y = 0; y < vt->height; y++) {
-        c = vt->cells + y1 * vt->width;
-        for (x = 0; x < vt->width; x++) {
-            vt100_putcharxy(vt, x, y, c->ch,
-                          &(c->t_attrib));
-            c++;
-        }
-        if (++y1 == vt->total_height) {
-            y1 = 0;
-        }
-    }
-    vt100_show_cursor(vt, 1);
-    vt100_image_update(vt, 0, 0, w, h);
-}
-
-static void vt100_scroll(QemuVT100 *vt, int ydelta)
-{
-    int i, y1;
-
-    if (ydelta > 0) {
-        for(i = 0; i < ydelta; i++) {
-            if (vt->y_displayed == vt->y_base)
-                break;
-            if (++vt->y_displayed == vt->total_height)
-                vt->y_displayed = 0;
-        }
-    } else {
-        ydelta = -ydelta;
-        i = vt->backscroll_height;
-        if (i > vt->total_height - vt->height)
-            i = vt->total_height - vt->height;
-        y1 = vt->y_base - i;
-        if (y1 < 0)
-            y1 += vt->total_height;
-        for(i = 0; i < ydelta; i++) {
-            if (vt->y_displayed == y1)
-                break;
-            if (--vt->y_displayed < 0)
-                vt->y_displayed = vt->total_height - 1;
-        }
-    }
-    vt100_refresh(vt);
-}
-
 static void qemu_text_console_out_flush(QemuTextConsole *s)
 {
     uint32_t len, avail;
@@ -318,64 +65,6 @@ static void qemu_text_console_out_flush(QemuTextConsole *s)
     }
 }
 
-static void vt100_write(QemuVT100 *vt, const void *buf, size_t len)
-{
-    uint32_t num_free;
-
-    num_free = fifo8_num_free(&vt->out_fifo);
-    fifo8_push_all(&vt->out_fifo, buf, MIN(num_free, len));
-    vt->out_flush(vt);
-}
-
-static int vt100_input(QemuVT100 *vt, const uint8_t *buf, int len);
-
-static void vt100_keysym(QemuVT100 *vt, int keysym)
-{
-    uint8_t buf[16], *q;
-    int c;
-
-    switch(keysym) {
-    case QEMU_KEY_CTRL_UP:
-        vt100_scroll(vt, -1);
-        break;
-    case QEMU_KEY_CTRL_DOWN:
-        vt100_scroll(vt, 1);
-        break;
-    case QEMU_KEY_CTRL_PAGEUP:
-        vt100_scroll(vt, -10);
-        break;
-    case QEMU_KEY_CTRL_PAGEDOWN:
-        vt100_scroll(vt, 10);
-        break;
-    default:
-        /* convert the QEMU keysym to VT100 key string */
-        q = buf;
-        if (keysym >= 0xe100 && keysym <= 0xe11f) {
-            *q++ = '\033';
-            *q++ = '[';
-            c = keysym - 0xe100;
-            if (c >= 10)
-                *q++ = '0' + (c / 10);
-            *q++ = '0' + (c % 10);
-            *q++ = '~';
-        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
-            *q++ = '\033';
-            *q++ = '[';
-            *q++ = keysym & 0xff;
-        } else if (vt->echo && (keysym == '\r' || keysym == '\n')) {
-            vt100_input(vt, (uint8_t *)"\r", 1);
-            *q++ = '\n';
-        } else {
-            *q++ = keysym;
-        }
-        if (vt->echo) {
-            vt100_input(vt, buf, q - buf);
-        }
-        vt100_write(vt, buf, q - buf);
-        break;
-    }
-
-}
 /* called when an ascii key is pressed */
 void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
 {
@@ -410,682 +99,10 @@ static void text_console_update(void *opaque, uint32_t *chardata)
     }
 }
 
-static void vt100_set_image(QemuVT100 *vt, pixman_image_t *image)
-{
-    TextCell *cells, *c, *c1;
-    int w1, x, y, last_width, w, h;
-
-    vt->image = image;
-    w = pixman_image_get_width(image) / FONT_WIDTH;
-    h = pixman_image_get_height(image) / FONT_HEIGHT;
-    if (w == vt->width && h == vt->height) {
-        return;
-    }
-
-    last_width = vt->width;
-    vt->width = w;
-    vt->height = h;
-
-    w1 = MIN(vt->width, last_width);
-
-    cells = g_new(TextCell, vt->width * vt->total_height + 1);
-    for (y = 0; y < vt->total_height; y++) {
-        c = &cells[y * vt->width];
-        if (w1 > 0) {
-            c1 = &vt->cells[y * last_width];
-            for (x = 0; x < w1; x++) {
-                *c++ = *c1++;
-            }
-        }
-        for (x = w1; x < vt->width; x++) {
-            c->ch = ' ';
-            c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-            c++;
-        }
-    }
-    g_free(vt->cells);
-    vt->cells = cells;
-}
-
-static void vt100_put_lf(QemuVT100 *vt)
-{
-    TextCell *c;
-    int x, y1;
-
-    vt->y++;
-    if (vt->y >= vt->height) {
-        vt->y = vt->height - 1;
-
-        if (vt->y_displayed == vt->y_base) {
-            if (++vt->y_displayed == vt->total_height)
-                vt->y_displayed = 0;
-        }
-        if (++vt->y_base == vt->total_height)
-            vt->y_base = 0;
-        if (vt->backscroll_height < vt->total_height)
-            vt->backscroll_height++;
-        y1 = (vt->y_base + vt->height - 1) % vt->total_height;
-        c = &vt->cells[y1 * vt->width];
-        for(x = 0; x < vt->width; x++) {
-            c->ch = ' ';
-            c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-            c++;
-        }
-        if (vt->y_displayed == vt->y_base) {
-            vt->text_x[0] = 0;
-            vt->text_y[0] = 0;
-            vt->text_x[1] = vt->width - 1;
-            vt->text_y[1] = vt->height - 1;
-
-            image_bitblt(vt->image, 0, FONT_HEIGHT, 0, 0,
-                         vt->width * FONT_WIDTH,
-                         (vt->height - 1) * FONT_HEIGHT);
-            image_fill_rect(vt->image, 0, (vt->height - 1) * FONT_HEIGHT,
-                            vt->width * FONT_WIDTH, FONT_HEIGHT,
-                            color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
-            vt->update_x0 = 0;
-            vt->update_y0 = 0;
-            vt->update_x1 = vt->width * FONT_WIDTH;
-            vt->update_y1 = vt->height * FONT_HEIGHT;
-        }
-    }
-}
-
-/* Set console attributes depending on the current escape codes.
- * NOTE: I know this code is not very efficient (checking every color for it
- * self) but it is more readable and better maintainable.
- */
-static void vt100_handle_escape(QemuVT100 *vt)
-{
-    int i;
-
-    for (i = 0; i < vt->nb_esc_params; i++) {
-        switch (vt->esc_params[i]) {
-            case 0: /* reset all console attributes to default */
-                vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-                break;
-            case 1:
-                vt->t_attrib.bold = 1;
-                break;
-            case 4:
-                vt->t_attrib.uline = 1;
-                break;
-            case 5:
-                vt->t_attrib.blink = 1;
-                break;
-            case 7:
-                vt->t_attrib.invers = 1;
-                break;
-            case 8:
-                vt->t_attrib.unvisible = 1;
-                break;
-            case 22:
-                vt->t_attrib.bold = 0;
-                break;
-            case 24:
-                vt->t_attrib.uline = 0;
-                break;
-            case 25:
-                vt->t_attrib.blink = 0;
-                break;
-            case 27:
-                vt->t_attrib.invers = 0;
-                break;
-            case 28:
-                vt->t_attrib.unvisible = 0;
-                break;
-            /* set foreground color */
-            case 30:
-                vt->t_attrib.fgcol = QEMU_COLOR_BLACK;
-                break;
-            case 31:
-                vt->t_attrib.fgcol = QEMU_COLOR_RED;
-                break;
-            case 32:
-                vt->t_attrib.fgcol = QEMU_COLOR_GREEN;
-                break;
-            case 33:
-                vt->t_attrib.fgcol = QEMU_COLOR_YELLOW;
-                break;
-            case 34:
-                vt->t_attrib.fgcol = QEMU_COLOR_BLUE;
-                break;
-            case 35:
-                vt->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
-                break;
-            case 36:
-                vt->t_attrib.fgcol = QEMU_COLOR_CYAN;
-                break;
-            case 37:
-                vt->t_attrib.fgcol = QEMU_COLOR_WHITE;
-                break;
-            /* set background color */
-            case 40:
-                vt->t_attrib.bgcol = QEMU_COLOR_BLACK;
-                break;
-            case 41:
-                vt->t_attrib.bgcol = QEMU_COLOR_RED;
-                break;
-            case 42:
-                vt->t_attrib.bgcol = QEMU_COLOR_GREEN;
-                break;
-            case 43:
-                vt->t_attrib.bgcol = QEMU_COLOR_YELLOW;
-                break;
-            case 44:
-                vt->t_attrib.bgcol = QEMU_COLOR_BLUE;
-                break;
-            case 45:
-                vt->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
-                break;
-            case 46:
-                vt->t_attrib.bgcol = QEMU_COLOR_CYAN;
-                break;
-            case 47:
-                vt->t_attrib.bgcol = QEMU_COLOR_WHITE;
-                break;
-        }
-    }
-}
-
-static void vt100_update_xy(QemuVT100 *vt, int x, int y)
-{
-    TextCell *c;
-    int y1, y2;
-
-    vt->text_x[0] = MIN(vt->text_x[0], x);
-    vt->text_x[1] = MAX(vt->text_x[1], x);
-    vt->text_y[0] = MIN(vt->text_y[0], y);
-    vt->text_y[1] = MAX(vt->text_y[1], y);
-
-    y1 = (vt->y_base + y) % vt->total_height;
-    y2 = y1 - vt->y_displayed;
-    if (y2 < 0) {
-        y2 += vt->total_height;
-    }
-    if (y2 < vt->height) {
-        if (x >= vt->width) {
-            x = vt->width - 1;
-        }
-        c = &vt->cells[y1 * vt->width + x];
-        vt100_putcharxy(vt, x, y2, c->ch,
-                      &(c->t_attrib));
-        vt100_invalidate_xy(vt, x, y2);
-    }
-}
-
-static void vt100_clear_xy(QemuVT100 *vt, int x, int y)
-{
-    int y1 = (vt->y_base + y) % vt->total_height;
-    if (x >= vt->width) {
-        x = vt->width - 1;
-    }
-    TextCell *c = &vt->cells[y1 * vt->width + x];
-    c->ch = ' ';
-    c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-    vt100_update_xy(vt, x, y);
-}
-
-/*
- * UTF-8 DFA decoder by Bjoern Hoehrmann.
- * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
- * See https://github.com/polijan/utf8_decode for details.
- *
- * SPDX-License-Identifier: MIT
- */
-#define UTF8_ACCEPT 0
-#define UTF8_REJECT 12
-
-static const uint8_t utf8d[] = {
-    /* character class lookup */
-    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
-    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-    8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-   10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
-
-    /* state transition lookup */
-     0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
-    12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
-    12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
-    12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
-    12,36,12,12,12,12,12,12,12,12,12,12,
-};
-
-static uint32_t utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
-{
-    uint32_t type = utf8d[byte];
-
-    *codep = (*state != UTF8_ACCEPT) ?
-        (byte & 0x3fu) | (*codep << 6) :
-        (0xffu >> type) & (byte);
-
-    *state = utf8d[256 + *state + type];
-    return *state;
-}
-
-static void vt100_put_one(QemuVT100 *vt, int ch)
-{
-    TextCell *c;
-    int y1;
-    if (vt->x >= vt->width) {
-        /* line wrap */
-        vt->x = 0;
-        vt100_put_lf(vt);
-    }
-    y1 = (vt->y_base + vt->y) % vt->total_height;
-    c = &vt->cells[y1 * vt->width + vt->x];
-    c->ch = ch;
-    c->t_attrib = vt->t_attrib;
-    vt100_update_xy(vt, vt->x, vt->y);
-    vt->x++;
-}
-
-/* set cursor, checking bounds */
-static void vt100_set_cursor(QemuVT100 *vt, int x, int y)
-{
-    if (x < 0) {
-        x = 0;
-    }
-    if (y < 0) {
-        y = 0;
-    }
-    if (y >= vt->height) {
-        y = vt->height - 1;
-    }
-    if (x >= vt->width) {
-        x = vt->width - 1;
-    }
-
-    vt->x = x;
-    vt->y = y;
-}
-
-/**
- * vc_csi_P() - (DCH) deletes one or more characters from the cursor
- * position to the right. As characters are deleted, the remaining
- * characters between the cursor and right margin move to the
- * left. Character attributes move with the characters.
- */
-static void vt100_csi_P(QemuVT100 *vt, unsigned int nr)
-{
-    TextCell *c1, *c2;
-    unsigned int x1, x2, y;
-    unsigned int end, len;
-
-    if (!nr) {
-        nr = 1;
-    }
-    if (nr > vt->width - vt->x) {
-        nr = vt->width - vt->x;
-        if (!nr) {
-            return;
-        }
-    }
-
-    x1 = vt->x;
-    x2 = vt->x + nr;
-    len = vt->width - x2;
-    if (len) {
-        y = (vt->y_base + vt->y) % vt->total_height;
-        c1 = &vt->cells[y * vt->width + x1];
-        c2 = &vt->cells[y * vt->width + x2];
-        memmove(c1, c2, len * sizeof(*c1));
-        for (end = x1 + len; x1 < end; x1++) {
-            vt100_update_xy(vt, x1, vt->y);
-        }
-    }
-    /* Clear the rest */
-    for (; x1 < vt->width; x1++) {
-        vt100_clear_xy(vt, x1, vt->y);
-    }
-}
-
-/**
- * vc_csi_at() - (ICH) inserts `nr` blank characters with the default
- * character attribute. The cursor remains at the beginning of the
- * blank characters. Text between the cursor and right margin moves to
- * the right. Characters scrolled past the right margin are lost.
- */
-static void vt100_csi_at(QemuVT100 *vt, unsigned int nr)
-{
-    TextCell *c1, *c2;
-    unsigned int x1, x2, y;
-    unsigned int end, len;
-
-    if (!nr) {
-        nr = 1;
-    }
-    if (nr > vt->width - vt->x) {
-        nr = vt->width - vt->x;
-        if (!nr) {
-            return;
-        }
-    }
-
-    x1 = vt->x + nr;
-    x2 = vt->x;
-    len = vt->width - x1;
-    if (len) {
-        y = (vt->y_base + vt->y) % vt->total_height;
-        c1 = &vt->cells[y * vt->width + x1];
-        c2 = &vt->cells[y * vt->width + x2];
-        memmove(c1, c2, len * sizeof(*c1));
-        for (end = x1 + len; x1 < end; x1++) {
-            vt100_update_xy(vt, x1, vt->y);
-        }
-    }
-    /* Insert blanks */
-    for (x1 = vt->x; x1 < vt->x + nr; x1++) {
-        vt100_clear_xy(vt, x1, vt->y);
-    }
-}
-
-/**
- * vt100_save_cursor() - saves cursor position and character attributes.
- */
-static void vt100_save_cursor(QemuVT100 *vt)
-{
-    vt->x_saved = vt->x;
-    vt->y_saved = vt->y;
-    vt->t_attrib_saved = vt->t_attrib;
-}
-
-/**
- * vt100_restore_cursor() - restores cursor position and character
- * attributes from saved state.
- */
-static void vt100_restore_cursor(QemuVT100 *vt)
-{
-    vt->x = vt->x_saved;
-    vt->y = vt->y_saved;
-    vt->t_attrib = vt->t_attrib_saved;
-}
-
-static void vt100_putchar(QemuVT100 *vt, int ch)
-{
-    int i;
-    int x, y;
-    g_autofree char *response = NULL;
-
-    switch (vt->state) {
-    case TTY_STATE_NORM:
-        /* Feed byte through the UTF-8 DFA decoder */
-        if (ch >= 0x80) {
-            switch (utf8_decode(&vt->utf8_state, &vt->utf8_codepoint, ch)) {
-            case UTF8_ACCEPT:
-                vt100_put_one(vt, unicode_to_cp437(vt->utf8_codepoint));
-                break;
-            case UTF8_REJECT:
-                /* Reset state so the decoder can resync */
-                vt->utf8_state = UTF8_ACCEPT;
-                break;
-            default:
-                /* Need more bytes */
-                break;
-            }
-            break;
-        }
-        /* ASCII byte: abort any pending UTF-8 sequence */
-        vt->utf8_state = UTF8_ACCEPT;
-        switch(ch) {
-        case '\r':  /* carriage return */
-            vt->x = 0;
-            break;
-        case '\n':  /* newline */
-            vt100_put_lf(vt);
-            break;
-        case '\b':  /* backspace */
-            if (vt->x > 0)
-                vt->x--;
-            break;
-        case '\t':  /* tabspace */
-            if (vt->x + (8 - (vt->x % 8)) > vt->width) {
-                vt->x = 0;
-                vt100_put_lf(vt);
-            } else {
-                vt->x = vt->x + (8 - (vt->x % 8));
-            }
-            break;
-        case '\a':  /* alert aka. bell */
-            /* TODO: has to be implemented */
-            break;
-        case 14:
-            /* SO (shift out), character set 1 (ignored) */
-            break;
-        case 15:
-            /* SI (shift in), character set 0 (ignored) */
-            break;
-        case 27:    /* esc (introducing an escape sequence) */
-            vt->state = TTY_STATE_ESC;
-            break;
-        default:
-            vt100_put_one(vt, ch);
-            break;
-        }
-        break;
-    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
-        if (ch == '[') {
-            for(i=0;i<MAX_ESC_PARAMS;i++)
-                vt->esc_params[i] = 0;
-            vt->nb_esc_params = 0;
-            vt->state = TTY_STATE_CSI;
-        } else if (ch == '(') {
-            vt->state = TTY_STATE_G0;
-        } else if (ch == ')') {
-            vt->state = TTY_STATE_G1;
-        } else if (ch == ']' || ch == 'P' || ch == 'X'
-                   || ch == '^' || ch == '_') {
-            /* String sequences: OSC, DCS, SOS, PM, APC */
-            vt->state = TTY_STATE_OSC;
-        } else if (ch == '7') {
-            vt100_save_cursor(vt);
-            vt->state = TTY_STATE_NORM;
-        } else if (ch == '8') {
-            vt100_restore_cursor(vt);
-            vt->state = TTY_STATE_NORM;
-        } else {
-            vt->state = TTY_STATE_NORM;
-        }
-        break;
-    case TTY_STATE_CSI: /* handle escape sequence parameters */
-        if (ch >= '0' && ch <= '9') {
-            if (vt->nb_esc_params < MAX_ESC_PARAMS) {
-                int *param = &vt->esc_params[vt->nb_esc_params];
-                int digit = (ch - '0');
-
-                *param = (*param <= (INT_MAX - digit) / 10) ?
-                         *param * 10 + digit : INT_MAX;
-            }
-        } else {
-            if (vt->nb_esc_params < MAX_ESC_PARAMS)
-                vt->nb_esc_params++;
-            if (ch == ';' || ch == '?') {
-                break;
-            }
-            trace_console_putchar_csi(vt->esc_params[0], vt->esc_params[1],
-                                      ch, vt->nb_esc_params);
-            vt->state = TTY_STATE_NORM;
-            switch(ch) {
-            case 'A':
-                /* move cursor up */
-                if (vt->esc_params[0] == 0) {
-                    vt->esc_params[0] = 1;
-                }
-                vt100_set_cursor(vt, vt->x, vt->y - vt->esc_params[0]);
-                break;
-            case 'B':
-                /* move cursor down */
-                if (vt->esc_params[0] == 0) {
-                    vt->esc_params[0] = 1;
-                }
-                vt100_set_cursor(vt, vt->x, vt->y + vt->esc_params[0]);
-                break;
-            case 'C':
-                /* move cursor right */
-                if (vt->esc_params[0] == 0) {
-                    vt->esc_params[0] = 1;
-                }
-                vt100_set_cursor(vt, vt->x + vt->esc_params[0], vt->y);
-                break;
-            case 'D':
-                /* move cursor left */
-                if (vt->esc_params[0] == 0) {
-                    vt->esc_params[0] = 1;
-                }
-                vt100_set_cursor(vt, vt->x - vt->esc_params[0], vt->y);
-                break;
-            case 'G':
-                /* move cursor to column */
-                vt100_set_cursor(vt, vt->esc_params[0] - 1, vt->y);
-                break;
-            case 'f':
-            case 'H':
-                /* move cursor to row, column */
-                vt100_set_cursor(vt, vt->esc_params[1] - 1, vt->esc_params[0] - 1);
-                break;
-            case 'J':
-                switch (vt->esc_params[0]) {
-                case 0:
-                    /* clear to end of screen */
-                    for (y = vt->y; y < vt->height; y++) {
-                        for (x = 0; x < vt->width; x++) {
-                            if (y == vt->y && x < vt->x) {
-                                continue;
-                            }
-                            vt100_clear_xy(vt, x, y);
-                        }
-                    }
-                    break;
-                case 1:
-                    /* clear from beginning of screen */
-                    for (y = 0; y <= vt->y; y++) {
-                        for (x = 0; x < vt->width; x++) {
-                            if (y == vt->y && x > vt->x) {
-                                break;
-                            }
-                            vt100_clear_xy(vt, x, y);
-                        }
-                    }
-                    break;
-                case 2:
-                    /* clear entire screen */
-                    for (y = 0; y < vt->height; y++) {
-                        for (x = 0; x < vt->width; x++) {
-                            vt100_clear_xy(vt, x, y);
-                        }
-                    }
-                    break;
-                }
-                break;
-            case 'K':
-                switch (vt->esc_params[0]) {
-                case 0:
-                    /* clear to eol */
-                    for(x = vt->x; x < vt->width; x++) {
-                        vt100_clear_xy(vt, x, vt->y);
-                    }
-                    break;
-                case 1:
-                    /* clear from beginning of line */
-                    for (x = 0; x <= vt->x && x < vt->width; x++) {
-                        vt100_clear_xy(vt, x, vt->y);
-                    }
-                    break;
-                case 2:
-                    /* clear entire line */
-                    for(x = 0; x < vt->width; x++) {
-                        vt100_clear_xy(vt, x, vt->y);
-                    }
-                    break;
-                }
-                break;
-            case 'P':
-                vt100_csi_P(vt, vt->esc_params[0]);
-                break;
-            case 'm':
-                vt100_handle_escape(vt);
-                break;
-            case 'n':
-                switch (vt->esc_params[0]) {
-                case 5:
-                    /* report console status (always succeed)*/
-                    vt100_write(vt, "\033[0n", 4);
-                    break;
-                case 6:
-                    /* report cursor position */
-                    response = g_strdup_printf("\033[%d;%dR",
-                                               vt->y + 1, vt->x + 1);
-                    vt100_write(vt, response, strlen(response));
-                    break;
-                }
-                break;
-            case 's':
-                vt100_save_cursor(vt);
-                break;
-            case 'u':
-                vt100_restore_cursor(vt);
-                break;
-            case '@':
-                vt100_csi_at(vt, vt->esc_params[0]);
-                break;
-            default:
-                trace_console_putchar_unhandled(ch);
-                break;
-            }
-            break;
-        }
-        break;
-    case TTY_STATE_OSC: /* Operating System Command: ESC ] ... BEL/ST */
-        if (ch == '\a') {
-            /* BEL terminates OSC */
-            vt->state = TTY_STATE_NORM;
-        } else if (ch == 27) {
-            /* ESC might start ST (ESC \) */
-            vt->state = TTY_STATE_ESC;
-        }
-        /* All other bytes are silently consumed */
-        break;
-    case TTY_STATE_G0: /* set character sets */
-    case TTY_STATE_G1: /* set character sets */
-        switch (ch) {
-        case 'B':
-            /* Latin-1 map */
-            break;
-        }
-        vt->state = TTY_STATE_NORM;
-        break;
-    }
-}
-
 #define TYPE_CHARDEV_VC "chardev-vc"
 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
                          TYPE_CHARDEV_VC)
 
-static int vt100_input(QemuVT100 *vt, const uint8_t *buf, int len)
-{
-    int i;
-
-    vt->update_x0 = vt->width * FONT_WIDTH;
-    vt->update_y0 = vt->height * FONT_HEIGHT;
-    vt->update_x1 = 0;
-    vt->update_y1 = 0;
-    vt100_show_cursor(vt, 0);
-    for(i = 0; i < len; i++) {
-        vt100_putchar(vt, buf[i]);
-    }
-    vt100_show_cursor(vt, 1);
-    if (vt->update_x0 < vt->update_x1) {
-        vt100_image_update(vt, vt->update_x0, vt->update_y0,
-                           vt->update_x1 - vt->update_x0,
-                           vt->update_y1 - vt->update_y0);
-    }
-    return len;
-}
-
 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
     VCChardev *drv = VC_CHARDEV(chr);
@@ -1094,30 +111,6 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
     return vt100_input(&s->vt, buf, len);
 }
 
-void vt100_update_cursor(void)
-{
-    QemuVT100 *vt;
-
-    cursor_visible_phase = !cursor_visible_phase;
-
-    if (QTAILQ_EMPTY(&vt100s)) {
-        return;
-    }
-
-    QTAILQ_FOREACH(vt, &vt100s, list) {
-        vt100_refresh(vt);
-    }
-
-    timer_mod(cursor_timer,
-        qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
-}
-
-static void
-cursor_timer_cb(void *opaque)
-{
-    vt100_update_cursor();
-}
-
 static void text_console_invalidate(void *opaque)
 {
     QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
@@ -1128,13 +121,6 @@ static void text_console_invalidate(void *opaque)
     vt100_refresh(&s->vt);
 }
 
-static void vt100_fini(QemuVT100 *vt)
-{
-    QTAILQ_REMOVE(&vt100s, vt, list);
-    fifo8_destroy(&vt->out_fifo);
-    g_free(vt->cells);
-}
-
 static void
 qemu_text_console_finalize(Object *obj)
 {
@@ -1213,25 +199,6 @@ static void text_console_out_flush(QemuVT100 *vt)
     qemu_text_console_out_flush(console);
 }
 
-static void vt100_init(QemuVT100 *vt,
-                       pixman_image_t *image,
-                       void (*image_update)(QemuVT100 *vt, int x, int y, int w, int h),
-                       void (*out_flush)(QemuVT100 *vt))
-{
-    if (!cursor_timer) {
-        cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);
-    }
-
-    QTAILQ_INSERT_HEAD(&vt100s, vt, list);
-    fifo8_create(&vt->out_fifo, 16);
-    vt->total_height = DEFAULT_BACKSCROLL;
-    vt->image_update = image_update;
-    vt->out_flush = out_flush;
-    /* set current text attributes to default */
-    vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
-    vt100_set_image(vt, image);
-}
-
 static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
 {
     ChardevVC *vc = backend->u.vc.data;
diff --git a/ui/console.c b/ui/console.c
index c997e8df572..7ffea2776ef 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -39,6 +39,8 @@
 #include "system/memory.h"
 #include "qom/object.h"
 #include "qemu/memfd.h"
+#include "ui/vt100.h"
+#include "vgafont.h"
 
 #include "console-priv.h"
 
diff --git a/ui/vt100.c b/ui/vt100.c
new file mode 100644
index 00000000000..e24c91f538c
--- /dev/null
+++ b/ui/vt100.c
@@ -0,0 +1,987 @@
+/*
+ * SPDX-License-Identifier: MIT
+ * QEMU vt100
+ */
+#include "qemu/osdep.h"
+#include "qemu/timer.h"
+#include "cp437.h"
+#include "vgafont.h"
+#include "vt100.h"
+
+#include "trace.h"
+
+#define DEFAULT_BACKSCROLL 512
+#define CONSOLE_CURSOR_PERIOD 500
+
+static const pixman_color_t color_table_rgb[2][8] = {
+    {   /* dark */
+        [QEMU_COLOR_BLACK]   = QEMU_PIXMAN_COLOR_BLACK,
+        [QEMU_COLOR_BLUE]    = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa),  /* blue */
+        [QEMU_COLOR_GREEN]   = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00),  /* green */
+        [QEMU_COLOR_CYAN]    = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa),  /* cyan */
+        [QEMU_COLOR_RED]     = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00),  /* red */
+        [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa),  /* magenta */
+        [QEMU_COLOR_YELLOW]  = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00),  /* yellow */
+        [QEMU_COLOR_WHITE]   = QEMU_PIXMAN_COLOR_GRAY,
+    },
+    {   /* bright */
+        [QEMU_COLOR_BLACK]   = QEMU_PIXMAN_COLOR_BLACK,
+        [QEMU_COLOR_BLUE]    = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff),  /* blue */
+        [QEMU_COLOR_GREEN]   = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00),  /* green */
+        [QEMU_COLOR_CYAN]    = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff),  /* cyan */
+        [QEMU_COLOR_RED]     = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00),  /* red */
+        [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff),  /* magenta */
+        [QEMU_COLOR_YELLOW]  = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00),  /* yellow */
+        [QEMU_COLOR_WHITE]   = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff),  /* white */
+    }
+};
+
+static bool cursor_visible_phase;
+static QEMUTimer *cursor_timer;
+static QTAILQ_HEAD(QemuVT100Head, QemuVT100) vt100s =
+    QTAILQ_HEAD_INITIALIZER(vt100s);
+
+static void image_fill_rect(pixman_image_t *image, int posx, int posy,
+                            int width, int height, pixman_color_t color)
+{
+    pixman_rectangle16_t rect = {
+        .x = posx, .y = posy, .width = width, .height = height
+    };
+
+    pixman_image_fill_rectangles(PIXMAN_OP_SRC, image,
+                                 &color, 1, &rect);
+}
+
+/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
+static void image_bitblt(pixman_image_t *image,
+                         int xs, int ys, int xd, int yd, int w, int h)
+{
+    pixman_image_composite(PIXMAN_OP_SRC,
+                           image, NULL, image,
+                           xs, ys, 0, 0, xd, yd, w, h);
+}
+
+static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch,
+                            TextAttributes *t_attrib)
+{
+    static pixman_image_t *glyphs[256];
+    pixman_color_t fgcol, bgcol;
+
+    assert(vt->image);
+    if (t_attrib->invers) {
+        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
+        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
+    } else {
+        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
+        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
+    }
+
+    if (!glyphs[ch]) {
+        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
+    }
+    qemu_pixman_glyph_render(glyphs[ch], vt->image,
+                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
+}
+
+static void vt100_invalidate_xy(QemuVT100 *vt, int x, int y)
+{
+    if (vt->update_x0 > x * FONT_WIDTH) {
+        vt->update_x0 = x * FONT_WIDTH;
+    }
+    if (vt->update_y0 > y * FONT_HEIGHT) {
+        vt->update_y0 = y * FONT_HEIGHT;
+    }
+    if (vt->update_x1 < (x + 1) * FONT_WIDTH) {
+        vt->update_x1 = (x + 1) * FONT_WIDTH;
+    }
+    if (vt->update_y1 < (y + 1) * FONT_HEIGHT) {
+        vt->update_y1 = (y + 1) * FONT_HEIGHT;
+    }
+}
+
+static void vt100_show_cursor(QemuVT100 *vt, int show)
+{
+    TextCell *c;
+    int y, y1;
+    int x = vt->x;
+
+    vt->cursor_invalidate = 1;
+
+    if (x >= vt->width) {
+        x = vt->width - 1;
+    }
+    y1 = (vt->y_base + vt->y) % vt->total_height;
+    y = y1 - vt->y_displayed;
+    if (y < 0) {
+        y += vt->total_height;
+    }
+    if (y < vt->height) {
+        c = &vt->cells[y1 * vt->width + x];
+        if (show && cursor_visible_phase) {
+            TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+            t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
+            vt100_putcharxy(vt, x, y, c->ch, &t_attrib);
+        } else {
+            vt100_putcharxy(vt, x, y, c->ch, &(c->t_attrib));
+        }
+        vt100_invalidate_xy(vt, x, y);
+    }
+}
+
+static void vt100_image_update(QemuVT100 *vt, int x, int y, int width, int height)
+{
+    vt->image_update(vt, x, y, width, height);
+}
+
+void vt100_refresh(QemuVT100 *vt)
+{
+    TextCell *c;
+    int x, y, y1;
+    int w = pixman_image_get_width(vt->image);
+    int h = pixman_image_get_height(vt->image);
+
+    vt->text_x[0] = 0;
+    vt->text_y[0] = 0;
+    vt->text_x[1] = vt->width - 1;
+    vt->text_y[1] = vt->height - 1;
+    vt->cursor_invalidate = 1;
+
+    image_fill_rect(vt->image, 0, 0, w, h,
+                    color_table_rgb[0][QEMU_COLOR_BLACK]);
+    y1 = vt->y_displayed;
+    for (y = 0; y < vt->height; y++) {
+        c = vt->cells + y1 * vt->width;
+        for (x = 0; x < vt->width; x++) {
+            vt100_putcharxy(vt, x, y, c->ch,
+                            &(c->t_attrib));
+            c++;
+        }
+        if (++y1 == vt->total_height) {
+            y1 = 0;
+        }
+    }
+    vt100_show_cursor(vt, 1);
+    vt100_image_update(vt, 0, 0, w, h);
+}
+
+static void vt100_scroll(QemuVT100 *vt, int ydelta)
+{
+    int i, y1;
+
+    if (ydelta > 0) {
+        for (i = 0; i < ydelta; i++) {
+            if (vt->y_displayed == vt->y_base) {
+                break;
+            }
+            if (++vt->y_displayed == vt->total_height) {
+                vt->y_displayed = 0;
+            }
+        }
+    } else {
+        ydelta = -ydelta;
+        i = vt->backscroll_height;
+        if (i > vt->total_height - vt->height) {
+            i = vt->total_height - vt->height;
+        }
+        y1 = vt->y_base - i;
+        if (y1 < 0) {
+            y1 += vt->total_height;
+        }
+        for (i = 0; i < ydelta; i++) {
+            if (vt->y_displayed == y1) {
+                break;
+            }
+            if (--vt->y_displayed < 0) {
+                vt->y_displayed = vt->total_height - 1;
+            }
+        }
+    }
+    vt100_refresh(vt);
+}
+
+static void vt100_write(QemuVT100 *vt, const void *buf, size_t len)
+{
+    uint32_t num_free;
+
+    num_free = fifo8_num_free(&vt->out_fifo);
+    fifo8_push_all(&vt->out_fifo, buf, MIN(num_free, len));
+    vt->out_flush(vt);
+}
+
+void vt100_set_image(QemuVT100 *vt, pixman_image_t *image)
+{
+    TextCell *cells, *c, *c1;
+    int w1, x, y, last_width, w, h;
+
+    vt->image = image;
+    w = pixman_image_get_width(vt->image) / FONT_WIDTH;
+    h = pixman_image_get_height(vt->image) / FONT_HEIGHT;
+    if (w == vt->width && h == vt->height) {
+        return;
+    }
+
+    last_width = vt->width;
+    vt->width = w;
+    vt->height = h;
+
+    w1 = MIN(vt->width, last_width);
+
+    cells = g_new(TextCell, vt->width * vt->total_height + 1);
+    for (y = 0; y < vt->total_height; y++) {
+        c = &cells[y * vt->width];
+        if (w1 > 0) {
+            c1 = &vt->cells[y * last_width];
+            for (x = 0; x < w1; x++) {
+                *c++ = *c1++;
+            }
+        }
+        for (x = w1; x < vt->width; x++) {
+            c->ch = ' ';
+            c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+            c++;
+        }
+    }
+    g_free(vt->cells);
+    vt->cells = cells;
+}
+
+static void vt100_put_lf(QemuVT100 *vt)
+{
+    TextCell *c;
+    int x, y1;
+
+    vt->y++;
+    if (vt->y >= vt->height) {
+        vt->y = vt->height - 1;
+
+        if (vt->y_displayed == vt->y_base) {
+            if (++vt->y_displayed == vt->total_height) {
+                vt->y_displayed = 0;
+            }
+        }
+        if (++vt->y_base == vt->total_height) {
+            vt->y_base = 0;
+        }
+        if (vt->backscroll_height < vt->total_height) {
+            vt->backscroll_height++;
+        }
+        y1 = (vt->y_base + vt->height - 1) % vt->total_height;
+        c = &vt->cells[y1 * vt->width];
+        for (x = 0; x < vt->width; x++) {
+            c->ch = ' ';
+            c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+            c++;
+        }
+        if (vt->y_displayed == vt->y_base) {
+            vt->text_x[0] = 0;
+            vt->text_y[0] = 0;
+            vt->text_x[1] = vt->width - 1;
+            vt->text_y[1] = vt->height - 1;
+
+            image_bitblt(vt->image, 0, FONT_HEIGHT, 0, 0,
+                         vt->width * FONT_WIDTH,
+                         (vt->height - 1) * FONT_HEIGHT);
+            image_fill_rect(vt->image, 0, (vt->height - 1) * FONT_HEIGHT,
+                            vt->width * FONT_WIDTH, FONT_HEIGHT,
+                            color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
+            vt->update_x0 = 0;
+            vt->update_y0 = 0;
+            vt->update_x1 = vt->width * FONT_WIDTH;
+            vt->update_y1 = vt->height * FONT_HEIGHT;
+        }
+    }
+}
+
+/*
+ * Set console attributes depending on the current escape codes.
+ * NOTE: I know this code is not very efficient (checking every color for it
+ * self) but it is more readable and better maintainable.
+ */
+static void vt100_handle_escape(QemuVT100 *vt)
+{
+    int i;
+
+    for (i = 0; i < vt->nb_esc_params; i++) {
+        switch (vt->esc_params[i]) {
+        case 0: /* reset all console attributes to default */
+            vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+            break;
+        case 1:
+            vt->t_attrib.bold = 1;
+            break;
+        case 4:
+            vt->t_attrib.uline = 1;
+            break;
+        case 5:
+            vt->t_attrib.blink = 1;
+            break;
+        case 7:
+            vt->t_attrib.invers = 1;
+            break;
+        case 8:
+            vt->t_attrib.unvisible = 1;
+            break;
+        case 22:
+            vt->t_attrib.bold = 0;
+            break;
+        case 24:
+            vt->t_attrib.uline = 0;
+            break;
+        case 25:
+            vt->t_attrib.blink = 0;
+            break;
+        case 27:
+            vt->t_attrib.invers = 0;
+            break;
+        case 28:
+            vt->t_attrib.unvisible = 0;
+            break;
+        /* set foreground color */
+        case 30:
+            vt->t_attrib.fgcol = QEMU_COLOR_BLACK;
+            break;
+        case 31:
+            vt->t_attrib.fgcol = QEMU_COLOR_RED;
+            break;
+        case 32:
+            vt->t_attrib.fgcol = QEMU_COLOR_GREEN;
+            break;
+        case 33:
+            vt->t_attrib.fgcol = QEMU_COLOR_YELLOW;
+            break;
+        case 34:
+            vt->t_attrib.fgcol = QEMU_COLOR_BLUE;
+            break;
+        case 35:
+            vt->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
+            break;
+        case 36:
+            vt->t_attrib.fgcol = QEMU_COLOR_CYAN;
+            break;
+        case 37:
+            vt->t_attrib.fgcol = QEMU_COLOR_WHITE;
+            break;
+        /* set background color */
+        case 40:
+            vt->t_attrib.bgcol = QEMU_COLOR_BLACK;
+            break;
+        case 41:
+            vt->t_attrib.bgcol = QEMU_COLOR_RED;
+            break;
+        case 42:
+            vt->t_attrib.bgcol = QEMU_COLOR_GREEN;
+            break;
+        case 43:
+            vt->t_attrib.bgcol = QEMU_COLOR_YELLOW;
+            break;
+        case 44:
+            vt->t_attrib.bgcol = QEMU_COLOR_BLUE;
+            break;
+        case 45:
+            vt->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
+            break;
+        case 46:
+            vt->t_attrib.bgcol = QEMU_COLOR_CYAN;
+            break;
+        case 47:
+            vt->t_attrib.bgcol = QEMU_COLOR_WHITE;
+            break;
+        }
+    }
+}
+
+static void vt100_update_xy(QemuVT100 *vt, int x, int y)
+{
+    TextCell *c;
+    int y1, y2;
+
+    vt->text_x[0] = MIN(vt->text_x[0], x);
+    vt->text_x[1] = MAX(vt->text_x[1], x);
+    vt->text_y[0] = MIN(vt->text_y[0], y);
+    vt->text_y[1] = MAX(vt->text_y[1], y);
+
+    y1 = (vt->y_base + y) % vt->total_height;
+    y2 = y1 - vt->y_displayed;
+    if (y2 < 0) {
+        y2 += vt->total_height;
+    }
+    if (y2 < vt->height) {
+        if (x >= vt->width) {
+            x = vt->width - 1;
+        }
+        c = &vt->cells[y1 * vt->width + x];
+        vt100_putcharxy(vt, x, y2, c->ch,
+                      &(c->t_attrib));
+        vt100_invalidate_xy(vt, x, y2);
+    }
+}
+
+static void vt100_clear_xy(QemuVT100 *vt, int x, int y)
+{
+    int y1 = (vt->y_base + y) % vt->total_height;
+    if (x >= vt->width) {
+        x = vt->width - 1;
+    }
+    TextCell *c = &vt->cells[y1 * vt->width + x];
+    c->ch = ' ';
+    c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+    vt100_update_xy(vt, x, y);
+}
+
+/*
+ * UTF-8 DFA decoder by Bjoern Hoehrmann.
+ * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ * See https://github.com/polijan/utf8_decode for details.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 12
+
+static const uint8_t utf8d[] = {
+    /* character class lookup */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+    8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+   10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+    /* state transition lookup */
+     0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+    12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+    12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+    12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+    12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static uint32_t utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
+{
+    uint32_t type = utf8d[byte];
+
+    *codep = (*state != UTF8_ACCEPT) ?
+        (byte & 0x3fu) | (*codep << 6) :
+        (0xffu >> type) & (byte);
+
+    *state = utf8d[256 + *state + type];
+    return *state;
+}
+
+static void vt100_put_one(QemuVT100 *vt, int ch)
+{
+    TextCell *c;
+    int y1;
+    if (vt->x >= vt->width) {
+        /* line wrap */
+        vt->x = 0;
+        vt100_put_lf(vt);
+    }
+    y1 = (vt->y_base + vt->y) % vt->total_height;
+    c = &vt->cells[y1 * vt->width + vt->x];
+    c->ch = ch;
+    c->t_attrib = vt->t_attrib;
+    vt100_update_xy(vt, vt->x, vt->y);
+    vt->x++;
+}
+
+/* set cursor, checking bounds */
+static void vt100_set_cursor(QemuVT100 *vt, int x, int y)
+{
+    if (x < 0) {
+        x = 0;
+    }
+    if (y < 0) {
+        y = 0;
+    }
+    if (y >= vt->height) {
+        y = vt->height - 1;
+    }
+    if (x >= vt->width) {
+        x = vt->width - 1;
+    }
+
+    vt->x = x;
+    vt->y = y;
+}
+
+/**
+ * vt100_csi_P() - (DCH) deletes one or more characters from the cursor
+ * position to the right. As characters are deleted, the remaining
+ * characters between the cursor and right margin move to the
+ * left. Character attributes move with the characters.
+ */
+static void vt100_csi_P(QemuVT100 *vt, unsigned int nr)
+{
+    TextCell *c1, *c2;
+    unsigned int x1, x2, y;
+    unsigned int end, len;
+
+    if (!nr) {
+        nr = 1;
+    }
+    if (nr > vt->width - vt->x) {
+        nr = vt->width - vt->x;
+        if (!nr) {
+            return;
+        }
+    }
+
+    x1 = vt->x;
+    x2 = vt->x + nr;
+    len = vt->width - x2;
+    if (len) {
+        y = (vt->y_base + vt->y) % vt->total_height;
+        c1 = &vt->cells[y * vt->width + x1];
+        c2 = &vt->cells[y * vt->width + x2];
+        memmove(c1, c2, len * sizeof(*c1));
+        for (end = x1 + len; x1 < end; x1++) {
+            vt100_update_xy(vt, x1, vt->y);
+        }
+    }
+    /* Clear the rest */
+    for (; x1 < vt->width; x1++) {
+        vt100_clear_xy(vt, x1, vt->y);
+    }
+}
+
+/**
+ * vt100_csi_at() - (ICH) inserts `nr` blank characters with the default
+ * character attribute. The cursor remains at the beginning of the
+ * blank characters. Text between the cursor and right margin moves to
+ * the right. Characters scrolled past the right margin are lost.
+ */
+static void vt100_csi_at(QemuVT100 *vt, unsigned int nr)
+{
+    TextCell *c1, *c2;
+    unsigned int x1, x2, y;
+    unsigned int end, len;
+
+    if (!nr) {
+        nr = 1;
+    }
+    if (nr > vt->width - vt->x) {
+        nr = vt->width - vt->x;
+        if (!nr) {
+            return;
+        }
+    }
+
+    x1 = vt->x + nr;
+    x2 = vt->x;
+    len = vt->width - x1;
+    if (len) {
+        y = (vt->y_base + vt->y) % vt->total_height;
+        c1 = &vt->cells[y * vt->width + x1];
+        c2 = &vt->cells[y * vt->width + x2];
+        memmove(c1, c2, len * sizeof(*c1));
+        for (end = x1 + len; x1 < end; x1++) {
+            vt100_update_xy(vt, x1, vt->y);
+        }
+    }
+    /* Insert blanks */
+    for (x1 = vt->x; x1 < vt->x + nr; x1++) {
+        vt100_clear_xy(vt, x1, vt->y);
+    }
+}
+
+/**
+ * vt100_save_cursor() - saves cursor position and character attributes.
+ */
+static void vt100_save_cursor(QemuVT100 *vt)
+{
+    vt->x_saved = vt->x;
+    vt->y_saved = vt->y;
+    vt->t_attrib_saved = vt->t_attrib;
+}
+
+/**
+ * vt100_restore_cursor() - restores cursor position and character
+ * attributes from saved state.
+ */
+static void vt100_restore_cursor(QemuVT100 *vt)
+{
+    vt->x = vt->x_saved;
+    vt->y = vt->y_saved;
+    vt->t_attrib = vt->t_attrib_saved;
+}
+
+static void vt100_putchar(QemuVT100 *vt, int ch)
+{
+    int i;
+    int x, y;
+    g_autofree char *response = NULL;
+
+    switch (vt->state) {
+    case TTY_STATE_NORM:
+        /* Feed byte through the UTF-8 DFA decoder */
+        if (ch >= 0x80) {
+            switch (utf8_decode(&vt->utf8_state, &vt->utf8_codepoint, ch)) {
+            case UTF8_ACCEPT:
+                vt100_put_one(vt, unicode_to_cp437(vt->utf8_codepoint));
+                break;
+            case UTF8_REJECT:
+                /* Reset state so the decoder can resync */
+                vt->utf8_state = UTF8_ACCEPT;
+                break;
+            default:
+                /* Need more bytes */
+                break;
+            }
+            break;
+        }
+        /* ASCII byte: abort any pending UTF-8 sequence */
+        vt->utf8_state = UTF8_ACCEPT;
+        switch (ch) {
+        case '\r':  /* carriage return */
+            vt->x = 0;
+            break;
+        case '\n':  /* newline */
+            vt100_put_lf(vt);
+            break;
+        case '\b':  /* backspace */
+            if (vt->x > 0) {
+                vt->x--;
+            }
+            break;
+        case '\t':  /* tabspace */
+            if (vt->x + (8 - (vt->x % 8)) > vt->width) {
+                vt->x = 0;
+                vt100_put_lf(vt);
+            } else {
+                vt->x = vt->x + (8 - (vt->x % 8));
+            }
+            break;
+        case '\a':  /* alert aka. bell */
+            /* TODO: has to be implemented */
+            break;
+        case 14:
+            /* SO (shift out), character set 1 (ignored) */
+            break;
+        case 15:
+            /* SI (shift in), character set 0 (ignored) */
+            break;
+        case 27:    /* esc (introducing an escape sequence) */
+            vt->state = TTY_STATE_ESC;
+            break;
+        default:
+            vt100_put_one(vt, ch);
+            break;
+        }
+        break;
+    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
+        if (ch == '[') {
+            for (i = 0; i < MAX_ESC_PARAMS; i++) {
+                vt->esc_params[i] = 0;
+            }
+            vt->nb_esc_params = 0;
+            vt->state = TTY_STATE_CSI;
+        } else if (ch == '(') {
+            vt->state = TTY_STATE_G0;
+        } else if (ch == ')') {
+            vt->state = TTY_STATE_G1;
+        } else if (ch == ']' || ch == 'P' || ch == 'X'
+                   || ch == '^' || ch == '_') {
+            /* String sequences: OSC, DCS, SOS, PM, APC */
+            vt->state = TTY_STATE_OSC;
+        } else if (ch == '7') {
+            vt100_save_cursor(vt);
+            vt->state = TTY_STATE_NORM;
+        } else if (ch == '8') {
+            vt100_restore_cursor(vt);
+            vt->state = TTY_STATE_NORM;
+        } else {
+            vt->state = TTY_STATE_NORM;
+        }
+        break;
+    case TTY_STATE_CSI: /* handle escape sequence parameters */
+        if (ch >= '0' && ch <= '9') {
+            if (vt->nb_esc_params < MAX_ESC_PARAMS) {
+                int *param = &vt->esc_params[vt->nb_esc_params];
+                int digit = (ch - '0');
+
+                *param = (*param <= (INT_MAX - digit) / 10) ?
+                         *param * 10 + digit : INT_MAX;
+            }
+        } else {
+            if (vt->nb_esc_params < MAX_ESC_PARAMS) {
+                vt->nb_esc_params++;
+            }
+            if (ch == ';' || ch == '?') {
+                break;
+            }
+            trace_console_putchar_csi(vt->esc_params[0], vt->esc_params[1],
+                                      ch, vt->nb_esc_params);
+            vt->state = TTY_STATE_NORM;
+            switch (ch) {
+            case 'A':
+                /* move cursor up */
+                if (vt->esc_params[0] == 0) {
+                    vt->esc_params[0] = 1;
+                }
+                vt100_set_cursor(vt, vt->x, vt->y - vt->esc_params[0]);
+                break;
+            case 'B':
+                /* move cursor down */
+                if (vt->esc_params[0] == 0) {
+                    vt->esc_params[0] = 1;
+                }
+                vt100_set_cursor(vt, vt->x, vt->y + vt->esc_params[0]);
+                break;
+            case 'C':
+                /* move cursor right */
+                if (vt->esc_params[0] == 0) {
+                    vt->esc_params[0] = 1;
+                }
+                vt100_set_cursor(vt, vt->x + vt->esc_params[0], vt->y);
+                break;
+            case 'D':
+                /* move cursor left */
+                if (vt->esc_params[0] == 0) {
+                    vt->esc_params[0] = 1;
+                }
+                vt100_set_cursor(vt, vt->x - vt->esc_params[0], vt->y);
+                break;
+            case 'G':
+                /* move cursor to column */
+                vt100_set_cursor(vt, vt->esc_params[0] - 1, vt->y);
+                break;
+            case 'f':
+            case 'H':
+                /* move cursor to row, column */
+                vt100_set_cursor(vt, vt->esc_params[1] - 1, vt->esc_params[0] - 1);
+                break;
+            case 'J':
+                switch (vt->esc_params[0]) {
+                case 0:
+                    /* clear to end of screen */
+                    for (y = vt->y; y < vt->height; y++) {
+                        for (x = 0; x < vt->width; x++) {
+                            if (y == vt->y && x < vt->x) {
+                                continue;
+                            }
+                            vt100_clear_xy(vt, x, y);
+                        }
+                    }
+                    break;
+                case 1:
+                    /* clear from beginning of screen */
+                    for (y = 0; y <= vt->y; y++) {
+                        for (x = 0; x < vt->width; x++) {
+                            if (y == vt->y && x > vt->x) {
+                                break;
+                            }
+                            vt100_clear_xy(vt, x, y);
+                        }
+                    }
+                    break;
+                case 2:
+                    /* clear entire screen */
+                    for (y = 0; y < vt->height; y++) {
+                        for (x = 0; x < vt->width; x++) {
+                            vt100_clear_xy(vt, x, y);
+                        }
+                    }
+                    break;
+                }
+                break;
+            case 'K':
+                switch (vt->esc_params[0]) {
+                case 0:
+                    /* clear to eol */
+                    for (x = vt->x; x < vt->width; x++) {
+                        vt100_clear_xy(vt, x, vt->y);
+                    }
+                    break;
+                case 1:
+                    /* clear from beginning of line */
+                    for (x = 0; x <= vt->x && x < vt->width; x++) {
+                        vt100_clear_xy(vt, x, vt->y);
+                    }
+                    break;
+                case 2:
+                    /* clear entire line */
+                    for (x = 0; x < vt->width; x++) {
+                        vt100_clear_xy(vt, x, vt->y);
+                    }
+                    break;
+                }
+                break;
+            case 'P':
+                vt100_csi_P(vt, vt->esc_params[0]);
+                break;
+            case 'm':
+                vt100_handle_escape(vt);
+                break;
+            case 'n':
+                switch (vt->esc_params[0]) {
+                case 5:
+                    /* report console status (always succeed)*/
+                    vt100_write(vt, "\033[0n", 4);
+                    break;
+                case 6:
+                    /* report cursor position */
+                    response = g_strdup_printf("\033[%d;%dR",
+                                               vt->y + 1, vt->x + 1);
+                    vt100_write(vt, response, strlen(response));
+                    break;
+                }
+                break;
+            case 's':
+                vt100_save_cursor(vt);
+                break;
+            case 'u':
+                vt100_restore_cursor(vt);
+                break;
+            case '@':
+                vt100_csi_at(vt, vt->esc_params[0]);
+                break;
+            default:
+                trace_console_putchar_unhandled(ch);
+                break;
+            }
+            break;
+        }
+        break;
+    case TTY_STATE_OSC: /* Operating System Command: ESC ] ... BEL/ST */
+        if (ch == '\a') {
+            /* BEL terminates OSC */
+            vt->state = TTY_STATE_NORM;
+        } else if (ch == 27) {
+            /* ESC might start ST (ESC \) */
+            vt->state = TTY_STATE_ESC;
+        }
+        /* All other bytes are silently consumed */
+        break;
+    case TTY_STATE_G0: /* set character sets */
+    case TTY_STATE_G1: /* set character sets */
+        switch (ch) {
+        case 'B':
+            /* Latin-1 map */
+            break;
+        }
+        vt->state = TTY_STATE_NORM;
+        break;
+    }
+
+}
+
+int vt100_input(QemuVT100 *vt, const uint8_t *buf, int len)
+{
+    int i;
+
+    vt->update_x0 = vt->width * FONT_WIDTH;
+    vt->update_y0 = vt->height * FONT_HEIGHT;
+    vt->update_x1 = 0;
+    vt->update_y1 = 0;
+    vt100_show_cursor(vt, 0);
+    for (i = 0; i < len; i++) {
+        vt100_putchar(vt, buf[i]);
+    }
+    vt100_show_cursor(vt, 1);
+    if (vt->update_x0 < vt->update_x1) {
+        vt100_image_update(vt, vt->update_x0, vt->update_y0,
+                           vt->update_x1 - vt->update_x0,
+                           vt->update_y1 - vt->update_y0);
+    }
+    return len;
+}
+
+void vt100_keysym(QemuVT100 *vt, int keysym)
+{
+    uint8_t buf[16], *q;
+    int c;
+
+    switch (keysym) {
+    case QEMU_KEY_CTRL_UP:
+        vt100_scroll(vt, -1);
+        break;
+    case QEMU_KEY_CTRL_DOWN:
+        vt100_scroll(vt, 1);
+        break;
+    case QEMU_KEY_CTRL_PAGEUP:
+        vt100_scroll(vt, -10);
+        break;
+    case QEMU_KEY_CTRL_PAGEDOWN:
+        vt100_scroll(vt, 10);
+        break;
+    default:
+        /* convert the QEMU keysym to VT100 key string */
+        q = buf;
+        if (keysym >= 0xe100 && keysym <= 0xe11f) {
+            *q++ = '\033';
+            *q++ = '[';
+            c = keysym - 0xe100;
+            if (c >= 10) {
+                *q++ = '0' + (c / 10);
+            }
+            *q++ = '0' + (c % 10);
+            *q++ = '~';
+        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
+            *q++ = '\033';
+            *q++ = '[';
+            *q++ = keysym & 0xff;
+        } else if (vt->echo && (keysym == '\r' || keysym == '\n')) {
+            vt100_input(vt, (uint8_t *)"\r", 1);
+            *q++ = '\n';
+        } else {
+            *q++ = keysym;
+        }
+        if (vt->echo) {
+            vt100_input(vt, buf, q - buf);
+        }
+        vt100_write(vt, buf, q - buf);
+        break;
+    }
+}
+
+void vt100_update_cursor(void)
+{
+    QemuVT100 *vt;
+
+    cursor_visible_phase = !cursor_visible_phase;
+
+    if (QTAILQ_EMPTY(&vt100s)) {
+        return;
+    }
+
+    QTAILQ_FOREACH(vt, &vt100s, list) {
+        vt100_refresh(vt);
+    }
+
+    timer_mod(cursor_timer,
+        qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
+}
+
+static void
+cursor_timer_cb(void *opaque)
+{
+    vt100_update_cursor();
+}
+
+void vt100_init(QemuVT100 *vt,
+                pixman_image_t *image,
+                void (*image_update)(QemuVT100 *vt, int x, int y, int w, int h),
+                void (*out_flush)(QemuVT100 *vt))
+{
+    if (!cursor_timer) {
+        cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);
+    }
+
+    QTAILQ_INSERT_HEAD(&vt100s, vt, list);
+    fifo8_create(&vt->out_fifo, 16);
+    vt->total_height = DEFAULT_BACKSCROLL;
+    vt->image_update = image_update;
+    vt->out_flush = out_flush;
+    /* set current text attributes to default */
+    vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+    vt100_set_image(vt, image);
+}
+
+void vt100_fini(QemuVT100 *vt)
+{
+    QTAILQ_REMOVE(&vt100s, vt, list);
+    fifo8_destroy(&vt->out_fifo);
+    g_free(vt->cells);
+}
diff --git a/ui/meson.build b/ui/meson.build
index 25657af50e7..9ece6f262b6 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -3,6 +3,7 @@ system_ss.add(png)
 system_ss.add(files(
   'clipboard.c',
   'console.c',
+  'cp437.c',
   'cursor.c',
   'dmabuf.c',
   'input-keymap.c',
@@ -16,8 +17,9 @@ system_ss.add(files(
   'ui-qmp-cmds.c',
   'util.c',
   'vgafont.c',
+  'vt100.c',
 ))
-system_ss.add(when: pixman, if_true: files('console-vc.c', 'cp437.c'), if_false: files('console-vc-stubs.c'))
+system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c'))
 if dbus_display
   system_ss.add(files('dbus-module.c'))
 endif

-- 
2.53.0



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

* [PATCH 36/60] util: move datadir.c from system/
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (34 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 35/60] ui/console-vc: move VT100 emulation into separate unit Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:27   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 37/60] ui: move DisplaySurface functions to display-surface.c Marc-André Lureau
                   ` (24 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

The datadir module provides general-purpose data file lookup
utilities that are not specific to system emulation. Move it
to util/ so it can be reused more broadly.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 {system => util}/datadir.c | 0
 system/meson.build         | 1 -
 system/trace-events        | 1 -
 util/meson.build           | 1 +
 util/trace-events          | 3 +++
 5 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/system/datadir.c b/util/datadir.c
similarity index 100%
rename from system/datadir.c
rename to util/datadir.c
diff --git a/system/meson.build b/system/meson.build
index 579e8353d53..9cdfe1b3e75 100644
--- a/system/meson.build
+++ b/system/meson.build
@@ -8,7 +8,6 @@ system_ss.add(files(
   'bootdevice.c',
   'cpus.c',
   'cpu-timers.c',
-  'datadir.c',
   'dirtylimit.c',
   'dma-helpers.c',
   'exit-with-parent.c',
diff --git a/system/trace-events b/system/trace-events
index 6d29a823f04..e6e1b612798 100644
--- a/system/trace-events
+++ b/system/trace-events
@@ -46,7 +46,6 @@ vm_stop_flush_all(int ret) "ret %d"
 
 # vl.c
 vm_state_notify(int running, int reason, const char *reason_str) "running %d reason %d (%s)"
-load_file(const char *name, const char *path) "name %s location %s"
 runstate_set(int current_state, const char *current_state_str, int new_state, const char *new_state_str) "current_run_state %d (%s) new_state %d (%s)"
 system_wakeup_request(int reason) "reason=%d"
 qemu_system_shutdown_request(int reason) "reason=%d"
diff --git a/util/meson.build b/util/meson.build
index 33132c04ad6..cd56fe2ffe4 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -30,6 +30,7 @@ util_ss.add(when: linux_io_uring, if_true: files('fdmon-io_uring.c'))
 if glib_has_gslice
   util_ss.add(files('qtree.c'))
 endif
+util_ss.add(files('datadir.c'))
 util_ss.add(files('defer-call.c'))
 util_ss.add(files('envlist.c', 'path.c', 'module.c'))
 util_ss.add(files('event.c'))
diff --git a/util/trace-events b/util/trace-events
index 540d6625073..df549646d12 100644
--- a/util/trace-events
+++ b/util/trace-events
@@ -114,3 +114,6 @@ uffd_unregister_memory_failed(void *addr, uint64_t length, int err) "addr: %p le
 # module.c
 module_load_module(const char *name) "file %s"
 module_lookup_object_type(const char *name) "name %s"
+
+# datadir.c
+load_file(const char *name, const char *path) "name %s location %s"

-- 
2.53.0



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

* [PATCH 37/60] ui: move DisplaySurface functions to display-surface.c
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (35 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 36/60] util: move datadir.c from system/ Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-17  8:50 ` [PATCH 38/60] ui: make qemu_default_pixelformat() static inline Marc-André Lureau
                   ` (23 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

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

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(-)

diff --git a/ui/console.c b/ui/console.c
index 7ffea2776ef..24794e5a9dc 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -507,102 +507,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 9ece6f262b6..bae6ea0d2dc 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -5,6 +5,7 @@ system_ss.add(files(
   'console.c',
   'cp437.c',
   'cursor.c',
+  'display-surface.c',
   'dmabuf.c',
   'input-keymap.c',
   'input-legacy.c',

-- 
2.53.0



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

* [PATCH 38/60] ui: make qemu_default_pixelformat() static inline
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (36 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 37/60] ui: move DisplaySurface functions to display-surface.c Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:28   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 39/60] ui: make unregister_displaychangelistener() skip unregistered Marc-André Lureau
                   ` (22 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

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.

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 006b1986bb9..3d71010c758 100644
--- a/include/ui/surface.h
+++ b/include/ui/surface.h
@@ -28,8 +28,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 24794e5a9dc..78b0ac27827 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1392,13 +1392,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.53.0



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

* [PATCH 39/60] ui: make unregister_displaychangelistener() skip unregistered
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (37 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 38/60] ui: make qemu_default_pixelformat() static inline Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:28   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 40/60] ui: minor code simplification Marc-André Lureau
                   ` (21 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

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

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 78b0ac27827..a9132e09eac 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -677,6 +677,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.53.0



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

* [PATCH 40/60] ui: minor code simplification
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (38 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 39/60] ui: make unregister_displaychangelistener() skip unregistered Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:30   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 41/60] system: make qemu_del_vm_change_state_handler accept NULL Marc-André Lureau
                   ` (20 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Remove the extra "info" variable and its initialization.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/sdl2.c | 10 +++++-----
 ui/vnc.c  |  7 ++-----
 2 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/ui/sdl2.c b/ui/sdl2.c
index aaaede56e0e..3ffb8acaff8 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -600,11 +600,11 @@ 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;
-            dpy_set_ui_info(scon->dcl.con, &info, true);
+            dpy_set_ui_info(scon->dcl.con,
+                &(QemuUIInfo) {
+                    .width = ev->window.data1,
+                    .height = ev->window.data2,
+                }, true);
         }
         sdl2_redraw(scon);
         break;
diff --git a/ui/vnc.c b/ui/vnc.c
index 4aa446a48d7..87ec3e6d799 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2638,11 +2638,8 @@ 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;
-            dpy_set_ui_info(vs->vd->dcl.con, &info, false);
+            dpy_set_ui_info(vs->vd->dcl.con,
+                &(QemuUIInfo){ .width = w, .height = h }, false);
             vnc_desktop_resize_ext(vs, 4 /* Request forwarded */);
         } else {
             vnc_desktop_resize_ext(vs, 3 /* Invalid screen layout */);

-- 
2.53.0



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

* [PATCH 41/60] system: make qemu_del_vm_change_state_handler accept NULL
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (39 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 40/60] ui: minor code simplification Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:31   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 42/60] ui/vnc: assert preconditions instead of silently returning Marc-André Lureau
                   ` (19 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

For convenience.

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

diff --git a/system/runstate.c b/system/runstate.c
index 77cb14ae028..7d8bdf62dd6 100644
--- a/system/runstate.c
+++ b/system/runstate.c
@@ -349,6 +349,9 @@ VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,
 
 void qemu_del_vm_change_state_handler(VMChangeStateEntry *e)
 {
+    if (!e) {
+        return;
+    }
     QTAILQ_REMOVE(&vm_change_state_head, e, entries);
     g_free(e);
 }

-- 
2.53.0



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

* [PATCH 42/60] ui/vnc: assert preconditions instead of silently returning
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (40 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 41/60] system: make qemu_del_vm_change_state_handler accept NULL Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:31   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 43/60] ui/vnc: simplify vnc_init_func error handling Marc-André Lureau
                   ` (18 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Replace defensive NULL guards with assert() in vnc_display_close()
and vnc_display_open(). These are internal functions whose callers
guarantee non-NULL arguments, so a NULL value would indicate a
programming error rather than a runtime condition.

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

diff --git a/ui/vnc.c b/ui/vnc.c
index 87ec3e6d799..605e5117b7f 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3464,9 +3464,7 @@ void vnc_display_init(const char *id, Error **errp)
 
 static void vnc_display_close(VncDisplay *vd)
 {
-    if (!vd) {
-        return;
-    }
+    assert(vd);
 
     if (vd->listener) {
         qio_net_listener_disconnect(vd->listener);
@@ -4067,15 +4065,10 @@ void vnc_display_open(const char *id, Error **errp)
     const char *audiodev;
     const char *passwordSecret;
 
-    if (!vd) {
-        error_setg(errp, "VNC display not active");
-        return;
-    }
-    vnc_display_close(vd);
+    assert(vd);
+    assert(opts);
 
-    if (!opts) {
-        return;
-    }
+    vnc_display_close(vd);
 
     reverse = qemu_opt_get_bool(opts, "reverse", false);
     if (vnc_display_get_addresses(opts, reverse, &saddr_list, &wsaddr_list,

-- 
2.53.0



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

* [PATCH 43/60] ui/vnc: simplify vnc_init_func error handling
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (41 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 42/60] ui/vnc: assert preconditions instead of silently returning Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:38   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 44/60] ui/vnc: VncDisplay.id is not const Marc-André Lureau
                   ` (17 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

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

diff --git a/ui/vnc.c b/ui/vnc.c
index 605e5117b7f..af5fb3c1551 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -4318,7 +4318,7 @@ void vnc_parse(const char *str)
 
 int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
 {
-    Error *local_err = NULL;
+    ERRP_GUARD();
     char *id = (char *)qemu_opts_id(opts);
 
     if (!id) {
@@ -4326,14 +4326,12 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
         id = vnc_auto_assign_id(opts);
     }
 
-    vnc_display_init(id, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
+    vnc_display_init(id, errp);
+    if (*errp) {
         return -1;
     }
-    vnc_display_open(id, &local_err);
-    if (local_err != NULL) {
-        error_propagate(errp, local_err);
+    vnc_display_open(id, errp);
+    if (*errp) {
         return -1;
     }
     return 0;

-- 
2.53.0



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

* [PATCH 44/60] ui/vnc: VncDisplay.id is not const
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (42 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 43/60] ui/vnc: simplify vnc_init_func error handling Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:39   ` Daniel P. Berrangé
  2026-03-17  8:50 ` [PATCH 45/60] ui/vnc: fix vnc_display_init() leak on failure Marc-André Lureau
                   ` (16 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

s/strdup/g_strdup to highlight the issue and be consistent with other
allocations.

The next patch is going to introduce vnc_display_free() to take care of
deallocating it.

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

diff --git a/ui/vnc.h b/ui/vnc.h
index ec8d0c91b57..c5d678ac31e 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -166,7 +166,7 @@ struct VncDisplay
     pixman_image_t *server;    /* vnc server surface */
     int true_width; /* server surface width before rounding up */
 
-    const char *id;
+    char *id;
     QTAILQ_ENTRY(VncDisplay) next;
     char *password;
     time_t expires;
diff --git a/ui/vnc.c b/ui/vnc.c
index af5fb3c1551..763b13acbde 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3430,7 +3430,7 @@ void vnc_display_init(const char *id, Error **errp)
     }
     vd = g_malloc0(sizeof(*vd));
 
-    vd->id = strdup(id);
+    vd->id = g_strdup(id);
     QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
 
     QTAILQ_INIT(&vd->clients);

-- 
2.53.0



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

* [PATCH 45/60] ui/vnc: fix vnc_display_init() leak on failure
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (43 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 44/60] ui/vnc: VncDisplay.id is not const Marc-André Lureau
@ 2026-03-17  8:50 ` Marc-André Lureau
  2026-03-24 14:47   ` Daniel P. Berrangé
  2026-03-17  8:51 ` [PATCH 46/60] ui/vnc: merge vnc_display_init() and vnc_display_open() Marc-André Lureau
                   ` (15 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:50 UTC (permalink / raw)
  To: qemu-devel

Do not add the display state to the vnc list, if the initialization
failed. Add vnc_display_free(), to free the display state and associated
data in such case. The function is meant to be public and reused in the
following changes.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/keymaps.h |  1 +
 ui/keymaps.c | 13 ++++++++++---
 ui/vnc.c     | 30 ++++++++++++++++++++++++++----
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/ui/keymaps.h b/ui/keymaps.h
index 3d52c0882a1..e8917e56404 100644
--- a/ui/keymaps.h
+++ b/ui/keymaps.h
@@ -54,6 +54,7 @@ typedef struct kbd_layout_t kbd_layout_t;
 
 kbd_layout_t *init_keyboard_layout(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);
 int keycode_is_keypad(kbd_layout_t *k, int keycode);
diff --git a/ui/keymaps.c b/ui/keymaps.c
index 2359dbfe7e6..d1b3f43dc8a 100644
--- a/ui/keymaps.c
+++ b/ui/keymaps.c
@@ -178,6 +178,14 @@ out:
     return ret;
 }
 
+void kbd_layout_free(kbd_layout_t *k)
+{
+    if (!k) {
+        return;
+    }
+    g_hash_table_unref(k->hash);
+    g_free(k);
+}
 
 kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
                                    const char *language, Error **errp)
@@ -185,10 +193,9 @@ kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
     kbd_layout_t *k;
 
     k = g_new0(kbd_layout_t, 1);
-    k->hash = g_hash_table_new(NULL, NULL);
+    k->hash = g_hash_table_new_full(NULL, NULL, NULL, g_free);
     if (parse_keyboard_layout(k, table, language, errp) < 0) {
-        g_hash_table_unref(k->hash);
-        g_free(k);
+        kbd_layout_free(k);
         return NULL;
     }
     return k;
diff --git a/ui/vnc.c b/ui/vnc.c
index 763b13acbde..115ff8a988e 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3421,6 +3421,8 @@ static void vmstate_change_handler(void *opaque, bool running, RunState state)
     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
 }
 
+static void vnc_display_free(VncDisplay *vd);
+
 void vnc_display_init(const char *id, Error **errp)
 {
     VncDisplay *vd;
@@ -3430,8 +3432,9 @@ void vnc_display_init(const char *id, Error **errp)
     }
     vd = g_malloc0(sizeof(*vd));
 
+    qemu_mutex_init(&vd->mutex);
     vd->id = g_strdup(id);
-    QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
+    vd->dcl.ops = &dcl_ops;
 
     QTAILQ_INIT(&vd->clients);
     vd->expires = TIME_MAX;
@@ -3445,22 +3448,22 @@ void vnc_display_init(const char *id, Error **errp)
     }
 
     if (!vd->kbd_layout) {
+        vnc_display_free(vd);
         return;
     }
 
     vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
     vd->connections_limit = 32;
 
-    qemu_mutex_init(&vd->mutex);
     vnc_start_worker_thread();
 
-    vd->dcl.ops = &dcl_ops;
     register_displaychangelistener(&vd->dcl);
     vd->kbd = qkbd_state_init(vd->dcl.con);
     vd->vmstate_handler_entry = qemu_add_vm_change_state_handler(
         &vmstate_change_handler, vd);
-}
 
+    QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
+}
 
 static void vnc_display_close(VncDisplay *vd)
 {
@@ -3504,6 +3507,25 @@ static void vnc_display_close(VncDisplay *vd)
 #endif
 }
 
+static void vnc_display_free(VncDisplay *vd)
+{
+    if (!vd) {
+        return;
+    }
+    vnc_display_close(vd);
+    unregister_displaychangelistener(&vd->dcl);
+    qkbd_state_free(vd->kbd);
+    qemu_del_vm_change_state_handler(vd->vmstate_handler_entry);
+    kbd_layout_free(vd->kbd_layout);
+    qemu_mutex_destroy(&vd->mutex);
+    if (QTAILQ_IN_USE(vd, next)) {
+        QTAILQ_REMOVE(&vnc_displays, vd, next);
+    }
+    g_free(vd->id);
+    g_free(vd);
+}
+
+
 int vnc_display_password(const char *id, const char *password, Error **errp)
 {
     VncDisplay *vd = vnc_display_find(id);

-- 
2.53.0



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

* [PATCH 46/60] ui/vnc: merge vnc_display_init() and vnc_display_open()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (44 preceding siblings ...)
  2026-03-17  8:50 ` [PATCH 45/60] ui/vnc: fix vnc_display_init() leak on failure Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-24 14:51   ` Daniel P. Berrangé
  2026-03-17  8:51 ` [PATCH 47/60] ui/vnc: report an error for duplicate display id Marc-André Lureau
                   ` (14 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

Combine the two-step vnc_display_init()/vnc_display_open() sequence
into a single vnc_display_new() function that returns VncDisplay*.
This simplifies the API by making vnc_display_open() an
internal detail and will allow further code simplification.

vnc_display_new() is moved to vnc.h, since it returns VncDisplay* now.
Add vnc_display_free() for consistency, and it will be later used.

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

diff --git a/include/ui/console.h b/include/ui/console.h
index 152333d60fc..737ceba3890 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -448,8 +448,6 @@ const char *qemu_display_get_vc(DisplayOptions *opts);
 void qemu_display_help(void);
 
 /* vnc.c */
-void vnc_display_init(const char *id, Error **errp);
-void vnc_display_open(const char *id, Error **errp);
 void vnc_display_add_client(const char *id, int csock, bool skipauth);
 int vnc_display_password(const char *id, const char *password, Error **errp);
 int vnc_display_pw_expire(const char *id, time_t expires);
diff --git a/ui/vnc.h b/ui/vnc.h
index c5d678ac31e..6afe0f16d12 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -548,6 +548,9 @@ enum VncFeatures {
 #define VNC_CLIPBOARD_NOTIFY   (1 << 27)
 #define VNC_CLIPBOARD_PROVIDE  (1 << 28)
 
+VncDisplay *vnc_display_new(const char *id, Error **errp);
+void vnc_display_free(VncDisplay *vd);
+
 /*****************************************************************************
  *
  * Internal APIs
diff --git a/ui/vnc.c b/ui/vnc.c
index 115ff8a988e..03b99c9e590 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3421,14 +3421,14 @@ static void vmstate_change_handler(void *opaque, bool running, RunState state)
     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
 }
 
-static void vnc_display_free(VncDisplay *vd);
+static bool vnc_display_open(VncDisplay *vd, Error **errp);
 
-void vnc_display_init(const char *id, Error **errp)
+VncDisplay *vnc_display_new(const char *id, Error **errp)
 {
     VncDisplay *vd;
 
     if (vnc_display_find(id) != NULL) {
-        return;
+        return NULL;
     }
     vd = g_malloc0(sizeof(*vd));
 
@@ -3449,7 +3449,7 @@ void vnc_display_init(const char *id, Error **errp)
 
     if (!vd->kbd_layout) {
         vnc_display_free(vd);
-        return;
+        return NULL;
     }
 
     vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
@@ -3462,7 +3462,13 @@ void vnc_display_init(const char *id, Error **errp)
     vd->vmstate_handler_entry = qemu_add_vm_change_state_handler(
         &vmstate_change_handler, vd);
 
+    if (!vnc_display_open(vd, errp)) {
+        vnc_display_free(vd);
+        return NULL;
+    }
+
     QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
+    return vd;
 }
 
 static void vnc_display_close(VncDisplay *vd)
@@ -3507,7 +3513,7 @@ static void vnc_display_close(VncDisplay *vd)
 #endif
 }
 
-static void vnc_display_free(VncDisplay *vd)
+void vnc_display_free(VncDisplay *vd)
 {
     if (!vd) {
         return;
@@ -3525,7 +3531,6 @@ static void vnc_display_free(VncDisplay *vd)
     g_free(vd);
 }
 
-
 int vnc_display_password(const char *id, const char *password, Error **errp)
 {
     VncDisplay *vd = vnc_display_find(id);
@@ -4068,10 +4073,9 @@ bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp)
     return true;
 }
 
-void vnc_display_open(const char *id, Error **errp)
+static bool vnc_display_open(VncDisplay *vd, Error **errp)
 {
-    VncDisplay *vd = vnc_display_find(id);
-    QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
+    QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, vd->id);
     g_autoptr(SocketAddressList) saddr_list = NULL;
     g_autoptr(SocketAddressList) wsaddr_list = NULL;
     const char *share, *device_id;
@@ -4090,26 +4094,23 @@ void vnc_display_open(const char *id, Error **errp)
     assert(vd);
     assert(opts);
 
-    vnc_display_close(vd);
-
     reverse = qemu_opt_get_bool(opts, "reverse", false);
     if (vnc_display_get_addresses(opts, reverse, &saddr_list, &wsaddr_list,
                                   errp) < 0) {
-        goto fail;
+        return false;
     }
 
-
     passwordSecret = qemu_opt_get(opts, "password-secret");
     if (passwordSecret) {
         if (qemu_opt_get(opts, "password")) {
             error_setg(errp,
                        "'password' flag is redundant with 'password-secret'");
-            goto fail;
+            return false;
         }
         vd->password = qcrypto_secret_lookup_as_utf8(passwordSecret,
                                                      errp);
         if (!vd->password) {
-            goto fail;
+            return false;
         }
         password = true;
     } else {
@@ -4120,7 +4121,7 @@ void vnc_display_open(const char *id, Error **errp)
                 QCRYPTO_CIPHER_ALGO_DES, QCRYPTO_CIPHER_MODE_ECB)) {
             error_setg(errp,
                        "Cipher backend does not support DES algorithm");
-            goto fail;
+            return false;
         }
     }
 
@@ -4130,7 +4131,7 @@ void vnc_display_open(const char *id, Error **errp)
 #ifndef CONFIG_VNC_SASL
     if (sasl) {
         error_setg(errp, "VNC SASL auth requires cyrus-sasl support");
-        goto fail;
+        return false;
     }
 #endif /* CONFIG_VNC_SASL */
     credid = qemu_opt_get(opts, "tls-creds");
@@ -4141,7 +4142,7 @@ void vnc_display_open(const char *id, Error **errp)
         if (!creds) {
             error_setg(errp, "No TLS credentials with id '%s'",
                        credid);
-            goto fail;
+            return false;
         }
         vd->tlscreds = (QCryptoTLSCreds *)
             object_dynamic_cast(creds,
@@ -4149,26 +4150,26 @@ void vnc_display_open(const char *id, Error **errp)
         if (!vd->tlscreds) {
             error_setg(errp, "Object with id '%s' is not TLS credentials",
                        credid);
-            goto fail;
+            return false;
         }
         object_ref(OBJECT(vd->tlscreds));
 
         if (!qcrypto_tls_creds_check_endpoint(vd->tlscreds,
                                               QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
                                               errp)) {
-            goto fail;
+            return false;
         }
     }
     tlsauthz = qemu_opt_get(opts, "tls-authz");
     if (tlsauthz && !vd->tlscreds) {
         error_setg(errp, "'tls-authz' provided but TLS is not enabled");
-        goto fail;
+        return false;
     }
 
     saslauthz = qemu_opt_get(opts, "sasl-authz");
     if (saslauthz && !sasl) {
         error_setg(errp, "'sasl-authz' provided but SASL auth is not enabled");
-        goto fail;
+        return false;
     }
 
     share = qemu_opt_get(opts, "share");
@@ -4181,7 +4182,7 @@ void vnc_display_open(const char *id, Error **errp)
             vd->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
         } else {
             error_setg(errp, "unknown vnc share= option");
-            goto fail;
+            return false;
         }
     } else {
         vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
@@ -4215,20 +4216,20 @@ void vnc_display_open(const char *id, Error **errp)
     if (vnc_display_setup_auth(&vd->auth, &vd->subauth,
                                vd->tlscreds, password,
                                sasl, false, errp) < 0) {
-        goto fail;
+        return false;
     }
     trace_vnc_auth_init(vd, 0, vd->auth, vd->subauth);
 
     if (vnc_display_setup_auth(&vd->ws_auth, &vd->ws_subauth,
                                vd->tlscreds, password,
                                sasl, true, errp) < 0) {
-        goto fail;
+        return false;
     }
     trace_vnc_auth_init(vd, 1, vd->ws_auth, vd->ws_subauth);
 
 #ifdef CONFIG_VNC_SASL
     if (sasl && !vnc_sasl_server_init(errp)) {
-        goto fail;
+        return false;
     }
 #endif
     vd->lock_key_sync = lock_key_sync;
@@ -4241,7 +4242,7 @@ void vnc_display_open(const char *id, Error **errp)
     if (audiodev) {
         vd->audio_be = audio_be_by_name(audiodev, errp);
         if (!vd->audio_be) {
-            goto fail;
+            return false;
         }
     } else {
         vd->audio_be = audio_get_default_audio_be(NULL);
@@ -4255,7 +4256,7 @@ void vnc_display_open(const char *id, Error **errp)
         con = qemu_console_lookup_by_device_name(device_id, head, &err);
         if (err) {
             error_propagate(errp, err);
-            goto fail;
+            return false;
         }
     } else {
         con = qemu_console_lookup_default();
@@ -4271,16 +4272,16 @@ void vnc_display_open(const char *id, Error **errp)
     qkbd_state_set_delay(vd->kbd, key_delay_ms);
 
     if (saddr_list == NULL) {
-        return;
+        return true;
     }
 
     if (reverse) {
         if (vnc_display_connect(vd, saddr_list, wsaddr_list, errp) < 0) {
-            goto fail;
+            return false;
         }
     } else {
         if (vnc_display_listen(vd, saddr_list, wsaddr_list, errp) < 0) {
-            goto fail;
+            return false;
         }
     }
 
@@ -4288,11 +4289,7 @@ void vnc_display_open(const char *id, Error **errp)
         vnc_display_print_local_addr(vd);
     }
 
-    /* Success */
-    return;
-
-fail:
-    vnc_display_close(vd);
+    return true;
 }
 
 void vnc_display_add_client(const char *id, int csock, bool skipauth)
@@ -4348,15 +4345,7 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
         id = vnc_auto_assign_id(opts);
     }
 
-    vnc_display_init(id, errp);
-    if (*errp) {
-        return -1;
-    }
-    vnc_display_open(id, errp);
-    if (*errp) {
-        return -1;
-    }
-    return 0;
+    return vnc_display_new(id, errp) != NULL ? 0 : -1;
 }
 
 static void vnc_register_config(void)

-- 
2.53.0



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

* [PATCH 47/60] ui/vnc: report an error for duplicate display id
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (45 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 46/60] ui/vnc: merge vnc_display_init() and vnc_display_open() Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-24 14:52   ` Daniel P. Berrangé
  2026-03-17  8:51 ` [PATCH 48/60] ui/vnc: defer listener registration until the console is known Marc-André Lureau
                   ` (13 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

Returning NULL without setting an error is odd.

Note that we could make this a programming error too, as the duplicate
check is also done during QemuOpt parsing.

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

diff --git a/ui/vnc.c b/ui/vnc.c
index 03b99c9e590..2c53c92914b 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3428,6 +3428,7 @@ VncDisplay *vnc_display_new(const char *id, Error **errp)
     VncDisplay *vd;
 
     if (vnc_display_find(id) != NULL) {
+        error_setg(errp, "Display '%s' already exists", id);
         return NULL;
     }
     vd = g_malloc0(sizeof(*vd));

-- 
2.53.0



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

* [PATCH 48/60] ui/vnc: defer listener registration until the console is known
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (46 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 47/60] ui/vnc: report an error for duplicate display id Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-24 14:53   ` Daniel P. Berrangé
  2026-03-17  8:51 ` [PATCH 49/60] ui/vnc: explicitly link with png Marc-André Lureau
                   ` (12 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

Previously, the display change listener was registered early in
vnc_display_new() without a console, requiring vnc_display_open() to
conditionally unregister and re-register it when the actual console was
resolved. Since vnc_display_new() and vnc_display_open() were merged in
the previous commit, simply delay the registration and keyboard state
initialization to vnc_display_open(), after the console has been looked
up. This removes the conditional re-registration and simplifies the code.

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

diff --git a/ui/vnc.c b/ui/vnc.c
index 2c53c92914b..c38be7113f1 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3458,8 +3458,6 @@ VncDisplay *vnc_display_new(const char *id, Error **errp)
 
     vnc_start_worker_thread();
 
-    register_displaychangelistener(&vd->dcl);
-    vd->kbd = qkbd_state_init(vd->dcl.con);
     vd->vmstate_handler_entry = qemu_add_vm_change_state_handler(
         &vmstate_change_handler, vd);
 
@@ -4263,13 +4261,9 @@ static bool vnc_display_open(VncDisplay *vd, Error **errp)
         con = qemu_console_lookup_default();
     }
 
-    if (con != vd->dcl.con) {
-        qkbd_state_free(vd->kbd);
-        unregister_displaychangelistener(&vd->dcl);
-        vd->dcl.con = con;
-        register_displaychangelistener(&vd->dcl);
-        vd->kbd = qkbd_state_init(vd->dcl.con);
-    }
+    vd->dcl.con = con;
+    register_displaychangelistener(&vd->dcl);
+    vd->kbd = qkbd_state_init(vd->dcl.con);
     qkbd_state_set_delay(vd->kbd, key_delay_ms);
 
     if (saddr_list == NULL) {

-- 
2.53.0



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

* [PATCH 49/60] ui/vnc: explicitly link with png
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (47 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 48/60] ui/vnc: defer listener registration until the console is known Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-24 14:56   ` Daniel P. Berrangé
  2026-03-17  8:51 ` [PATCH 50/60] ui/vnc: add vnc-system unit, to allow different implementations Marc-André Lureau
                   ` (11 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

The VNC code uses PNG directly.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/meson.build | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ui/meson.build b/ui/meson.build
index bae6ea0d2dc..b31ddf96282 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -45,7 +45,7 @@ vnc_ss.add(files(
   'vnc-jobs.c',
   'vnc-clipboard.c',
 ))
-vnc_ss.add(zlib, jpeg)
+vnc_ss.add(zlib, jpeg, png)
 vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c'))
 system_ss.add_all(when: [vnc, pixman], if_true: vnc_ss)
 system_ss.add(when: vnc, if_false: files('vnc-stubs.c'))

-- 
2.53.0



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

* [PATCH 50/60] ui/vnc: add vnc-system unit, to allow different implementations
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (48 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 49/60] ui/vnc: explicitly link with png Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-17  8:51 ` [PATCH 51/60] ui/console: remove qemu_console_is_visible() Marc-André Lureau
                   ` (10 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

The qemu-vnc server will want to signal the XVP requests, let it
have its own implementation.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/vnc.h        |  4 ++++
 ui/vnc-system.c | 19 +++++++++++++++++++
 ui/vnc.c        |  4 ++--
 ui/meson.build  |  2 +-
 4 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/ui/vnc.h b/ui/vnc.h
index 6afe0f16d12..1db9747599c 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -648,4 +648,8 @@ void vnc_server_cut_text_caps(VncState *vs);
 void vnc_client_cut_text(VncState *vs, size_t len, uint8_t *text);
 void vnc_client_cut_text_ext(VncState *vs, int32_t len, uint32_t flags, uint8_t *data);
 
+/* XVP events */
+void vnc_action_shutdown(VncState *vs);
+void vnc_action_reset(VncState *vs);
+
 #endif /* QEMU_VNC_H */
diff --git a/ui/vnc-system.c b/ui/vnc-system.c
new file mode 100644
index 00000000000..0632885f655
--- /dev/null
+++ b/ui/vnc-system.c
@@ -0,0 +1,19 @@
+/*
+ * QEMU VNC display driver
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+
+#include "ui/vnc.h"
+#include "system/runstate.h"
+
+void vnc_action_shutdown(VncState *vs)
+{
+    qemu_system_powerdown_request();
+}
+
+void vnc_action_reset(VncState *vs)
+{
+    qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET);
+}
diff --git a/ui/vnc.c b/ui/vnc.c
index c38be7113f1..fe2659a39ca 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2520,13 +2520,13 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
 
             switch (action) {
             case VNC_XVP_ACTION_SHUTDOWN:
-                qemu_system_powerdown_request();
+                vnc_action_shutdown(vs);
                 break;
             case VNC_XVP_ACTION_REBOOT:
                 send_xvp_message(vs, VNC_XVP_CODE_FAIL);
                 break;
             case VNC_XVP_ACTION_RESET:
-                qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET);
+                vnc_action_reset(vs);
                 break;
             default:
                 send_xvp_message(vs, VNC_XVP_CODE_FAIL);
diff --git a/ui/meson.build b/ui/meson.build
index b31ddf96282..f959f8972b5 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -48,7 +48,7 @@ vnc_ss.add(files(
 vnc_ss.add(zlib, jpeg, png)
 vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c'))
 system_ss.add_all(when: [vnc, pixman], if_true: vnc_ss)
-system_ss.add(when: vnc, if_false: files('vnc-stubs.c'))
+system_ss.add(when: vnc, if_true: files('vnc-system.c'), if_false: files('vnc-stubs.c'))
 
 ui_modules = {}
 

-- 
2.53.0



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

* [PATCH 51/60] ui/console: remove qemu_console_is_visible()
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (49 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 50/60] ui/vnc: add vnc-system unit, to allow different implementations Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-24 14:57   ` Daniel P. Berrangé
  2026-03-17  8:51 ` [PATCH 52/60] ui/console: simplify registering display/console change listener Marc-André Lureau
                   ` (9 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

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.

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 737ceba3890..3189788fb5f 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -392,7 +392,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 f8855753e30..4f731b4f9ce 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 a9132e09eac..baffdeb22b1 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -650,9 +650,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));
@@ -680,9 +677,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);
@@ -747,9 +741,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) {
@@ -848,9 +839,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;
@@ -866,9 +854,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;
@@ -884,9 +869,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;
@@ -906,9 +888,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;
@@ -927,9 +906,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;
@@ -1287,11 +1263,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.53.0



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

* [PATCH 52/60] ui/console: simplify registering display/console change listener
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (50 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 51/60] ui/console: remove qemu_console_is_visible() Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-17  8:51 ` [PATCH 53/60] ui/console: return completion status from gfx_update callback Marc-André Lureau
                   ` (8 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

Introduce qemu_console_register_listener() which combines setting
dcl->con, dcl->ops and calling register_displaychangelistener() into a
single call. This removes repetitive boilerplate across all display
backends and makes it harder to forget setting one of the fields.

Also move the early-return check in unregister_displaychangelistener()
before the trace call, so that unregistering a never-registered listener
(e.g. on error paths) does not dereference a NULL ops pointer.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h |  6 ++++--
 hw/display/qxl.c     |  4 +---
 ui/console.c         | 11 ++++++++---
 ui/curses.c          |  9 +++------
 ui/dbus-console.c    |  6 ++----
 ui/dbus-listener.c   | 27 ++++++++-------------------
 ui/egl-headless.c    |  4 +---
 ui/gtk.c             | 10 ++++------
 ui/sdl2.c            |  8 +++-----
 ui/spice-display.c   |  8 +++-----
 ui/vnc.c             | 11 ++++-------
 ui/cocoa.m           | 13 ++++---------
 12 files changed, 45 insertions(+), 72 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 3189788fb5f..c695b433fe3 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -305,10 +305,12 @@ struct DisplayGLCtx {
 
 DisplayState *init_displaystate(void);
 
-void register_displaychangelistener(DisplayChangeListener *dcl);
+void qemu_console_register_listener(QemuConsole *con,
+                                    DisplayChangeListener *dcl,
+                                    const DisplayChangeListenerOps *ops);
 void update_displaychangelistener(DisplayChangeListener *dcl,
                                   uint64_t interval);
-void unregister_displaychangelistener(DisplayChangeListener *dcl);
+void qemu_console_unregister_listener(DisplayChangeListener *dcl);
 
 bool dpy_ui_info_supported(const QemuConsole *con);
 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con);
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 3d4b5635568..02e8c1435be 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -2252,9 +2252,7 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp)
         return;
     }
 
-    qxl->ssd.dcl.ops = &display_listener_ops;
-    qxl->ssd.dcl.con = vga->con;
-    register_displaychangelistener(&qxl->ssd.dcl);
+    qemu_console_register_listener(vga->con, &qxl->ssd.dcl, &display_listener_ops);
 }
 
 static void qxl_realize_secondary(PCIDevice *dev, Error **errp)
diff --git a/ui/console.c b/ui/console.c
index baffdeb22b1..3ab987add34 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -642,10 +642,15 @@ dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con)
     }
 }
 
-void register_displaychangelistener(DisplayChangeListener *dcl)
+void qemu_console_register_listener(QemuConsole *con,
+                                    DisplayChangeListener *dcl,
+                                    const DisplayChangeListenerOps *ops)
 {
     assert(!dcl->ds);
 
+    dcl->con = con;
+    dcl->ops = ops;
+
     trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
     dcl->ds = get_alloc_displaystate();
     QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
@@ -670,10 +675,10 @@ void update_displaychangelistener(DisplayChangeListener *dcl,
     }
 }
 
-void unregister_displaychangelistener(DisplayChangeListener *dcl)
+void qemu_console_unregister_listener(DisplayChangeListener *dcl)
 {
     DisplayState *ds = dcl->ds;
-    trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
+    trace_displaychangelistener_unregister(dcl, dcl->ops ? dcl->ops->dpy_name : NULL);
     if (!ds) {
         return;
     }
diff --git a/ui/curses.c b/ui/curses.c
index 78f21d940e3..4e2a0b25955 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -324,9 +324,8 @@ static void curses_refresh(DisplayChangeListener *dcl)
                         if (con) {
                             erase();
                             wnoutrefresh(stdscr);
-                            unregister_displaychangelistener(dcl);
-                            dcl->con = con;
-                            register_displaychangelistener(dcl);
+                            qemu_console_unregister_listener(dcl);
+                            qemu_console_register_listener(con, dcl, dcl->ops);
 
                             invalidate = 1;
                         }
@@ -805,9 +804,7 @@ static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
     curses_winch_init();
 
     dcl = g_new0(DisplayChangeListener, 1);
-    dcl->con = qemu_console_lookup_default();
-    dcl->ops = &dcl_ops;
-    register_displaychangelistener(dcl);
+    qemu_console_register_listener(qemu_console_lookup_default(), dcl, &dcl_ops);
 
     invalidate = 1;
 }
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index 85e215ef233..249760d82aa 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -143,7 +143,6 @@ dbus_display_console_init(DBusDisplayConsole *object)
     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
 
     ddc->listeners = g_ptr_array_new_with_free_func(g_object_unref);
-    ddc->dcl.ops = &dbus_console_dcl_ops;
 }
 
 static void
@@ -151,7 +150,7 @@ dbus_display_console_dispose(GObject *object)
 {
     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
 
-    unregister_displaychangelistener(&ddc->dcl);
+    qemu_console_unregister_listener(&ddc->dcl);
     g_clear_object(&ddc->iface_touch);
     g_clear_object(&ddc->iface_mouse);
     g_clear_object(&ddc->iface_kbd);
@@ -553,7 +552,6 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
                         "g-object-path", path,
                         NULL);
     ddc->display = display;
-    ddc->dcl.con = con;
     /* handle errors, and skip non graphics? */
     qemu_console_fill_device_address(
         con, device_addr, sizeof(device_addr), NULL);
@@ -611,7 +609,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
         slot->tracking_id = -1;
     }
 
-    register_displaychangelistener(&ddc->dcl);
+    qemu_console_register_listener(con, &ddc->dcl, &dbus_console_dcl_ops);
     ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change;
     qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
     dbus_mouse_update_is_absolute(ddc);
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 3e2b4adf41f..45b8cc74a6b 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -956,7 +956,7 @@ dbus_display_listener_dispose(GObject *object)
 {
     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
 
-    unregister_displaychangelistener(&ddl->dcl);
+    qemu_console_unregister_listener(&ddl->dcl);
     g_clear_object(&ddl->conn);
     g_clear_pointer(&ddl->bus_name, g_free);
     g_clear_object(&ddl->proxy);
@@ -977,28 +977,12 @@ dbus_display_listener_dispose(GObject *object)
     G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
 }
 
-static void
-dbus_display_listener_constructed(GObject *object)
-{
-    DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
-
-    ddl->dcl.ops = &dbus_dcl_ops;
-#ifdef CONFIG_OPENGL
-    if (display_opengl) {
-        ddl->dcl.ops = &dbus_gl_dcl_ops;
-    }
-#endif
-
-    G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object);
-}
-
 static void
 dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS(klass);
 
     object_class->dispose = dbus_display_listener_dispose;
-    object_class->constructed = dbus_display_listener_constructed;
 }
 
 static void
@@ -1241,6 +1225,7 @@ dbus_display_listener_new(const char *bus_name,
                           GDBusConnection *conn,
                           DBusDisplayConsole *console)
 {
+    const DisplayChangeListenerOps *ops = &dbus_dcl_ops;
     DBusDisplayListener *ddl;
     QemuConsole *con;
     g_autoptr(GError) err = NULL;
@@ -1272,8 +1257,12 @@ dbus_display_listener_new(const char *bus_name,
 
     con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
     assert(con);
-    ddl->dcl.con = con;
-    register_displaychangelistener(&ddl->dcl);
+#ifdef CONFIG_OPENGL
+    if (display_opengl) {
+        ops = &dbus_gl_dcl_ops;
+    }
+#endif
+    qemu_console_register_listener(con, &ddl->dcl, ops);
 
     return ddl;
 }
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index 352b30b43fb..4f046c975a9 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -229,13 +229,11 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
         }
 
         edpy = g_new0(egl_dpy, 1);
-        edpy->dcl.con = con;
-        edpy->dcl.ops = &egl_ops;
         edpy->gls = qemu_gl_init_shader();
         ctx = g_new0(DisplayGLCtx, 1);
         ctx->ops = &eglctx_ops;
         qemu_console_set_display_gl_ctx(con, ctx);
-        register_displaychangelistener(&edpy->dcl);
+        qemu_console_register_listener(con, &edpy->dcl, &egl_ops);
     }
 }
 
diff --git a/ui/gtk.c b/ui/gtk.c
index 9ebe7e8df0d..3aaa44ff3e2 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -2251,6 +2251,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
                               QemuConsole *con, int idx,
                               GSList *group, GtkWidget *view_menu)
 {
+    const DisplayChangeListenerOps *ops = &dcl_ops;
     bool zoom_to_fit = false;
     int i;
 
@@ -2275,7 +2276,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
             vc->gfx.drawing_area = gtk_gl_area_new();
             g_signal_connect(vc->gfx.drawing_area, "realize",
                              G_CALLBACK(gl_area_realize), vc);
-            vc->gfx.dcl.ops = &dcl_gl_area_ops;
+            ops = &dcl_gl_area_ops;
             vc->gfx.dgc.ops = &gl_area_ctx_ops;
         } else {
 #ifdef CONFIG_X11
@@ -2290,7 +2291,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
             gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
 #pragma GCC diagnostic pop
-            vc->gfx.dcl.ops = &dcl_egl_ops;
+            ops = &dcl_egl_ops;
             vc->gfx.dgc.ops = &egl_ctx_ops;
             vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
 #else
@@ -2301,7 +2302,6 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
 #endif
     {
         vc->gfx.drawing_area = gtk_drawing_area_new();
-        vc->gfx.dcl.ops = &dcl_ops;
     }
 
 
@@ -2325,12 +2325,10 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
                              vc->tab_item, gtk_label_new(vc->label));
 
     vc->gfx.kbd = qkbd_state_init(con);
-    vc->gfx.dcl.con = con;
-
     if (display_opengl) {
         qemu_console_set_display_gl_ctx(con, &vc->gfx.dgc);
     }
-    register_displaychangelistener(&vc->gfx.dcl);
+    qemu_console_register_listener(con, &vc->gfx.dcl, ops);
 
     gd_connect_vc_gfx_signals(vc);
     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 3ffb8acaff8..105e1ee9399 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -934,6 +934,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
     sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs);
     for (i = 0; i < sdl2_num_outputs; i++) {
         QemuConsole *con = qemu_console_lookup_by_index(i);
+        const DisplayChangeListenerOps *ops = &dcl_2d_ops;
         assert(con != NULL);
         if (!qemu_console_is_graphic(con) &&
             qemu_console_get_index(con) != 0) {
@@ -943,13 +944,11 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
         sdl2_console[i].opts = o;
 #ifdef CONFIG_OPENGL
         sdl2_console[i].opengl = display_opengl;
-        sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
         sdl2_console[i].dgc.ops = display_opengl ? &gl_ctx_ops : NULL;
+        ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
 #else
         sdl2_console[i].opengl = 0;
-        sdl2_console[i].dcl.ops = &dcl_2d_ops;
 #endif
-        sdl2_console[i].dcl.con = con;
         sdl2_console[i].kbd = qkbd_state_init(con);
 #ifdef CONFIG_OPENGL
         if (display_opengl) {
@@ -957,8 +956,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
             sdl2_gl_console_init(&sdl2_console[i]);
         }
 #endif
-        register_displaychangelistener(&sdl2_console[i].dcl);
-
+        qemu_console_register_listener(con, &sdl2_console[i].dcl, ops);
 #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)
         if (SDL_GetWindowWMInfo(sdl2_console[i].real_window, &info)) {
 #if defined(SDL_VIDEO_DRIVER_WINDOWS)
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 5052f371f44..44e8637ea4f 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -1380,13 +1380,13 @@ static void qemu_spice_display_init_one(QemuConsole *con)
     SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
     Error *err = NULL;
     char device_address[256] = "";
+    const DisplayChangeListenerOps *ops = &display_listener_ops;
 
     qemu_spice_display_init_common(ssd);
 
-    ssd->dcl.ops = &display_listener_ops;
 #ifdef HAVE_SPICE_GL
     if (spice_opengl) {
-        ssd->dcl.ops = &display_listener_gl_ops;
+        ops = &display_listener_gl_ops;
         ssd->dgc.ops = &gl_ctx_ops;
         ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd);
         ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
@@ -1396,8 +1396,6 @@ static void qemu_spice_display_init_one(QemuConsole *con)
         ssd->have_scanout = false;
     }
 #endif
-    ssd->dcl.con = con;
-
     ssd->qxl.base.sif = &dpy_interface.base;
     qemu_spice_add_display_interface(&ssd->qxl, con);
 
@@ -1415,7 +1413,7 @@ static void qemu_spice_display_init_one(QemuConsole *con)
     if (spice_opengl) {
         qemu_console_set_display_gl_ctx(con, &ssd->dgc);
     }
-    register_displaychangelistener(&ssd->dcl);
+    qemu_console_register_listener(con, &ssd->dcl, ops);
 }
 
 void qemu_spice_display_init(void)
diff --git a/ui/vnc.c b/ui/vnc.c
index fe2659a39ca..9daf295a763 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1858,10 +1858,9 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
             qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) {
             QemuConsole *con = qemu_console_lookup_by_index(qcode - Q_KEY_CODE_1);
             if (con) {
-                unregister_displaychangelistener(&vs->vd->dcl);
+                qemu_console_unregister_listener(&vs->vd->dcl);
                 qkbd_state_switch_console(vs->vd->kbd, con);
-                vs->vd->dcl.con = con;
-                register_displaychangelistener(&vs->vd->dcl);
+                qemu_console_register_listener(con, &vs->vd->dcl, vs->vd->dcl.ops);
             }
             return;
         }
@@ -3435,7 +3434,6 @@ VncDisplay *vnc_display_new(const char *id, Error **errp)
 
     qemu_mutex_init(&vd->mutex);
     vd->id = g_strdup(id);
-    vd->dcl.ops = &dcl_ops;
 
     QTAILQ_INIT(&vd->clients);
     vd->expires = TIME_MAX;
@@ -3518,7 +3516,7 @@ void vnc_display_free(VncDisplay *vd)
         return;
     }
     vnc_display_close(vd);
-    unregister_displaychangelistener(&vd->dcl);
+    qemu_console_unregister_listener(&vd->dcl);
     qkbd_state_free(vd->kbd);
     qemu_del_vm_change_state_handler(vd->vmstate_handler_entry);
     kbd_layout_free(vd->kbd_layout);
@@ -4261,8 +4259,7 @@ static bool vnc_display_open(VncDisplay *vd, Error **errp)
         con = qemu_console_lookup_default();
     }
 
-    vd->dcl.con = con;
-    register_displaychangelistener(&vd->dcl);
+    qemu_console_register_listener(con, &vd->dcl, &dcl_ops);
     vd->kbd = qkbd_state_init(vd->dcl.con);
     qkbd_state_set_delay(vd->kbd, key_delay_ms);
 
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 9093d1e408f..aaf82421589 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -93,9 +93,7 @@ static void cocoa_switch(DisplayChangeListener *dcl,
     .dpy_mouse_set = cocoa_mouse_set,
     .dpy_cursor_define = cocoa_cursor_define,
 };
-static DisplayChangeListener dcl = {
-    .ops = &dcl_ops,
-};
+static DisplayChangeListener dcl;
 static QKbdState *kbd;
 static int cursor_hide = 1;
 static int left_command_key_enabled = 1;
@@ -425,8 +423,7 @@ - (void) selectConsoleLocked:(unsigned int)index
 
     unregister_displaychangelistener(&dcl);
     qkbd_state_switch_console(kbd, con);
-    dcl.con = con;
-    register_displaychangelistener(&dcl);
+    qemu_console_register_listener(con, &dcl, &dcl_ops);
     [self notifyMouseModeChange];
     [self updateUIInfo];
 }
@@ -2145,11 +2142,9 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
     add_console_menu_entries();
     addRemovableDevicesMenuItems();
 
-    dcl.con = qemu_console_lookup_default();
+    qemu_console_register_listener(qemu_console_lookup_default(),
+                                   &dcl, &dcl_ops);
     kbd = qkbd_state_init(dcl.con);
-
-    // register vga output callbacks
-    register_displaychangelistener(&dcl);
     qemu_add_mouse_mode_change_notifier(&mouse_mode_change_notifier);
     [cocoaView notifyMouseModeChange];
     [cocoaView updateUIInfo];

-- 
2.53.0



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

* [PATCH 53/60] ui/console: return completion status from gfx_update callback
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (51 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 52/60] ui/console: simplify registering display/console change listener Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-17 11:43   ` BALATON Zoltan
  2026-03-17  8:51 ` [PATCH 54/60] ui/console: rename public API to use consistent qemu_console_ prefix Marc-André Lureau
                   ` (7 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

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>
---
 hw/display/qxl.h              |  2 +-
 include/ui/console.h          |  3 +--
 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/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 +++++-----
 31 files changed, 121 insertions(+), 85 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 c695b433fe3..2ac9c59e151 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -363,8 +363,7 @@ 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() */
+    bool (*gfx_update)(void *opaque); /* false if deferred update_done */
     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);
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 250fdb49b6f..913f98b5d8e 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -153,7 +153,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;
@@ -172,6 +172,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 61bdb0552e9..8c2bf534ab7 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/jazz_led.c b/hw/display/jazz_led.c
index d5783982950..ee9758a94b5 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 02e8c1435be..6c3a9b1e879 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 c8a4ac21caf..aff26d3ce58 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 36cfc59a74e..409c02272a3 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 7b107509510..bdc24492850 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, uint32_t *chardata)
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index 02fb36b31fc..efd4858f3d0 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 1e154e7f99e..39606d80be1 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 3ab987add34..c8e3bc300fb 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -138,15 +138,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.53.0



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

* [PATCH 54/60] ui/console: rename public API to use consistent qemu_console_ prefix
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (52 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 53/60] ui/console: return completion status from gfx_update callback Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-17 11:46   ` BALATON Zoltan
  2026-03-17  8:51 ` [PATCH 55/60] ui/console: move console_handle_touch_event() to input Marc-André Lureau
                   ` (6 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

Rename the display and graphic console public functions to follow a
consistent qemu_console_ (or qemu_graphic_console_) naming convention.

The previous API used a mix of prefixes: dpy_, graphic_hw_,
graphic_console_, console_has_, and update_displaychangelistener().
Unify them under a common qemu_console_ namespace for better
discoverability and consistency.

The main renames are:
- dpy_gfx_*() / dpy_text_*() / dpy_gl_*() → qemu_console_*()
- dpy_{get,set}_ui_info() → qemu_console_{get,set}_ui_info()
- graphic_hw_*() → qemu_console_hw_*()
- graphic_console_*() → qemu_graphic_console_*()
- console_has_gl() → qemu_console_has_gl()
- update_displaychangelistener() → qemu_console_listener_set_refresh()

No functional changes.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h             | 108 +++++++++++++++---------------
 hw/arm/musicpal.c                |   4 +-
 hw/display/artist.c              |   4 +-
 hw/display/ati.c                 |  16 ++---
 hw/display/bcm2835_fb.c          |   5 +-
 hw/display/bochs-display.c       |  14 ++--
 hw/display/cg3.c                 |   6 +-
 hw/display/cirrus_vga.c          |   8 +--
 hw/display/cirrus_vga_isa.c      |   2 +-
 hw/display/dm163.c               |   6 +-
 hw/display/exynos4210_fimd.c     |   4 +-
 hw/display/g364fb.c              |  10 +--
 hw/display/jazz_led.c            |   8 +--
 hw/display/macfb.c               |   6 +-
 hw/display/next-fb.c             |   4 +-
 hw/display/omap_lcdc.c           |   4 +-
 hw/display/pl110.c               |   4 +-
 hw/display/qxl-render.c          |  12 ++--
 hw/display/qxl.c                 |  14 ++--
 hw/display/ramfb-standalone.c    |   2 +-
 hw/display/ramfb.c               |   4 +-
 hw/display/sm501.c               |   6 +-
 hw/display/ssd0303.c             |   4 +-
 hw/display/ssd0323.c             |   5 +-
 hw/display/tcx.c                 |  16 ++---
 hw/display/vga-isa.c             |   2 +-
 hw/display/vga-mmio.c            |   2 +-
 hw/display/vga-pci.c             |   6 +-
 hw/display/vga.c                 |  40 ++++++-----
 hw/display/vhost-user-gpu.c      |  22 +++---
 hw/display/virtio-gpu-base.c     |   2 +-
 hw/display/virtio-gpu-rutabaga.c |  10 +--
 hw/display/virtio-gpu-udmabuf.c  |   4 +-
 hw/display/virtio-gpu-virgl.c    |  20 +++---
 hw/display/virtio-gpu.c          |  26 ++++----
 hw/display/virtio-vga.c          |   2 +-
 hw/display/vmware_vga.c          |  12 ++--
 hw/display/xenfb.c               |   6 +-
 hw/display/xlnx_dp.c             |  10 +--
 hw/vfio/display.c                |  32 ++++-----
 ui/console-vc.c                  |  12 ++--
 ui/console.c                     | 140 +++++++++++++++++++--------------------
 ui/curses.c                      |   8 +--
 ui/dbus-console.c                |   4 +-
 ui/dbus-listener.c               |  10 +--
 ui/egl-headless.c                |   4 +-
 ui/gtk-egl.c                     |   6 +-
 ui/gtk-gl-area.c                 |   6 +-
 ui/gtk.c                         |  18 ++---
 ui/sdl2-2d.c                     |   2 +-
 ui/sdl2-gl.c                     |   2 +-
 ui/sdl2.c                        |  16 ++---
 ui/spice-display.c               |  16 ++---
 ui/vnc.c                         |  22 +++---
 hw/display/apple-gfx.m           |  16 ++---
 ui/cocoa.m                       |  10 +--
 56 files changed, 376 insertions(+), 388 deletions(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 2ac9c59e151..f8163ef96a8 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -308,49 +308,49 @@ DisplayState *init_displaystate(void);
 void qemu_console_register_listener(QemuConsole *con,
                                     DisplayChangeListener *dcl,
                                     const DisplayChangeListenerOps *ops);
-void update_displaychangelistener(DisplayChangeListener *dcl,
-                                  uint64_t interval);
+void qemu_console_listener_set_refresh(DisplayChangeListener *dcl,
+                                       uint64_t interval);
 void qemu_console_unregister_listener(DisplayChangeListener *dcl);
 
-bool dpy_ui_info_supported(const QemuConsole *con);
-const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con);
-int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay);
-
-void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h);
-void dpy_gfx_update_full(QemuConsole *con);
-void dpy_gfx_replace_surface(QemuConsole *con,
-                             DisplaySurface *surface);
-void dpy_text_cursor(QemuConsole *con, int x, int y);
-void dpy_text_update(QemuConsole *con, int x, int y, int w, int h);
-void dpy_text_resize(QemuConsole *con, int w, int h);
-void dpy_mouse_set(QemuConsole *con, int x, int y, bool on);
-void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor);
-bool dpy_gfx_check_format(QemuConsole *con,
-                          pixman_format_code_t format);
-
-void dpy_gl_scanout_disable(QemuConsole *con);
-void dpy_gl_scanout_texture(QemuConsole *con,
-                            uint32_t backing_id, bool backing_y_0_top,
-                            uint32_t backing_width, uint32_t backing_height,
-                            uint32_t x, uint32_t y, uint32_t w, uint32_t h,
-                            void *d3d_tex2d);
-void dpy_gl_scanout_dmabuf(QemuConsole *con,
-                           QemuDmaBuf *dmabuf);
-void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
-                          bool have_hot, uint32_t hot_x, uint32_t hot_y);
-void dpy_gl_cursor_position(QemuConsole *con,
-                            uint32_t pos_x, uint32_t pos_y);
-void dpy_gl_release_dmabuf(QemuConsole *con,
-                           QemuDmaBuf *dmabuf);
-void dpy_gl_update(QemuConsole *con,
-                   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
-
-QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
-                                QEMUGLParams *params);
-void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx);
-int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx);
-
-bool console_has_gl(QemuConsole *con);
+bool qemu_console_ui_info_supported(const QemuConsole *con);
+const QemuUIInfo *qemu_console_get_ui_info(const QemuConsole *con);
+int qemu_console_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay);
+
+void qemu_console_update(QemuConsole *con, int x, int y, int w, int h);
+void qemu_console_update_full(QemuConsole *con);
+void qemu_console_set_surface(QemuConsole *con,
+                              DisplaySurface *surface);
+void qemu_console_text_set_cursor(QemuConsole *con, int x, int y);
+void qemu_console_text_update(QemuConsole *con, int x, int y, int w, int h);
+void qemu_console_text_resize(QemuConsole *con, int w, int h);
+void qemu_console_set_mouse(QemuConsole *con, int x, int y, bool on);
+void qemu_console_set_cursor(QemuConsole *con, QEMUCursor *cursor);
+bool qemu_console_check_format(QemuConsole *con,
+                               pixman_format_code_t format);
+
+void qemu_console_gl_scanout_disable(QemuConsole *con);
+void qemu_console_gl_scanout_texture(QemuConsole *con,
+                                     uint32_t backing_id, bool backing_y_0_top,
+                                     uint32_t backing_width, uint32_t backing_height,
+                                     uint32_t x, uint32_t y, uint32_t w, uint32_t h,
+                                     void *d3d_tex2d);
+void qemu_console_gl_scanout_dmabuf(QemuConsole *con,
+                                    QemuDmaBuf *dmabuf);
+void qemu_console_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
+                                   bool have_hot, uint32_t hot_x, uint32_t hot_y);
+void qemu_console_gl_cursor_position(QemuConsole *con,
+                                     uint32_t pos_x, uint32_t pos_y);
+void qemu_console_gl_release_dmabuf(QemuConsole *con,
+                                    QemuDmaBuf *dmabuf);
+void qemu_console_gl_update(QemuConsole *con,
+                            uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+
+QEMUGLContext qemu_console_gl_ctx_create(QemuConsole *con,
+                                         QEMUGLParams *params);
+void qemu_console_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx);
+int qemu_console_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx);
+
+bool qemu_console_has_gl(QemuConsole *con);
 
 enum {
     GRAPHIC_FLAGS_NONE     = 0,
@@ -369,19 +369,19 @@ typedef struct GraphicHwOps {
     void (*gl_block)(void *opaque, bool block);
 } GraphicHwOps;
 
-QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
-                                  const GraphicHwOps *ops,
-                                  void *opaque);
-void graphic_console_set_hwops(QemuConsole *con,
-                               const GraphicHwOps *hw_ops,
-                               void *opaque);
-void graphic_console_close(QemuConsole *con);
-
-void graphic_hw_update(QemuConsole *con);
-void graphic_hw_update_done(QemuConsole *con);
-void graphic_hw_invalidate(QemuConsole *con);
-void graphic_hw_text_update(QemuConsole *con, uint32_t *chardata);
-void graphic_hw_gl_block(QemuConsole *con, bool block);
+QemuConsole *qemu_graphic_console_create(DeviceState *dev, uint32_t head,
+                                         const GraphicHwOps *ops,
+                                         void *opaque);
+void qemu_graphic_console_set_hwops(QemuConsole *con,
+                                    const GraphicHwOps *hw_ops,
+                                    void *opaque);
+void qemu_graphic_console_close(QemuConsole *con);
+
+void qemu_console_hw_update(QemuConsole *con);
+void qemu_console_hw_update_done(QemuConsole *con);
+void qemu_console_hw_invalidate(QemuConsole *con);
+void qemu_console_hw_text_update(QemuConsole *con, uint32_t *chardata);
+void qemu_console_hw_gl_block(QemuConsole *con, bool block);
 
 void qemu_console_early_init(void);
 
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 913f98b5d8e..2f43d774dbe 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -171,7 +171,7 @@ static bool lcd_refresh(void *opaque)
         }
     }
 
-    dpy_gfx_update(s->con, 0, 0, 128*3, 64*3);
+    qemu_console_update(s->con, 0, 0, 128*3, 64*3);
     return true;
 }
 
@@ -254,7 +254,7 @@ static const GraphicHwOps musicpal_gfx_ops = {
 static void musicpal_lcd_realize(DeviceState *dev, Error **errp)
 {
     musicpal_lcd_state *s = MUSICPAL_LCD(dev);
-    s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &musicpal_gfx_ops, s);
     qemu_console_resize(s->con, 128 * 3, 64 * 3);
 }
 
diff --git a/hw/display/artist.c b/hw/display/artist.c
index a07508378c7..288d466ec64 100644
--- a/hw/display/artist.c
+++ b/hw/display/artist.c
@@ -1324,7 +1324,7 @@ static bool artist_update_display(void *opaque)
     artist_draw_cursor(s);
 
     if (first >= 0) {
-        dpy_gfx_update(s->con, 0, first, s->width, last - first + 1);
+        qemu_console_update(s->con, 0, first, s->width, last - first + 1);
     }
 
     return true;
@@ -1424,7 +1424,7 @@ static void artist_realizefn(DeviceState *dev, Error **errp)
     s->misc_video |= 0x0A000000;
     s->misc_ctrl  |= 0x00800000;
 
-    s->con = graphic_console_init(dev, 0, &artist_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &artist_ops, s);
     qemu_console_resize(s->con, s->width, s->height);
 }
 
diff --git a/hw/display/ati.c b/hw/display/ati.c
index 05cf507bd47..6dd024f984e 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -162,7 +162,7 @@ static void ati_cursor_define(ATIVGAState *s)
     }
     cursor_set_mono(s->cursor, s->regs.cur_color1, s->regs.cur_color0,
                     &data[512], 1, &data[0]);
-    dpy_cursor_define(s->vga.con, s->cursor);
+    qemu_console_set_cursor(s->vga.con, s->cursor);
 }
 
 /* Alternatively support guest rendered hardware cursor */
@@ -623,9 +623,9 @@ static void ati_mm_write(void *opaque, hwaddr addr,
                 if (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) {
                     ati_cursor_define(s);
                 }
-                dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16,
-                              s->regs.cur_hv_pos & 0xffff,
-                              (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) != 0);
+                qemu_console_set_mouse(s->vga.con, s->regs.cur_hv_pos >> 16,
+                                       s->regs.cur_hv_pos & 0xffff,
+                                       (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) != 0);
             }
         }
         if ((val & (CRTC2_EXT_DISP_EN | CRTC2_EN)) !=
@@ -772,8 +772,8 @@ static void ati_mm_write(void *opaque, hwaddr addr,
         }
         if (!s->cursor_guest_mode &&
             (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(t & BIT(31))) {
-            dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16,
-                          s->regs.cur_hv_pos & 0xffff, true);
+            qemu_console_set_mouse(s->vga.con, s->regs.cur_hv_pos >> 16,
+                                   s->regs.cur_hv_pos & 0xffff, true);
         }
         break;
     }
@@ -1099,7 +1099,7 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp)
     }
     vga_init(vga, OBJECT(s), pci_address_space(dev),
              pci_address_space_io(dev), true);
-    vga->con = graphic_console_init(DEVICE(s), 0, s->vga.hw_ops, vga);
+    vga->con = qemu_graphic_console_create(DEVICE(s), 0, s->vga.hw_ops, vga);
     if (s->cursor_guest_mode) {
         vga->cursor_invalidate = ati_cursor_invalidate;
         vga->cursor_draw_line = ati_cursor_draw_line;
@@ -1159,7 +1159,7 @@ static void ati_vga_exit(PCIDevice *dev)
     ATIVGAState *s = ATI_VGA(dev);
 
     timer_del(&s->vblank_timer);
-    graphic_console_close(s->vga.con);
+    qemu_graphic_console_close(s->vga.con);
 }
 
 static const Property ati_vga_properties[] = {
diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c
index 83c4c03c7ca..bd58f625fcd 100644
--- a/hw/display/bcm2835_fb.c
+++ b/hw/display/bcm2835_fb.c
@@ -207,8 +207,7 @@ static bool fb_update_display(void *opaque)
                                draw_line_src16, s, &first, &last);
 
     if (first >= 0) {
-        dpy_gfx_update(s->con, 0, first, s->config.xres,
-                       last - first + 1);
+        qemu_console_update(s->con, 0, first, s->config.xres, last - first + 1);
     }
 
     s->invalidate = false;
@@ -427,7 +426,7 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
 
     bcm2835_fb_reset(dev);
 
-    s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &vgafb_ops, s);
     qemu_console_resize(s->con, s->config.xres, s->config.yres);
 }
 
diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c
index 8ef9b76cf85..64e669429c4 100644
--- a/hw/display/bochs-display.c
+++ b/hw/display/bochs-display.c
@@ -224,12 +224,12 @@ static bool bochs_display_update(void *opaque)
                                              mode.format,
                                              mode.stride,
                                              ptr + mode.offset);
-        dpy_gfx_replace_surface(s->con, ds);
+        qemu_console_set_surface(s->con, ds);
         full_update = true;
     }
 
     if (full_update) {
-        dpy_gfx_update_full(s->con);
+        qemu_console_update_full(s->con);
     } else {
         snap = memory_region_snapshot_and_clear_dirty(&s->vram,
                                                       mode.offset, mode.size,
@@ -243,14 +243,12 @@ static bool bochs_display_update(void *opaque)
                 ys = y;
             }
             if (!dirty && ys >= 0) {
-                dpy_gfx_update(s->con, 0, ys,
-                               mode.width, y - ys);
+                qemu_console_update(s->con, 0, ys, mode.width, y - ys);
                 ys = -1;
             }
         }
         if (ys >= 0) {
-            dpy_gfx_update(s->con, 0, ys,
-                           mode.width, y - ys);
+            qemu_console_update(s->con, 0, ys, mode.width, y - ys);
         }
 
         g_free(snap);
@@ -279,7 +277,7 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp)
     }
     s->vgamem = pow2ceil(s->vgamem);
 
-    s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
+    s->con = qemu_graphic_console_create(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
 
     memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem,
                            &error_fatal);
@@ -344,7 +342,7 @@ static void bochs_display_exit(PCIDevice *dev)
 {
     BochsDisplayState *s = BOCHS_DISPLAY(dev);
 
-    graphic_console_close(s->con);
+    qemu_graphic_console_close(s->con);
 }
 
 static const Property bochs_display_properties[] = {
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index 8c2bf534ab7..2f714e523c0 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -137,7 +137,7 @@ static bool cg3_update_display(void *opaque)
             }
         } else {
             if (y_start >= 0) {
-                dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
+                qemu_console_update(s->con, 0, y_start, width, y - y_start);
                 y_start = -1;
             }
             pix += width;
@@ -146,7 +146,7 @@ static bool cg3_update_display(void *opaque)
     }
     s->full_update = 0;
     if (y_start >= 0) {
-        dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
+        qemu_console_update(s->con, 0, y_start, width, y - y_start);
     }
     /* vsync interrupt? */
     if (s->regs[0] & CG3_CR_ENABLE_INTS) {
@@ -316,7 +316,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp)
 
     sysbus_init_irq(sbd, &s->irq);
 
-    s->con = graphic_console_init(dev, 0, &cg3_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &cg3_ops, s);
     qemu_console_resize(s->con, s->width, s->height);
 }
 
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index 629b34fc68d..4cf4403b446 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -779,9 +779,9 @@ static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
                       s->cirrus_blt_width, s->cirrus_blt_height);
 
     if (notify) {
-        dpy_gfx_update(s->vga.con, dx, dy,
-                       s->cirrus_blt_width / depth,
-                       s->cirrus_blt_height);
+        qemu_console_update(s->vga.con, dx, dy,
+                            s->cirrus_blt_width / depth,
+                            s->cirrus_blt_height);
     }
 
     /* we don't have to notify the display that this portion has
@@ -2962,7 +2962,7 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp)
     }
     cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev),
                        pci_address_space_io(dev));
-    s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga);
+    s->vga.con = qemu_graphic_console_create(DEVICE(dev), 0, s->vga.hw_ops, &s->vga);
 
     /* setup PCI */
     memory_region_init(&s->pci_bar, OBJECT(dev), "cirrus-pci-bar0", 0x2000000);
diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c
index bad9ec7599c..979155e1916 100644
--- a/hw/display/cirrus_vga_isa.c
+++ b/hw/display/cirrus_vga_isa.c
@@ -63,7 +63,7 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp)
     cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0,
                        isa_address_space(isadev),
                        isa_address_space_io(isadev));
-    s->con = graphic_console_init(dev, 0, s->hw_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, s->hw_ops, s);
     rom_add_vga(VGABIOS_CIRRUS_FILENAME);
     /* XXX ISA-LFB support */
     /* FIXME not qdev yet */
diff --git a/hw/display/dm163.c b/hw/display/dm163.c
index 9ea62cb4f76..afade0b98c3 100644
--- a/hw/display/dm163.c
+++ b/hw/display/dm163.c
@@ -277,8 +277,8 @@ static uint32_t *update_display_of_row(DM163State *s, uint32_t *dest,
         }
     }
 
-    dpy_gfx_update(s->console, 0, LED_SQUARE_SIZE * row,
-                    RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE, LED_SQUARE_SIZE);
+    qemu_console_update(s->console, 0, LED_SQUARE_SIZE * row,
+                        RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE, LED_SQUARE_SIZE);
     s->redraw &= ~(1 << row);
     trace_dm163_redraw(s->redraw);
 
@@ -322,7 +322,7 @@ static void dm163_realize(DeviceState *dev, Error **errp)
     qdev_init_gpio_in(dev, dm163_en_b_gpio_handler, 1);
     qdev_init_gpio_out_named(dev, &s->sout, "sout", 1);
 
-    s->console = graphic_console_init(dev, 0, &dm163_ops, s);
+    s->console = qemu_graphic_console_create(dev, 0, &dm163_ops, s);
     qemu_console_resize(s->console, RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE,
                         RGB_MATRIX_NUM_ROWS * LED_SQUARE_SIZE);
 }
diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index a91f04aaf79..5133623ee2e 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1340,7 +1340,7 @@ static bool exynos4210_fimd_update(void *opaque)
             fimd_copy_line_toqemu(global_width, s->ifb + global_width * line *
                     RGBA_SIZE, d + global_width * line * bpp);
         }
-        dpy_gfx_update_full(s->console);
+        qemu_console_update_full(s->console);
     }
     s->invalidate = false;
     s->vidintcon[1] |= FIMD_VIDINT_INTFRMPEND;
@@ -1964,7 +1964,7 @@ static void exynos4210_fimd_realize(DeviceState *dev, Error **errp)
         return;
     }
 
-    s->console = graphic_console_init(dev, 0, &exynos4210_fimd_ops, s);
+    s->console = qemu_graphic_console_create(dev, 0, &exynos4210_fimd_ops, s);
 }
 
 static void exynos4210_fimd_class_init(ObjectClass *klass, const void *data)
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
index bd15f6f0acc..af54f1f9005 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -191,8 +191,8 @@ static void g364fb_draw_graphic8(G364State *s)
         } else {
             int dy;
             if (xmax || ymax) {
-                dpy_gfx_update(s->con, xmin, ymin,
-                               xmax - xmin + 1, ymax - ymin + 1);
+                qemu_console_update(s->con, xmin, ymin,
+                                   xmax - xmin + 1, ymax - ymin + 1);
                 xmin = s->width;
                 xmax = 0;
                 ymin = s->height;
@@ -211,7 +211,7 @@ static void g364fb_draw_graphic8(G364State *s)
 
 done:
     if (xmax || ymax) {
-        dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
+        qemu_console_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
     }
     g_free(snap);
 }
@@ -234,7 +234,7 @@ static void g364fb_draw_blank(G364State *s)
         d += surface_stride(surface);
     }
 
-    dpy_gfx_update_full(s->con);
+    qemu_console_update_full(s->con);
     s->blanked = 1;
 }
 
@@ -478,7 +478,7 @@ static const GraphicHwOps g364fb_ops = {
 
 static void g364fb_init(DeviceState *dev, G364State *s)
 {
-    s->con = graphic_console_init(dev, 0, &g364fb_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &g364fb_ops, s);
 
     memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &g364fb_ctrl_ops, s,
                           "ctrl", 0x180000);
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
index ee9758a94b5..84fe1058406 100644
--- a/hw/display/jazz_led.c
+++ b/hw/display/jazz_led.c
@@ -217,7 +217,7 @@ static bool jazz_led_update_display(void *opaque)
     }
 
     s->state = REDRAW_NONE;
-    dpy_gfx_update_full(s->con);
+    qemu_console_update_full(s->con);
 
     return true;
 }
@@ -233,7 +233,7 @@ static void jazz_led_text_update(void *opaque, uint32_t *chardata)
     LedState *s = opaque;
     char buf[3];
 
-    dpy_text_cursor(s->con, -1, -1);
+    qemu_console_text_set_cursor(s->con, -1, -1);
     qemu_console_resize(s->con, 2, 1);
 
     /* TODO: draw the segments */
@@ -243,7 +243,7 @@ static void jazz_led_text_update(void *opaque, uint32_t *chardata)
     *chardata++ = ATTR2CHTYPE(buf[1], QEMU_COLOR_BLUE,
                               QEMU_COLOR_BLACK, 1);
 
-    dpy_text_update(s->con, 0, 0, 2, 1);
+    qemu_console_text_update(s->con, 0, 0, 2, 1);
 }
 
 static int jazz_led_post_load(void *opaque, int version_id)
@@ -284,7 +284,7 @@ static void jazz_led_realize(DeviceState *dev, Error **errp)
 {
     LedState *s = JAZZ_LED(dev);
 
-    s->con = graphic_console_init(dev, 0, &jazz_led_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &jazz_led_ops, s);
 }
 
 static void jazz_led_reset(DeviceState *d)
diff --git a/hw/display/macfb.c b/hw/display/macfb.c
index 848c3c282bd..f40a7ed9f52 100644
--- a/hw/display/macfb.c
+++ b/hw/display/macfb.c
@@ -320,14 +320,14 @@ static void macfb_draw_graphic(MacfbState *s)
             }
         } else {
             if (ymin >= 0) {
-                dpy_gfx_update(s->con, 0, ymin, s->width, y - ymin);
+                qemu_console_update(s->con, 0, ymin, s->width, y - ymin);
                 ymin = -1;
             }
         }
     }
 
     if (ymin >= 0) {
-        dpy_gfx_update(s->con, 0, ymin, s->width, y - ymin);
+        qemu_console_update(s->con, 0, ymin, s->width, y - ymin);
     }
 
     g_free(snap);
@@ -671,7 +671,7 @@ static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp)
     s->regs[DAFB_MODE_CTRL1 >> 2] = s->mode->mode_ctrl1;
     s->regs[DAFB_MODE_CTRL2 >> 2] = s->mode->mode_ctrl2;
 
-    s->con = graphic_console_init(dev, 0, &macfb_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &macfb_ops, s);
     surface = qemu_console_surface(s->con);
 
     if (surface_bits_per_pixel(surface) != 32) {
diff --git a/hw/display/next-fb.c b/hw/display/next-fb.c
index e758b223ef7..fa2e0d0da80 100644
--- a/hw/display/next-fb.c
+++ b/hw/display/next-fb.c
@@ -89,7 +89,7 @@ static bool nextfb_update(void *opaque)
                                src_width, dest_width, 0, 1, nextfb_draw_line,
                                s, &first, &last);
 
-    dpy_gfx_update(s->con, 0, 0, s->cols, s->rows);
+    qemu_console_update(s->con, 0, 0, s->cols, s->rows);
 
     return true;
 }
@@ -117,7 +117,7 @@ static void nextfb_realize(DeviceState *dev, Error **errp)
     s->cols = 1120;
     s->rows = 832;
 
-    s->con = graphic_console_init(dev, 0, &nextfb_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &nextfb_ops, s);
     qemu_console_resize(s->con, s->cols, s->rows);
 }
 
diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c
index 1e8385ebffb..2a8d5ffdd57 100644
--- a/hw/display/omap_lcdc.c
+++ b/hw/display/omap_lcdc.c
@@ -320,7 +320,7 @@ static bool omap_update_display(void *opaque)
                                &first, &last);
 
     if (first >= 0) {
-        dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
+        qemu_console_update(omap_lcd->con, 0, first, width, last - first + 1);
     }
     omap_lcd->invalidate = 0;
 
@@ -504,7 +504,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
     memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
     memory_region_add_subregion(sysmem, base, &s->iomem);
 
-    s->con = graphic_console_init(NULL, 0, &omap_ops, s);
+    s->con = qemu_graphic_console_create(NULL, 0, &omap_ops, s);
 
     return s;
 }
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index e134ac28eb6..4a93cf4cda9 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -303,7 +303,7 @@ static bool pl110_update_display(void *opaque)
                                &first, &last);
 
     if (first >= 0) {
-        dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
+        qemu_console_update(s->con, 0, first, s->cols, last - first + 1);
     }
     s->invalidate = 0;
     return true;
@@ -557,7 +557,7 @@ static void pl110_realize(DeviceState *dev, Error **errp)
     s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                    pl110_vblank_interrupt, s);
     qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
-    s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &pl110_gfx_ops, s);
 }
 
 static void pl110_init(Object *obj)
diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
index 5b4f8842011..7b692d5a854 100644
--- a/hw/display/qxl-render.c
+++ b/hw/display/qxl-render.c
@@ -135,7 +135,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
                 (width,
                  height);
         }
-        dpy_gfx_replace_surface(vga->con, surface);
+        qemu_console_set_surface(vga->con, surface);
     }
 
     if (!qxl->guest_primary.data) {
@@ -154,16 +154,16 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
             continue;
         }
         qxl_blit(qxl, qxl->dirty+i);
-        dpy_gfx_update(vga->con,
-                       qxl->dirty[i].left, qxl->dirty[i].top,
-                       qxl->dirty[i].right - qxl->dirty[i].left,
-                       qxl->dirty[i].bottom - qxl->dirty[i].top);
+        qemu_console_update(vga->con,
+                            qxl->dirty[i].left, qxl->dirty[i].top,
+                            qxl->dirty[i].right - qxl->dirty[i].left,
+                            qxl->dirty[i].bottom - qxl->dirty[i].top);
     }
     qxl->num_dirty_rects = 0;
 
 end:
     if (qxl->render_update_cookie_num == 0) {
-        graphic_hw_update_done(qxl->ssd.dcl.con);
+        qemu_console_hw_update_done(qxl->ssd.dcl.con);
     }
 }
 
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 6c3a9b1e879..c4da51e4f4c 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1153,13 +1153,13 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d)
     }
     trace_qxl_enter_vga_mode(d->id);
     spice_qxl_driver_unload(&d->ssd.qxl);
-    graphic_console_set_hwops(d->ssd.dcl.con, d->vga.hw_ops, &d->vga);
-    update_displaychangelistener(&d->ssd.dcl, GUI_REFRESH_INTERVAL_DEFAULT);
+    qemu_graphic_console_set_hwops(d->ssd.dcl.con, d->vga.hw_ops, &d->vga);
+    qemu_console_listener_set_refresh(&d->ssd.dcl, GUI_REFRESH_INTERVAL_DEFAULT);
     qemu_spice_create_host_primary(&d->ssd);
     d->mode = QXL_MODE_VGA;
     qemu_spice_display_switch(&d->ssd, d->ssd.ds);
     vga_dirty_log_start(&d->vga);
-    graphic_hw_update(d->vga.con);
+    qemu_console_hw_update(d->vga.con);
 }
 
 static void qxl_exit_vga_mode(PCIQXLDevice *d)
@@ -1168,8 +1168,8 @@ static void qxl_exit_vga_mode(PCIQXLDevice *d)
         return;
     }
     trace_qxl_exit_vga_mode(d->id);
-    graphic_console_set_hwops(d->ssd.dcl.con, &qxl_ops, d);
-    update_displaychangelistener(&d->ssd.dcl, GUI_REFRESH_INTERVAL_IDLE);
+    qemu_graphic_console_set_hwops(d->ssd.dcl.con, &qxl_ops, d);
+    qemu_console_listener_set_refresh(&d->ssd.dcl, GUI_REFRESH_INTERVAL_IDLE);
     vga_dirty_log_stop(&d->vga);
     qxl_destroy_primary(d, QXL_SYNC);
 }
@@ -2237,7 +2237,7 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp)
     portio_list_add(&qxl->vga_port_list, pci_address_space_io(dev), 0x3b0);
     qxl->have_vga = true;
 
-    vga->con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl);
+    vga->con = qemu_graphic_console_create(DEVICE(dev), 0, &qxl_ops, qxl);
     qxl->id = qemu_console_get_index(vga->con); /* == channel_id */
     if (qxl->id != 0) {
         error_setg(errp, "primary qxl-vga device must be console 0 "
@@ -2262,7 +2262,7 @@ static void qxl_realize_secondary(PCIDevice *dev, Error **errp)
     memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram",
                            qxl->vga.vram_size, &error_fatal);
     qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
-    qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl);
+    qxl->vga.con = qemu_graphic_console_create(DEVICE(dev), 0, &qxl_ops, qxl);
     qxl->ssd.dcl.con = qxl->vga.con;
     qxl->id = qemu_console_get_index(qxl->vga.con); /* == channel_id */
 
diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c
index 27f0ba19f90..8e8ba37514a 100644
--- a/hw/display/ramfb-standalone.c
+++ b/hw/display/ramfb-standalone.c
@@ -41,7 +41,7 @@ static void ramfb_realizefn(DeviceState *dev, Error **errp)
 {
     RAMFBStandaloneState *ramfb = RAMFB(dev);
 
-    ramfb->con = graphic_console_init(dev, 0, &wrapper_ops, dev);
+    ramfb->con = qemu_graphic_console_create(dev, 0, &wrapper_ops, dev);
     ramfb->state = ramfb_setup(ramfb->use_legacy_x86_rom, errp);
 }
 
diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c
index 50c25706a52..7a88f934e11 100644
--- a/hw/display/ramfb.c
+++ b/hw/display/ramfb.c
@@ -111,12 +111,12 @@ void ramfb_display_update(QemuConsole *con, RAMFBState *s)
     }
 
     if (s->ds) {
-        dpy_gfx_replace_surface(con, s->ds);
+        qemu_console_set_surface(con, s->ds);
         s->ds = NULL;
     }
 
     /* simple full screen update */
-    dpy_gfx_update_full(con);
+    qemu_console_update_full(con);
 }
 
 static int ramfb_post_load(void *opaque, int version_id)
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index a3993ceba29..af870048372 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1822,7 +1822,7 @@ static bool sm501_update_display(void *opaque)
         } else {
             if (y_start >= 0) {
                 /* flush to display */
-                dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
+                qemu_console_update(s->con, 0, y_start, width, y - y_start);
                 y_start = -1;
             }
         }
@@ -1831,7 +1831,7 @@ static bool sm501_update_display(void *opaque)
 
     /* complete flush to display */
     if (y_start >= 0) {
-        dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
+        qemu_console_update(s->con, 0, y_start, width, y - y_start);
     }
 
     return true;
@@ -1936,7 +1936,7 @@ static void sm501_init(SM501State *s, DeviceState *dev,
                                 &s->twoD_engine_region);
 
     /* create qemu graphic console */
-    s->con = graphic_console_init(dev, 0, &sm501_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &sm501_ops, s);
 }
 
 static const VMStateDescription vmstate_sm501_state = {
diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c
index 229856cc427..4e3dede33f1 100644
--- a/hw/display/ssd0303.c
+++ b/hw/display/ssd0303.c
@@ -268,7 +268,7 @@ static bool ssd0303_update_display(void *opaque)
         }
     }
     s->redraw = 0;
-    dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
+    qemu_console_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
 
     return true;
 }
@@ -309,7 +309,7 @@ static void ssd0303_realize(DeviceState *dev, Error **errp)
 {
     ssd0303_state *s = SSD0303(dev);
 
-    s->con = graphic_console_init(dev, 0, &ssd0303_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &ssd0303_ops, s);
     qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
 }
 
diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c
index 67db16086c8..9309d4d10c4 100644
--- a/hw/display/ssd0323.c
+++ b/hw/display/ssd0323.c
@@ -270,7 +270,8 @@ static bool ssd0323_update_display(void *opaque)
         }
     }
     s->redraw = 0;
-    dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
+    qemu_console_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
+
     return true;
 }
 
@@ -356,7 +357,7 @@ static void ssd0323_realize(SSIPeripheral *d, Error **errp)
 
     s->col_end = 63;
     s->row_end = 79;
-    s->con = graphic_console_init(dev, 0, &ssd0323_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &ssd0323_ops, s);
     qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
 
     qdev_init_gpio_in(dev, ssd0323_cd, 1);
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index aff26d3ce58..85c7943168b 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -243,8 +243,7 @@ static bool tcx_update_display(void *opaque)
         } else {
             if (y_start >= 0) {
                 /* flush to display */
-                dpy_gfx_update(ts->con, 0, y_start,
-                               ts->width, y - y_start);
+                qemu_console_update(ts->con, 0, y_start, ts->width, y - y_start);
                 y_start = -1;
             }
         }
@@ -253,8 +252,7 @@ static bool tcx_update_display(void *opaque)
     }
     if (y_start >= 0) {
         /* flush to display */
-        dpy_gfx_update(ts->con, 0, y_start,
-                       ts->width, y - y_start);
+        qemu_console_update(ts->con, 0, y_start, ts->width, y - y_start);
     }
     g_free(snap);
     return true;
@@ -297,8 +295,7 @@ static bool tcx24_update_display(void *opaque)
         } else {
             if (y_start >= 0) {
                 /* flush to display */
-                dpy_gfx_update(ts->con, 0, y_start,
-                               ts->width, y - y_start);
+                qemu_console_update(ts->con, 0, y_start, ts->width, y - y_start);
                 y_start = -1;
             }
         }
@@ -309,8 +306,7 @@ static bool tcx24_update_display(void *opaque)
     }
     if (y_start >= 0) {
         /* flush to display */
-        dpy_gfx_update(ts->con, 0, y_start,
-                       ts->width, y - y_start);
+        qemu_console_update(ts->con, 0, y_start, ts->width, y - y_start);
     }
     g_free(snap);
     return true;
@@ -869,9 +865,9 @@ static void tcx_realizefn(DeviceState *dev, Error **errp)
     sysbus_init_irq(sbd, &s->irq);
 
     if (s->depth == 8) {
-        s->con = graphic_console_init(dev, 0, &tcx_ops, s);
+        s->con = qemu_graphic_console_create(dev, 0, &tcx_ops, s);
     } else {
-        s->con = graphic_console_init(dev, 0, &tcx24_ops, s);
+        s->con = qemu_graphic_console_create(dev, 0, &tcx24_ops, s);
     }
     s->thcmisc = 0;
 
diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c
index 95d85ff69a5..ec351cbba23 100644
--- a/hw/display/vga-isa.c
+++ b/hw/display/vga-isa.c
@@ -79,7 +79,7 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp)
                                         0x000a0000,
                                         vga_io_memory, 1);
     memory_region_set_coalescing(vga_io_memory);
-    s->con = graphic_console_init(dev, 0, s->hw_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, s->hw_ops, s);
 
     memory_region_add_subregion(isa_address_space(isadev),
                                 VBE_DISPI_LFB_PHYSICAL_ADDRESS,
diff --git a/hw/display/vga-mmio.c b/hw/display/vga-mmio.c
index 1a9608d865f..3cd64951c09 100644
--- a/hw/display/vga-mmio.c
+++ b/hw/display/vga-mmio.c
@@ -108,7 +108,7 @@ static void vga_mmio_realizefn(DeviceState *dev, Error **errp)
     }
 
     sysbus_init_mmio(sbd, &s->vga.vram);
-    s->vga.con = graphic_console_init(dev, 0, s->vga.hw_ops, &s->vga);
+    s->vga.con = qemu_graphic_console_create(dev, 0, s->vga.hw_ops, &s->vga);
 }
 
 static const Property vga_mmio_properties[] = {
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index 4e68dd57a17..d089847bdae 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -247,7 +247,7 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp)
     vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev),
              true);
 
-    s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s);
+    s->con = qemu_graphic_console_create(DEVICE(dev), 0, s->hw_ops, s);
 
     /* XXX: VGA_RAM_SIZE must be a power of two */
     pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
@@ -282,7 +282,7 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp)
     if (!vga_common_init(s, OBJECT(dev), errp)) {
         return;
     }
-    s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s);
+    s->con = qemu_graphic_console_create(DEVICE(dev), 0, s->hw_ops, s);
 
     /* mmio bar */
     memory_region_init_io(&d->mmio, OBJECT(dev), &unassigned_io_ops, NULL,
@@ -306,7 +306,7 @@ static void pci_secondary_vga_exit(PCIDevice *dev)
     PCIVGAState *d = PCI_VGA(dev);
     VGACommonState *s = &d->vga;
 
-    graphic_console_close(s->con);
+    qemu_graphic_console_close(s->con);
     memory_region_del_subregion(&d->mmio, &d->mrs[0]);
     memory_region_del_subregion(&d->mmio, &d->mrs[1]);
     if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) {
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 409c02272a3..3f456b96608 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1246,7 +1246,7 @@ static void vga_draw_text(VGACommonState *s, int full_update)
         s->last_scr_height = height * cheight;
         qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height);
         surface = qemu_console_surface(s->con);
-        dpy_text_resize(s->con, width, height);
+        qemu_console_text_resize(s->con, width, height);
         s->last_depth = 0;
         s->last_width = width;
         s->last_height = height;
@@ -1365,8 +1365,8 @@ static void vga_draw_text(VGACommonState *s, int full_update)
             ch_attr_ptr++;
         }
         if (cx_max != -1) {
-            dpy_gfx_update(s->con, cx_min * cw, cy * cheight,
-                           (cx_max - cx_min + 1) * cw, cheight);
+            qemu_console_update(s->con, cx_min * cw, cy * cheight,
+                                (cx_max - cx_min + 1) * cw, cheight);
         }
         dest += linesize * cheight;
         line1 = line + cheight;
@@ -1610,7 +1610,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
      */
     format = qemu_default_pixman_format(depth, !byteswap);
     if (format) {
-        allocate_surface = !dpy_gfx_check_format(s->con, format)
+        allocate_surface = !qemu_console_check_format(s->con, format)
             || s->force_shadow || force_shadow;
     } else {
         allocate_surface = true;
@@ -1647,7 +1647,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
             surface = qemu_create_displaysurface_from(disp_width,
                     height, format, s->params.line_offset,
                     s->vram_ptr + (s->params.start_addr * 4));
-            dpy_gfx_replace_surface(s->con, surface);
+            qemu_console_set_surface(s->con, surface);
         } else {
             qemu_console_resize(s->con, disp_width, height);
             surface = qemu_console_surface(s->con);
@@ -1720,8 +1720,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
         } else {
             if (y_start >= 0) {
                 /* flush to display */
-                dpy_gfx_update(s->con, 0, y_start,
-                               disp_width, y - y_start);
+                qemu_console_update(s->con, 0, y_start, disp_width, y - y_start);
                 y_start = -1;
             }
         }
@@ -1745,8 +1744,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
     }
     if (y_start >= 0) {
         /* flush to display */
-        dpy_gfx_update(s->con, 0, y_start,
-                       disp_width, y - y_start);
+        qemu_console_update(s->con, 0, y_start, disp_width, y - y_start);
     }
     g_free(snap);
     memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table));
@@ -1767,7 +1765,7 @@ static void vga_draw_blank(VGACommonState *s, int full_update)
         /* unshare buffer, otherwise the blanking corrupts vga vram */
         surface = qemu_create_displaysurface(s->last_scr_width,
                                              s->last_scr_height);
-        dpy_gfx_replace_surface(s->con, surface);
+        qemu_console_set_surface(s->con, surface);
     }
 
     w = s->last_scr_width * surface_bytes_per_pixel(surface);
@@ -1776,7 +1774,7 @@ static void vga_draw_blank(VGACommonState *s, int full_update)
         memset(d, 0, w);
         d += surface_stride(surface);
     }
-    dpy_gfx_update_full(s->con);
+    qemu_console_update_full(s->con);
 }
 
 #define GMODE_TEXT     0
@@ -1967,7 +1965,7 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
             s->last_scr_width = width * cw;
             s->last_scr_height = height * cheight;
             qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height);
-            dpy_text_resize(s->con, width, height);
+            qemu_console_text_resize(s->con, width, height);
             s->last_depth = 0;
             s->last_width = width;
             s->last_height = height;
@@ -1992,11 +1990,11 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
             s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) {
             cursor_visible = !(s->cr[VGA_CRTC_CURSOR_START] & 0x20);
             if (cursor_visible && cursor_offset < size && cursor_offset >= 0)
-                dpy_text_cursor(s->con,
-                                TEXTMODE_X(cursor_offset),
-                                TEXTMODE_Y(cursor_offset));
+                qemu_console_text_set_cursor(s->con,
+                                             TEXTMODE_X(cursor_offset),
+                                             TEXTMODE_Y(cursor_offset));
             else
-                dpy_text_cursor(s->con, -1, -1);
+                qemu_console_text_set_cursor(s->con, -1, -1);
             s->cursor_offset = cursor_offset;
             s->cursor_start = s->cr[VGA_CRTC_CURSOR_START];
             s->cursor_end = s->cr[VGA_CRTC_CURSOR_END];
@@ -2009,7 +2007,7 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
             for (i = 0; i < size; src ++, dst ++, i ++)
                 *dst = VMEM2CHTYPE(le32_to_cpu(*src));
 
-            dpy_text_update(s->con, 0, 0, width, height);
+            qemu_console_text_update(s->con, 0, 0, width, height);
         } else {
             c_max = 0;
 
@@ -2032,7 +2030,7 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
 
             if (c_min <= c_max) {
                 i = TEXTMODE_Y(c_min);
-                dpy_text_update(s->con, 0, i, width, TEXTMODE_Y(c_max) - i + 1);
+                qemu_console_text_update(s->con, 0, i, width, TEXTMODE_Y(c_max) - i + 1);
             }
         }
 
@@ -2057,8 +2055,8 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
     /* Display a message */
     s->last_width = 60;
     s->last_height = height = 3;
-    dpy_text_cursor(s->con, -1, -1);
-    dpy_text_resize(s->con, s->last_width, height);
+    qemu_console_text_set_cursor(s->con, -1, -1);
+    qemu_console_text_resize(s->con, s->last_width, height);
 
     for (dst = chardata, i = 0; i < s->last_width * height; i ++)
         *dst++ = ' ';
@@ -2070,7 +2068,7 @@ static void vga_update_text(void *opaque, uint32_t *chardata)
         *dst++ = ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
                              QEMU_COLOR_BLACK, 1);
 
-    dpy_text_update(s->con, 0, 0, s->last_width, height);
+    qemu_console_text_update(s->con, 0, 0, s->last_width, height);
 }
 
 static uint64_t vga_mem_read(void *opaque, hwaddr addr,
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
index 3f6fb7a8033..6e5e6540a46 100644
--- a/hw/display/vhost-user-gpu.c
+++ b/hw/display/vhost-user-gpu.c
@@ -142,11 +142,11 @@ vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg)
         memcpy(s->current_cursor->data, up->data,
                64 * 64 * sizeof(uint32_t));
 
-        dpy_cursor_define(s->con, s->current_cursor);
+        qemu_console_set_cursor(s->con, s->current_cursor);
     }
 
-    dpy_mouse_set(s->con, pos->x, pos->y,
-                  msg->request != VHOST_USER_GPU_CURSOR_POS_HIDE);
+    qemu_console_set_mouse(s->con, pos->x, pos->y,
+                           msg->request != VHOST_USER_GPU_CURSOR_POS_HIDE);
 }
 
 static void
@@ -238,7 +238,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
         con = s->con;
 
         if (m->width == 0) {
-            dpy_gfx_replace_surface(con, NULL);
+            qemu_console_set_surface(con, NULL);
         } else {
             s->ds = qemu_create_displaysurface(m->width, m->height);
             /* replace surface on next update */
@@ -269,12 +269,12 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
 
         if (dmabuf) {
             qemu_dmabuf_close(dmabuf);
-            dpy_gl_release_dmabuf(con, dmabuf);
+            qemu_console_gl_release_dmabuf(con, dmabuf);
             g_clear_pointer(&dmabuf, qemu_dmabuf_free);
         }
 
         if (fd == -1) {
-            dpy_gl_scanout_disable(con);
+            qemu_console_gl_scanout_disable(con);
             g->dmabuf[m->scanout_id] = NULL;
             break;
         }
@@ -291,7 +291,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
                                  &fd, 1, false, m->fd_flags &
                                  VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP);
 
-        dpy_gl_scanout_dmabuf(con, dmabuf);
+        qemu_console_gl_scanout_dmabuf(con, dmabuf);
         g->dmabuf[m->scanout_id] = dmabuf;
         break;
     }
@@ -306,13 +306,13 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
         }
 
         con = g->parent_obj.scanout[m->scanout_id].con;
-        if (!console_has_gl(con)) {
+        if (!qemu_console_has_gl(con)) {
             error_report("console doesn't support GL!");
             vhost_user_gpu_unblock(g);
             break;
         }
         g->backend_blocked = true;
-        dpy_gl_update(con, m->x, m->y, m->width, m->height);
+        qemu_console_gl_update(con, m->x, m->y, m->width, m->height);
         break;
     }
 #ifdef CONFIG_PIXMAN
@@ -337,9 +337,9 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
 
         pixman_image_unref(image);
         if (qemu_console_surface(con) != s->ds) {
-            dpy_gfx_replace_surface(con, s->ds);
+            qemu_console_set_surface(con, s->ds);
         } else {
-            dpy_gfx_update(con, m->x, m->y, m->width, m->height);
+            qemu_console_update(con, m->x, m->y, m->width, m->height);
         }
         break;
     }
diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index bdc24492850..a68b1848295 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -253,7 +253,7 @@ virtio_gpu_base_device_realize(DeviceState *qdev,
     g->hw_ops = &virtio_gpu_ops;
     for (i = 0; i < g->conf.max_outputs; i++) {
         g->scanout[i].con =
-            graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
+            qemu_graphic_console_create(DEVICE(g), i, &virtio_gpu_ops, g);
     }
 
     return true;
diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c
index ed5ae52acbe..0742024e9bc 100644
--- a/hw/display/virtio-gpu-rutabaga.c
+++ b/hw/display/virtio-gpu-rutabaga.c
@@ -282,7 +282,7 @@ rutabaga_cmd_resource_flush(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd)
                                              rf.resource_id, &transfer,
                                              &transfer_iovec);
     CHECK(!result, cmd);
-    dpy_gfx_update_full(scanout->con);
+    qemu_console_update_full(scanout->con);
 }
 
 static void
@@ -306,8 +306,8 @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd)
     scanout = &vb->scanout[ss.scanout_id];
 
     if (ss.resource_id == 0) {
-        dpy_gfx_replace_surface(scanout->con, NULL);
-        dpy_gl_scanout_disable(scanout->con);
+        qemu_console_set_surface(scanout->con, NULL);
+        qemu_console_gl_scanout_disable(scanout->con);
         return;
     }
 
@@ -331,8 +331,8 @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd)
 
     /* realloc the surface ptr */
     scanout->ds = qemu_create_displaysurface_pixman(res->image);
-    dpy_gfx_replace_surface(scanout->con, NULL);
-    dpy_gfx_replace_surface(scanout->con, scanout->ds);
+    qemu_console_set_surface(scanout->con, NULL);
+    qemu_console_set_surface(scanout->con, scanout->ds);
     res->scanout_bitmask = ss.scanout_id;
 }
 
diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 74b6a7766af..d5ac1cfca0e 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -156,7 +156,7 @@ static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf)
     struct virtio_gpu_scanout *scanout;
 
     scanout = &g->parent_obj.scanout[dmabuf->scanout_id];
-    dpy_gl_release_dmabuf(scanout->con, dmabuf->buf);
+    qemu_console_gl_release_dmabuf(scanout->con, dmabuf->buf);
     g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free);
     QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next);
     g_free(dmabuf);
@@ -232,7 +232,7 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g,
     height = qemu_dmabuf_get_height(new_primary->buf);
     g->dmabuf.primary[scanout_id] = new_primary;
     qemu_console_resize(scanout->con, width, height);
-    dpy_gl_scanout_dmabuf(scanout->con, new_primary->buf);
+    qemu_console_gl_scanout_dmabuf(scanout->con, new_primary->buf);
 
     if (old_primary) {
         virtio_gpu_free_dmabuf(g, old_primary);
diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index b7a2d160ddd..f8437616779 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -521,7 +521,7 @@ static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y,
         return;
     }
 
-    dpy_gl_update(g->parent_obj.scanout[idx].con, x, y, width, height);
+    qemu_console_gl_update(g->parent_obj.scanout[idx].con, x, y, width, height);
 }
 
 static void virgl_cmd_resource_flush(VirtIOGPU *g,
@@ -584,16 +584,15 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
         qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con,
                             ss.r.width, ss.r.height);
         virgl_renderer_force_ctx_0();
-        dpy_gl_scanout_texture(
+        qemu_console_gl_scanout_texture(
             g->parent_obj.scanout[ss.scanout_id].con, info.tex_id,
             info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
             info.width, info.height,
             ss.r.x, ss.r.y, ss.r.width, ss.r.height,
             d3d_tex2d);
     } else {
-        dpy_gfx_replace_surface(
-            g->parent_obj.scanout[ss.scanout_id].con, NULL);
-        dpy_gl_scanout_disable(g->parent_obj.scanout[ss.scanout_id].con);
+        qemu_console_set_surface(g->parent_obj.scanout[ss.scanout_id].con, NULL);
+        qemu_console_gl_scanout_disable(g->parent_obj.scanout[ss.scanout_id].con);
     }
     g->parent_obj.scanout[ss.scanout_id].resource_id = ss.resource_id;
 }
@@ -1315,7 +1314,7 @@ virgl_create_context(void *opaque, int scanout_idx,
     qparams.major_ver = params->major_ver;
     qparams.minor_ver = params->minor_ver;
 
-    ctx = dpy_gl_ctx_create(g->parent_obj.scanout[scanout_idx].con, &qparams);
+    ctx = qemu_console_gl_ctx_create(g->parent_obj.scanout[scanout_idx].con, &qparams);
     return (virgl_renderer_gl_context)ctx;
 }
 
@@ -1324,7 +1323,7 @@ static void virgl_destroy_context(void *opaque, virgl_renderer_gl_context ctx)
     VirtIOGPU *g = opaque;
     QEMUGLContext qctx = (QEMUGLContext)ctx;
 
-    dpy_gl_ctx_destroy(g->parent_obj.scanout[0].con, qctx);
+    qemu_console_gl_ctx_destroy(g->parent_obj.scanout[0].con, qctx);
 }
 
 static int virgl_make_context_current(void *opaque, int scanout_idx,
@@ -1333,8 +1332,7 @@ static int virgl_make_context_current(void *opaque, int scanout_idx,
     VirtIOGPU *g = opaque;
     QEMUGLContext qctx = (QEMUGLContext)ctx;
 
-    return dpy_gl_ctx_make_current(g->parent_obj.scanout[scanout_idx].con,
-                                   qctx);
+    return qemu_console_gl_ctx_make_current(g->parent_obj.scanout[scanout_idx].con, qctx);
 }
 
 static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = {
@@ -1399,8 +1397,8 @@ void virtio_gpu_virgl_reset_scanout(VirtIOGPU *g)
     int i;
 
     for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
-        dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL);
-        dpy_gl_scanout_disable(g->parent_obj.scanout[i].con);
+        qemu_console_set_surface(g->parent_obj.scanout[i].con, NULL);
+        qemu_console_gl_scanout_disable(g->parent_obj.scanout[i].con);
     }
 }
 
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index de7a86a73d2..836eea0dcea 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -103,14 +103,14 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
         if (cursor->resource_id > 0) {
             vgc->update_cursor_data(g, s, cursor->resource_id);
         }
-        dpy_cursor_define(s->con, s->current_cursor);
+        qemu_console_set_cursor(s->con, s->current_cursor);
 
         s->cursor = *cursor;
     } else {
         s->cursor.pos.x = cursor->pos.x;
         s->cursor.pos.y = cursor->pos.y;
     }
-    dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y, cursor->resource_id);
+    qemu_console_set_mouse(s->con, cursor->pos.x, cursor->pos.y, cursor->resource_id);
 }
 
 struct virtio_gpu_simple_resource *
@@ -378,7 +378,7 @@ void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id)
         res->scanout_bitmask &= ~(1 << scanout_id);
     }
 
-    dpy_gfx_replace_surface(scanout->con, NULL);
+    qemu_console_set_surface(scanout->con, NULL);
     scanout->resource_id = 0;
     scanout->ds = NULL;
     scanout->width = 0;
@@ -519,8 +519,8 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
                 rf.r.y + rf.r.height >= scanout->y) {
                 within_bounds = true;
 
-                if (console_has_gl(scanout->con)) {
-                    dpy_gl_update(scanout->con, 0, 0, scanout->width,
+                if (qemu_console_has_gl(scanout->con)) {
+                    qemu_console_gl_update(scanout->con, 0, 0, scanout->width,
                                   scanout->height);
                     update_submitted = true;
                 }
@@ -570,8 +570,8 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
         /* work out the area we need to update for each console */
         if (qemu_rect_intersect(&flush_rect, &rect, &rect)) {
             qemu_rect_translate(&rect, -scanout->x, -scanout->y);
-            dpy_gfx_update(g->parent_obj.scanout[i].con,
-                           rect.x, rect.y, rect.width, rect.height);
+            qemu_console_update(g->parent_obj.scanout[i].con,
+                                rect.x, rect.y, rect.width, rect.height);
         }
     }
 }
@@ -637,7 +637,7 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
     g->parent_obj.enable = 1;
 
     if (res->blob) {
-        if (console_has_gl(scanout->con)) {
+        if (qemu_console_has_gl(scanout->con)) {
             if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) {
                 virtio_gpu_update_scanout(g, scanout_id, res, fb, r);
             } else {
@@ -653,7 +653,7 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
     }
 
     /* create a surface for this scanout */
-    if ((res->blob && !console_has_gl(scanout->con)) ||
+    if ((res->blob && !qemu_console_has_gl(scanout->con)) ||
         !scanout->ds ||
         surface_data(scanout->ds) != data + fb->offset ||
         scanout->width != r->width ||
@@ -674,7 +674,7 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
         qemu_displaysurface_set_share_handle(scanout->ds, res->share_handle, fb->offset);
 
         pixman_image_unref(rect);
-        dpy_gfx_replace_surface(g->parent_obj.scanout[scanout_id].con,
+        qemu_console_set_surface(g->parent_obj.scanout[scanout_id].con,
                                 scanout->ds);
     }
 
@@ -1466,10 +1466,10 @@ static int virtio_gpu_post_load(void *opaque, int version_id)
             }
             scanout->ds = qemu_create_displaysurface_pixman(res->image);
             qemu_displaysurface_set_share_handle(scanout->ds, res->share_handle, 0);
-            dpy_gfx_replace_surface(scanout->con, scanout->ds);
+            qemu_console_set_surface(scanout->con, scanout->ds);
         }
 
-        dpy_gfx_update_full(scanout->con);
+        qemu_console_update_full(scanout->con);
         if (scanout->cursor.resource_id) {
             update_cursor(g, &scanout->cursor);
         }
@@ -1585,7 +1585,7 @@ static void virtio_gpu_reset_bh(void *opaque)
     }
 
     for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
-        dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL);
+        qemu_console_set_surface(g->parent_obj.scanout[i].con, NULL);
     }
 
     g->reset_finished = true;
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index efd4858f3d0..2ae649c91ae 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -172,7 +172,7 @@ static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
                                  vvga->vga_mrs, true, false);
 
     vga->con = g->scanout[0].con;
-    graphic_console_set_hwops(vga->con, &virtio_vga_base_ops, vvga);
+    qemu_graphic_console_set_hwops(vga->con, &virtio_vga_base_ops, vvga);
 
     for (i = 0; i < g->conf.max_outputs; i++) {
         object_property_set_link(OBJECT(g->scanout[i].con), "device",
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index 39606d80be1..f8906776b16 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -378,7 +378,7 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s,
     for (line = h; line > 0; line--, src += bypl, dst += bypl) {
         memcpy(dst, src, width);
     }
-    dpy_gfx_update(s->vga.con, x, y, w, h);
+    qemu_console_update(s->vga.con, x, y, w, h);
 }
 
 static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s)
@@ -554,7 +554,7 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s,
         qc = cursor_builtin_left_ptr();
     }
 
-    dpy_cursor_define(s->vga.con, qc);
+    qemu_console_set_cursor(s->vga.con, qc);
     cursor_unref(qc);
 }
 #endif
@@ -1082,7 +1082,7 @@ static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
         s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE);
 #ifdef HW_MOUSE_ACCEL
         if (value <= SVGA_CURSOR_ON_SHOW) {
-            dpy_mouse_set(s->vga.con, s->cursor.x, s->cursor.y, s->cursor.on);
+            qemu_console_set_mouse(s->vga.con, s->cursor.x, s->cursor.y, s->cursor.on);
         }
 #endif
         break;
@@ -1130,7 +1130,7 @@ static inline void vmsvga_check_size(struct vmsvga_state_s *s)
         surface = qemu_create_displaysurface_from(s->new_width, s->new_height,
                                                   format, stride,
                                                   s->vga.vram_ptr);
-        dpy_gfx_replace_surface(s->vga.con, surface);
+        qemu_console_set_surface(s->vga.con, surface);
         s->invalidated = 1;
     }
 }
@@ -1151,7 +1151,7 @@ static bool vmsvga_update_display(void *opaque)
 
     if (s->invalidated) {
         s->invalidated = 0;
-        dpy_gfx_update_full(s->vga.con);
+        qemu_console_update_full(s->vga.con);
     }
 
     return true;
@@ -1254,7 +1254,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s,
     s->scratch_size = SVGA_SCRATCH_SIZE;
     s->scratch = g_malloc(s->scratch_size * 4);
 
-    s->vga.con = graphic_console_init(dev, 0, &vmsvga_ops, s);
+    s->vga.con = qemu_graphic_console_create(dev, 0, &vmsvga_ops, s);
 
     s->fifo_size = SVGA_FIFO_SIZE;
     memory_region_init_ram(&s->fifo_ram, NULL, "vmsvga.fifo", s->fifo_size,
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index 2e431e27be6..8e9953bda43 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -657,7 +657,7 @@ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h)
         xen_pv_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n",
                       __func__, xenfb->depth, bpp);
 
-    dpy_gfx_update(xenfb->con, x, y, w, h);
+    qemu_console_update(xenfb->con, x, y, w, h);
 }
 
 #ifdef XENFB_TYPE_REFRESH_PERIOD
@@ -743,7 +743,7 @@ static bool xenfb_update(void *opaque)
             surface = qemu_create_displaysurface(xenfb->width, xenfb->height);
             break;
         }
-        dpy_gfx_replace_surface(xenfb->con, surface);
+        qemu_console_set_surface(xenfb->con, surface);
         xen_pv_printf(&xenfb->c.xendev, 1,
                       "update: resizing: %dx%d @ %d bpp%s\n",
                       xenfb->width, xenfb->height, xenfb->depth,
@@ -903,7 +903,7 @@ static int fb_initialise(struct XenLegacyDevice *xendev)
     if (rc != 0)
         return rc;
 
-    fb->con = graphic_console_init(NULL, 0, &xenfb_ops, fb);
+    fb->con = qemu_graphic_console_create(NULL, 0, &xenfb_ops, fb);
 
     if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1)
         fb->feature_update = 0;
diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c
index 50e6ef10984..2486d9e5825 100644
--- a/hw/display/xlnx_dp.c
+++ b/hw/display/xlnx_dp.c
@@ -605,7 +605,7 @@ static void xlnx_dp_recreate_surface(XlnxDPState *s)
 
     if ((width != 0) && (height != 0)) {
         /*
-         * As dpy_gfx_replace_surface calls qemu_free_displaysurface on the
+         * As qemu_console_replace_surface calls qemu_free_displaysurface on the
          * surface we need to be careful and don't free the surface associated
          * to the console or double free will happen.
          */
@@ -631,10 +631,10 @@ static void xlnx_dp_recreate_surface(XlnxDPState *s)
                                                             height,
                                                             s->g_plane.format,
                                                             0, NULL);
-            dpy_gfx_replace_surface(s->console, s->bout_plane.surface);
+            qemu_console_set_surface(s->console, s->bout_plane.surface);
         } else {
             s->bout_plane.surface = NULL;
-            dpy_gfx_replace_surface(s->console, s->g_plane.surface);
+            qemu_console_set_surface(s->console, s->g_plane.surface);
         }
 
         xlnx_dpdma_set_host_data_location(s->dpdma, DP_GRAPHIC_DMA_CHANNEL,
@@ -1287,7 +1287,7 @@ static bool xlnx_dp_update_display(void *opaque)
     /*
      * XXX: We might want to update only what changed.
      */
-    dpy_gfx_update_full(s->console);
+    qemu_console_update_full(s->console);
 
     return true;
 }
@@ -1387,7 +1387,7 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp)
     qdev_realize(DEVICE(s->edid), BUS(aux_get_i2c_bus(s->aux_bus)),
                  &error_fatal);
 
-    s->console = graphic_console_init(dev, 0, &xlnx_dp_gfx_ops, s);
+    s->console = qemu_graphic_console_create(dev, 0, &xlnx_dp_gfx_ops, s);
     surface = qemu_console_surface(s->console);
     xlnx_dpdma_set_host_data_location(s->dpdma, DP_GRAPHIC_DMA_CHANNEL,
                                       surface_data(surface));
diff --git a/hw/vfio/display.c b/hw/vfio/display.c
index 4a9a58036e3..8f91e83da88 100644
--- a/hw/vfio/display.c
+++ b/hw/vfio/display.c
@@ -264,7 +264,7 @@ static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf)
     QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
 
     qemu_dmabuf_close(dmabuf->buf);
-    dpy_gl_release_dmabuf(dpy->con, dmabuf->buf);
+    qemu_console_gl_release_dmabuf(dpy->con, dmabuf->buf);
     g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free);
     g_free(dmabuf);
 }
@@ -307,7 +307,7 @@ static bool vfio_display_dmabuf_update(void *opaque)
     if (dpy->dmabuf.primary != primary) {
         dpy->dmabuf.primary = primary;
         qemu_console_resize(dpy->con, width, height);
-        dpy_gl_scanout_dmabuf(dpy->con, primary->buf);
+        qemu_console_gl_scanout_dmabuf(dpy->con, primary->buf);
         free_bufs = true;
     }
 
@@ -321,21 +321,21 @@ static bool vfio_display_dmabuf_update(void *opaque)
     if (cursor && (new_cursor || cursor->hot_updates)) {
         bool have_hot = (cursor->hot_x != 0xffffffff &&
                          cursor->hot_y != 0xffffffff);
-        dpy_gl_cursor_dmabuf(dpy->con, cursor->buf, have_hot,
-                             cursor->hot_x, cursor->hot_y);
+        qemu_console_gl_cursor_dmabuf(dpy->con, cursor->buf, have_hot,
+                                      cursor->hot_x, cursor->hot_y);
         cursor->hot_updates = 0;
     } else if (!cursor && new_cursor) {
-        dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0);
+        qemu_console_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0);
     }
 
     if (cursor && cursor->pos_updates) {
-        dpy_gl_cursor_position(dpy->con,
+        qemu_console_gl_cursor_position(dpy->con,
                                cursor->pos_x,
                                cursor->pos_y);
         cursor->pos_updates = 0;
     }
 
-    dpy_gl_update(dpy->con, 0, 0, width, height);
+    qemu_console_gl_update(dpy->con, 0, 0, width, height);
 
     if (free_bufs) {
         vfio_display_free_dmabufs(vdev);
@@ -363,7 +363,7 @@ static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
     }
 
     vdev->dpy = g_new0(VFIODisplay, 1);
-    vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
+    vdev->dpy->con = qemu_graphic_console_create(DEVICE(vdev), 0,
                                           &vfio_display_dmabuf_ops,
                                           vdev);
     if (vdev->enable_ramfb) {
@@ -396,9 +396,9 @@ void vfio_display_reset(VFIOPCIDevice *vdev)
         return;
     }
 
-    dpy_gl_scanout_disable(vdev->dpy->con);
+    qemu_console_gl_scanout_disable(vdev->dpy->con);
     vfio_display_dmabuf_exit(vdev->dpy);
-    dpy_gfx_update_full(vdev->dpy->con);
+    qemu_console_update_full(vdev->dpy->con);
 }
 
 static bool vfio_display_region_update(void *opaque)
@@ -471,13 +471,13 @@ static bool vfio_display_region_update(void *opaque)
         dpy->region.surface = qemu_create_displaysurface_from
             (plane.width, plane.height, format,
              plane.stride, dpy->region.buffer.mmaps[0].mmap);
-        dpy_gfx_replace_surface(dpy->con, dpy->region.surface);
+        qemu_console_set_surface(dpy->con, dpy->region.surface);
     }
 
     /* full screen update */
-    dpy_gfx_update(dpy->con, 0, 0,
-                   surface_width(dpy->region.surface),
-                   surface_height(dpy->region.surface));
+    qemu_console_update(dpy->con, 0, 0,
+                        surface_width(dpy->region.surface),
+                        surface_height(dpy->region.surface));
     return true;
 
 err:
@@ -493,7 +493,7 @@ static const GraphicHwOps vfio_display_region_ops = {
 static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
 {
     vdev->dpy = g_new0(VFIODisplay, 1);
-    vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
+    vdev->dpy->con = qemu_graphic_console_create(DEVICE(vdev), 0,
                                           &vfio_display_region_ops,
                                           vdev);
     if (vdev->enable_ramfb) {
@@ -553,7 +553,7 @@ void vfio_display_finalize(VFIOPCIDevice *vdev)
         return;
     }
 
-    graphic_console_close(vdev->dpy->con);
+    qemu_graphic_console_close(vdev->dpy->con);
     vfio_display_dmabuf_exit(vdev->dpy);
     vfio_display_region_exit(vdev->dpy);
     vfio_display_edid_exit(vdev->dpy);
diff --git a/ui/console-vc.c b/ui/console-vc.c
index 6e8f2552e41..ec7106b169a 100644
--- a/ui/console-vc.c
+++ b/ui/console-vc.c
@@ -86,15 +86,15 @@ static void text_console_update(void *opaque, uint32_t *chardata)
                                           s->vt.cells[src].t_attrib.bgcol,
                                           s->vt.cells[src].t_attrib.bold);
             }
-        dpy_text_update(QEMU_CONSOLE(s), s->vt.text_x[0], s->vt.text_y[0],
-                        s->vt.text_x[1] - s->vt.text_x[0], i - s->vt.text_y[0]);
+        qemu_console_text_update(QEMU_CONSOLE(s), s->vt.text_x[0], s->vt.text_y[0],
+                                 s->vt.text_x[1] - s->vt.text_x[0], i - s->vt.text_y[0]);
         s->vt.text_x[0] = s->vt.width;
         s->vt.text_y[0] = s->vt.height;
         s->vt.text_x[1] = 0;
         s->vt.text_y[1] = 0;
     }
     if (s->vt.cursor_invalidate) {
-        dpy_text_cursor(QEMU_CONSOLE(s), s->vt.x, s->vt.y);
+        qemu_console_text_set_cursor(QEMU_CONSOLE(s), s->vt.x, s->vt.y);
         s->vt.cursor_invalidate = 0;
     }
 }
@@ -182,14 +182,14 @@ static void vc_chr_set_echo(Chardev *chr, bool echo)
 
 void qemu_text_console_update_size(QemuTextConsole *c)
 {
-    dpy_text_resize(QEMU_CONSOLE(c), c->vt.width, c->vt.height);
+    qemu_console_text_resize(QEMU_CONSOLE(c), c->vt.width, c->vt.height);
 }
 
 static void text_console_image_update(QemuVT100 *vt, int x, int y, int width, int height)
 {
     QemuTextConsole *console = container_of(vt, QemuTextConsole, vt);
 
-    dpy_gfx_update(QEMU_CONSOLE(console), x, y, width, height);
+    qemu_console_update(QEMU_CONSOLE(console), x, y, width, height);
 }
 
 static void text_console_out_flush(QemuVT100 *vt)
@@ -228,7 +228,7 @@ static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
         s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
     }
 
-    dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
+    qemu_console_set_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
     vt100_init(&s->vt, QEMU_CONSOLE(s)->surface->image,
                text_console_image_update,
                text_console_out_flush);
diff --git a/ui/console.c b/ui/console.c
index c8e3bc300fb..05c72e19c02 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -129,26 +129,26 @@ static void gui_setup_refresh(DisplayState *ds)
     }
 }
 
-void graphic_hw_update_done(QemuConsole *con)
+void qemu_console_hw_update_done(QemuConsole *con)
 {
     if (con) {
         qemu_co_enter_all(&con->dump_queue, NULL);
     }
 }
 
-void graphic_hw_update(QemuConsole *con)
+void qemu_console_hw_update(QemuConsole *con)
 {
     if (!con) {
         return;
     }
     if (!con->hw_ops->gfx_update || con->hw_ops->gfx_update(con->hw)) {
-        graphic_hw_update_done(con);
+        qemu_console_hw_update_done(con);
     }
 }
 
-static void graphic_hw_update_bh(void *con)
+static void console_hw_update_bh(void *con)
 {
-    graphic_hw_update(con);
+    qemu_console_hw_update(con);
 }
 
 void qemu_console_co_wait_update(QemuConsole *con)
@@ -156,18 +156,18 @@ void qemu_console_co_wait_update(QemuConsole *con)
     if (qemu_co_queue_empty(&con->dump_queue)) {
         /* Defer the update, it will restart the pending coroutines */
         aio_bh_schedule_oneshot(qemu_get_aio_context(),
-                                graphic_hw_update_bh, con);
+                                console_hw_update_bh, con);
     }
     qemu_co_queue_wait(&con->dump_queue, NULL);
 
 }
 
-static void graphic_hw_gl_unblock_timer(void *opaque)
+static void console_hw_gl_unblock_timer(void *opaque)
 {
     warn_report("console: no gl-unblock within one second");
 }
 
-void graphic_hw_gl_block(QemuConsole *con, bool block)
+void qemu_console_hw_gl_block(QemuConsole *con, bool block)
 {
     uint64_t timeout;
     assert(con != NULL);
@@ -205,14 +205,14 @@ void qemu_console_set_window_id(QemuConsole *con, int window_id)
     con->window_id = window_id;
 }
 
-void graphic_hw_invalidate(QemuConsole *con)
+void qemu_console_hw_invalidate(QemuConsole *con)
 {
     if (con && con->hw_ops->invalidate) {
         con->hw_ops->invalidate(con->hw);
     }
 }
 
-void graphic_hw_text_update(QemuConsole *con, uint32_t *chardata)
+void qemu_console_hw_text_update(QemuConsole *con, uint32_t *chardata)
 {
     if (con && con->hw_ops->text_update) {
         con->hw_ops->text_update(con->hw, chardata);
@@ -502,7 +502,7 @@ qemu_graphic_console_init(Object *obj)
 {
 }
 
-bool console_has_gl(QemuConsole *con)
+bool qemu_console_has_gl(QemuConsole *con)
 {
     return con->gl != NULL;
 }
@@ -527,7 +527,7 @@ static bool console_compatible_with(QemuConsole *con,
 
     flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
 
-    if (console_has_gl(con) &&
+    if (qemu_console_has_gl(con) &&
         !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
         error_setg(errp, "Display %s is incompatible with the GL context",
                    dcl->ops->dpy_name);
@@ -535,7 +535,7 @@ static bool console_compatible_with(QemuConsole *con,
     }
 
     if (flags & GRAPHIC_FLAGS_GL &&
-        !console_has_gl(con)) {
+        !qemu_console_has_gl(con)) {
         error_setg(errp, "The console requires a GL context.");
         return false;
 
@@ -659,8 +659,8 @@ void qemu_console_register_listener(QemuConsole *con,
     vt100_update_cursor();
 }
 
-void update_displaychangelistener(DisplayChangeListener *dcl,
-                                  uint64_t interval)
+void qemu_console_listener_set_refresh(DisplayChangeListener *dcl,
+                                       uint64_t interval)
 {
     DisplayState *ds = dcl->ds;
 
@@ -690,7 +690,7 @@ static void dpy_set_ui_info_timer(void *opaque)
     con->hw_ops->ui_info(con->hw, head, &con->ui_info);
 }
 
-bool dpy_ui_info_supported(const QemuConsole *con)
+bool qemu_console_ui_info_supported(const QemuConsole *con)
 {
     if (con == NULL) {
         return false;
@@ -699,16 +699,16 @@ bool dpy_ui_info_supported(const QemuConsole *con)
     return con->hw_ops->ui_info != NULL;
 }
 
-const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
+const QemuUIInfo *qemu_console_get_ui_info(const QemuConsole *con)
 {
-    assert(dpy_ui_info_supported(con));
+    assert(qemu_console_ui_info_supported(con));
 
     return &con->ui_info;
 }
 
-int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
+int qemu_console_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
 {
-    if (!dpy_ui_info_supported(con)) {
+    if (!qemu_console_ui_info_supported(con)) {
         return -1;
     }
     if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
@@ -727,7 +727,7 @@ int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
     return 0;
 }
 
-void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
+void qemu_console_update(QemuConsole *con, int x, int y, int w, int h)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -752,15 +752,15 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
     }
 }
 
-void dpy_gfx_update_full(QemuConsole *con)
+void qemu_console_update_full(QemuConsole *con)
 {
     int w = qemu_console_get_width(con, 0);
     int h = qemu_console_get_height(con, 0);
 
-    dpy_gfx_update(con, 0, 0, w, h);
+    qemu_console_update(con, 0, 0, w, h);
 }
 
-void dpy_gfx_replace_surface(QemuConsole *con,
+void qemu_console_set_surface(QemuConsole *con,
                              DisplaySurface *surface)
 {
     static const char placeholder_msg[] = "Display output is not active.";
@@ -798,8 +798,8 @@ void dpy_gfx_replace_surface(QemuConsole *con,
     qemu_free_displaysurface(old_surface);
 }
 
-bool dpy_gfx_check_format(QemuConsole *con,
-                          pixman_format_code_t format)
+bool qemu_console_check_format(QemuConsole *con,
+                               pixman_format_code_t format)
 {
     DisplayChangeListener *dcl;
     DisplayState *s = con->ds;
@@ -834,7 +834,7 @@ static void dpy_refresh(DisplayState *s)
     }
 }
 
-void dpy_text_cursor(QemuConsole *con, int x, int y)
+void qemu_console_text_set_cursor(QemuConsole *con, int x, int y)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -849,7 +849,7 @@ void dpy_text_cursor(QemuConsole *con, int x, int y)
     }
 }
 
-void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
+void qemu_console_text_update(QemuConsole *con, int x, int y, int w, int h)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -864,7 +864,7 @@ void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
     }
 }
 
-void dpy_text_resize(QemuConsole *con, int w, int h)
+void qemu_console_text_resize(QemuConsole *con, int w, int h)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -879,7 +879,7 @@ void dpy_text_resize(QemuConsole *con, int w, int h)
     }
 }
 
-void dpy_mouse_set(QemuConsole *c, int x, int y, bool on)
+void qemu_console_set_mouse(QemuConsole *c, int x, int y, bool on)
 {
     QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
     DisplayState *s = c->ds;
@@ -898,7 +898,7 @@ void dpy_mouse_set(QemuConsole *c, int x, int y, bool on)
     }
 }
 
-void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor)
+void qemu_console_set_cursor(QemuConsole *c, QEMUCursor *cursor)
 {
     QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
     DisplayState *s = c->ds;
@@ -916,26 +916,26 @@ void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor)
     }
 }
 
-QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
-                                struct QEMUGLParams *qparams)
+QEMUGLContext qemu_console_gl_ctx_create(QemuConsole *con,
+                                         QEMUGLParams *qparams)
 {
     assert(con->gl);
     return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
 }
 
-void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
+void qemu_console_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
 {
     assert(con->gl);
     con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
 }
 
-int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
+int qemu_console_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
 {
     assert(con->gl);
     return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
 }
 
-void dpy_gl_scanout_disable(QemuConsole *con)
+void qemu_console_gl_scanout_disable(QemuConsole *con)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -953,14 +953,14 @@ void dpy_gl_scanout_disable(QemuConsole *con)
     }
 }
 
-void dpy_gl_scanout_texture(QemuConsole *con,
-                            uint32_t backing_id,
-                            bool backing_y_0_top,
-                            uint32_t backing_width,
-                            uint32_t backing_height,
-                            uint32_t x, uint32_t y,
-                            uint32_t width, uint32_t height,
-                            void *d3d_tex2d)
+void qemu_console_gl_scanout_texture(QemuConsole *con,
+                                     uint32_t backing_id,
+                                     bool backing_y_0_top,
+                                     uint32_t backing_width,
+                                     uint32_t backing_height,
+                                     uint32_t x, uint32_t y,
+                                     uint32_t width, uint32_t height,
+                                     void *d3d_tex2d)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -984,8 +984,8 @@ void dpy_gl_scanout_texture(QemuConsole *con,
     }
 }
 
-void dpy_gl_scanout_dmabuf(QemuConsole *con,
-                           QemuDmaBuf *dmabuf)
+void qemu_console_gl_scanout_dmabuf(QemuConsole *con,
+                                    QemuDmaBuf *dmabuf)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -1002,8 +1002,8 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con,
     }
 }
 
-void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
-                          bool have_hot, uint32_t hot_x, uint32_t hot_y)
+void qemu_console_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
+                                   bool have_hot, uint32_t hot_x, uint32_t hot_y)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -1019,8 +1019,8 @@ void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
     }
 }
 
-void dpy_gl_cursor_position(QemuConsole *con,
-                            uint32_t pos_x, uint32_t pos_y)
+void qemu_console_gl_cursor_position(QemuConsole *con,
+                                     uint32_t pos_x, uint32_t pos_y)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -1035,8 +1035,8 @@ void dpy_gl_cursor_position(QemuConsole *con,
     }
 }
 
-void dpy_gl_release_dmabuf(QemuConsole *con,
-                          QemuDmaBuf *dmabuf)
+void qemu_console_gl_release_dmabuf(QemuConsole *con,
+                                    QemuDmaBuf *dmabuf)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
@@ -1051,15 +1051,15 @@ void dpy_gl_release_dmabuf(QemuConsole *con,
     }
 }
 
-void dpy_gl_update(QemuConsole *con,
-                   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+void qemu_console_gl_update(QemuConsole *con,
+                            uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
     DisplayState *s = con->ds;
     DisplayChangeListener *dcl;
 
     assert(con->gl);
 
-    graphic_hw_gl_block(con, true);
+    qemu_console_hw_gl_block(con, true);
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (con != dcl->con) {
             continue;
@@ -1068,7 +1068,7 @@ void dpy_gl_update(QemuConsole *con,
             dcl->ops->dpy_gl_update(dcl, x, y, w, h);
         }
     }
-    graphic_hw_gl_block(con, false);
+    qemu_console_hw_gl_block(con, false);
 }
 
 /***********************************************************/
@@ -1105,17 +1105,17 @@ DisplayState *init_displaystate(void)
     return display_state;
 }
 
-void graphic_console_set_hwops(QemuConsole *con,
-                               const GraphicHwOps *hw_ops,
-                               void *opaque)
+void qemu_graphic_console_set_hwops(QemuConsole *con,
+                                    const GraphicHwOps *hw_ops,
+                                    void *opaque)
 {
     con->hw_ops = hw_ops;
     con->hw = opaque;
 }
 
-QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
-                                  const GraphicHwOps *hw_ops,
-                                  void *opaque)
+QemuConsole *qemu_graphic_console_create(DeviceState *dev, uint32_t head,
+                                         const GraphicHwOps *hw_ops,
+                                         void *opaque)
 {
     static const char noinit[] =
         "Guest has not initialized the display (yet).";
@@ -1134,16 +1134,16 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
         s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE);
     }
     QEMU_GRAPHIC_CONSOLE(s)->head = head;
-    graphic_console_set_hwops(s, hw_ops, opaque);
+    qemu_graphic_console_set_hwops(s, hw_ops, opaque);
     if (dev) {
         object_property_set_link(OBJECT(s), "device", OBJECT(dev),
                                  &error_abort);
     }
 
     surface = qemu_create_placeholder_surface(width, height, noinit);
-    dpy_gfx_replace_surface(s, surface);
+    qemu_console_set_surface(s, surface);
     s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
-                                       graphic_hw_gl_unblock_timer, s);
+                                       console_hw_gl_unblock_timer, s);
     return s;
 }
 
@@ -1151,7 +1151,7 @@ static const GraphicHwOps unused_ops = {
     /* no callbacks */
 };
 
-void graphic_console_close(QemuConsole *con)
+void qemu_graphic_console_close(QemuConsole *con)
 {
     static const char unplugged[] =
         "Guest display has been unplugged";
@@ -1161,13 +1161,13 @@ void graphic_console_close(QemuConsole *con)
 
     trace_console_gfx_close(con->index);
     object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
-    graphic_console_set_hwops(con, &unused_ops, NULL);
+    qemu_graphic_console_set_hwops(con, &unused_ops, NULL);
 
     if (con->gl) {
-        dpy_gl_scanout_disable(con);
+        qemu_console_gl_scanout_disable(con);
     }
     surface = qemu_create_placeholder_surface(width, height, unplugged);
-    dpy_gfx_replace_surface(con, surface);
+    qemu_console_set_surface(con, surface);
 }
 
 QemuConsole *qemu_console_lookup_default(void)
@@ -1353,7 +1353,7 @@ void qemu_console_resize(QemuConsole *s, int width, int height)
     }
 
     surface = qemu_create_displaysurface(width, height);
-    dpy_gfx_replace_surface(s, surface);
+    qemu_console_set_surface(s, surface);
 }
 
 DisplaySurface *qemu_console_surface(QemuConsole *console)
diff --git a/ui/curses.c b/ui/curses.c
index 4e2a0b25955..f8d4542768b 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -1,8 +1,8 @@
 /*
  * QEMU curses/ncurses display driver
- * 
+ *
  * Copyright (c) 2005 Andrzej Zaborowski  <balrog@zabor.org>
- * 
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
@@ -275,11 +275,11 @@ static void curses_refresh(DisplayChangeListener *dcl)
         clear();
         refresh();
         curses_calc_pad();
-        graphic_hw_invalidate(dcl->con);
+        qemu_console_hw_invalidate(dcl->con);
         invalidate = 0;
     }
 
-    graphic_hw_text_update(dcl->con, screen);
+    qemu_console_hw_text_update(dcl->con, screen);
 
     while (1) {
         /* while there are any pending key strokes to process */
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index 249760d82aa..88f58e88efb 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -200,7 +200,7 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc,
         .height = arg_height,
     };
 
-    if (!dpy_ui_info_supported(ddc->dcl.con)) {
+    if (!qemu_console_ui_info_supported(ddc->dcl.con)) {
         g_dbus_method_invocation_return_error(invocation,
                                               DBUS_DISPLAY_ERROR,
                                               DBUS_DISPLAY_ERROR_UNSUPPORTED,
@@ -208,7 +208,7 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc,
         return DBUS_METHOD_INVOCATION_HANDLED;
     }
 
-    dpy_set_ui_info(ddc->dcl.con, &info, false);
+    qemu_console_set_ui_info(ddc->dcl.con, &info, false);
     qemu_dbus_display1_console_complete_set_uiinfo(ddc->iface, invocation);
     return DBUS_METHOD_INVOCATION_HANDLED;
 }
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 45b8cc74a6b..1c9b010c560 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -241,7 +241,7 @@ static void dbus_update_gl_cb(GObject *source_object,
     }
 #endif
 
-    graphic_hw_gl_block(ddl->dcl.con, false);
+    qemu_console_hw_gl_block(ddl->dcl.con, false);
     g_object_unref(ddl);
 }
 #endif
@@ -257,7 +257,7 @@ static void dbus_call_update_gl(DisplayChangeListener *dcl,
 
     glFlush();
 #ifdef CONFIG_GBM
-    graphic_hw_gl_block(ddl->dcl.con, true);
+    qemu_console_hw_gl_block(ddl->dcl.con, true);
     qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy,
         x, y, w, h,
         G_DBUS_CALL_FLAGS_NONE,
@@ -276,7 +276,7 @@ static void dbus_call_update_gl(DisplayChangeListener *dcl,
         Error *err = NULL;
         assert(ddl->d3d_texture);
 
-        graphic_hw_gl_block(ddl->dcl.con, true);
+        qemu_console_hw_gl_block(ddl->dcl.con, true);
         if (!d3d_texture2d_release0(ddl->d3d_texture, &err)) {
             error_report_err(err);
             return;
@@ -710,7 +710,7 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl)
 {
     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
 
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
 
     if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) {
         return;
@@ -739,7 +739,7 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl)
 
 static void dbus_refresh(DisplayChangeListener *dcl)
 {
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
 }
 
 #ifdef CONFIG_OPENGL
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index 4f046c975a9..878bfebb40c 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -23,7 +23,7 @@ typedef struct egl_dpy {
 
 static void egl_refresh(DisplayChangeListener *dcl)
 {
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
 }
 
 static void egl_gfx_update(DisplayChangeListener *dcl,
@@ -161,7 +161,7 @@ static void egl_scanout_flush(DisplayChangeListener *dcl,
     }
 
     egl_fb_read(edpy->ds, &edpy->blit_fb);
-    dpy_gfx_update(edpy->dcl.con, x, y, w, h);
+    qemu_console_update(edpy->dcl.con, x, y, w, h);
 }
 
 static const DisplayChangeListenerOps egl_ops = {
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 1b5c1d4533c..9422be612a3 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -108,7 +108,7 @@ void gd_egl_draw(VirtualConsole *vc)
                 qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc);
                 return;
             }
-            graphic_hw_gl_block(vc->gfx.dcl.con, false);
+            qemu_console_hw_gl_block(vc->gfx.dcl.con, false);
         }
 #endif
     } else {
@@ -176,7 +176,7 @@ void gd_egl_refresh(DisplayChangeListener *dcl)
 #endif
     }
 
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
 
     if (vc->gfx.glupdates) {
         vc->gfx.glupdates = 0;
@@ -405,7 +405,7 @@ void gd_egl_flush(DisplayChangeListener *dcl,
 
     if (vc->gfx.guest_fb.dmabuf &&
         !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
-        graphic_hw_gl_block(vc->gfx.dcl.con, true);
+        qemu_console_hw_gl_block(vc->gfx.dcl.con, true);
         qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
         gtk_egl_set_scanout_mode(vc, true);
         gtk_widget_queue_draw_area(area, x, y, w, h);
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index ce49000d3f1..23806b9d01b 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -131,7 +131,7 @@ void gd_gl_area_draw(VirtualConsole *vc)
                 qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc);
                 return;
             }
-            graphic_hw_gl_block(vc->gfx.dcl.con, false);
+            qemu_console_hw_gl_block(vc->gfx.dcl.con, false);
         }
 #endif
     } else {
@@ -195,7 +195,7 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl)
         }
     }
 
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
 
     if (vc->gfx.glupdates) {
         vc->gfx.glupdates = 0;
@@ -347,7 +347,7 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
 
     if (vc->gfx.guest_fb.dmabuf &&
         !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
-        graphic_hw_gl_block(vc->gfx.dcl.con, true);
+        qemu_console_hw_gl_block(vc->gfx.dcl.con, true);
         qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
         gtk_gl_area_set_scanout_mode(vc, true);
     }
diff --git a/ui/gtk.c b/ui/gtk.c
index 3aaa44ff3e2..bcb67db7ee7 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -436,7 +436,7 @@ static void gd_update(DisplayChangeListener *dcl,
 
 static void gd_refresh(DisplayChangeListener *dcl)
 {
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
 }
 
 static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
@@ -602,7 +602,7 @@ void gd_hw_gl_flushed(void *vcon)
         qemu_set_fd_handler(fence_fd, NULL, NULL, NULL);
         close(fence_fd);
         qemu_dmabuf_set_fence_fd(dmabuf, -1);
-        graphic_hw_gl_block(vc->gfx.dcl.con, false);
+        qemu_console_hw_gl_block(vc->gfx.dcl.con, false);
     }
 }
 
@@ -729,27 +729,27 @@ static void gd_set_ui_refresh_rate(VirtualConsole *vc, int refresh_rate)
 {
     QemuUIInfo info;
 
-    if (!dpy_ui_info_supported(vc->gfx.dcl.con)) {
+    if (!qemu_console_ui_info_supported(vc->gfx.dcl.con)) {
         return;
     }
 
-    info = *dpy_get_ui_info(vc->gfx.dcl.con);
+    info = *qemu_console_get_ui_info(vc->gfx.dcl.con);
     info.refresh_rate = refresh_rate;
-    dpy_set_ui_info(vc->gfx.dcl.con, &info, true);
+    qemu_console_set_ui_info(vc->gfx.dcl.con, &info, true);
 }
 
 static void gd_set_ui_size(VirtualConsole *vc, gint width, gint height)
 {
     QemuUIInfo info;
 
-    if (!dpy_ui_info_supported(vc->gfx.dcl.con)) {
+    if (!qemu_console_ui_info_supported(vc->gfx.dcl.con)) {
         return;
     }
 
-    info = *dpy_get_ui_info(vc->gfx.dcl.con);
+    info = *qemu_console_get_ui_info(vc->gfx.dcl.con);
     info.width = width;
     info.height = height;
-    dpy_set_ui_info(vc->gfx.dcl.con, &info, true);
+    qemu_console_set_ui_info(vc->gfx.dcl.con, &info, true);
 }
 
 #if defined(CONFIG_OPENGL)
@@ -2333,7 +2333,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
     gd_connect_vc_gfx_signals(vc);
     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
 
-    if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
+    if (qemu_console_ui_info_supported(vc->gfx.dcl.con)) {
         zoom_to_fit = true;
     }
     if (s->opts->u.gtk.has_zoom_to_fit) {
diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c
index 73052383c2e..68a3aff7151 100644
--- a/ui/sdl2-2d.c
+++ b/ui/sdl2-2d.c
@@ -129,7 +129,7 @@ void sdl2_2d_refresh(DisplayChangeListener *dcl)
     struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
 
     assert(!scon->opengl);
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
     sdl2_poll_events(scon);
 }
 
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index bb066cdd885..1547ad2f6f8 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -115,7 +115,7 @@ void sdl2_gl_refresh(DisplayChangeListener *dcl)
 
     assert(scon->opengl);
 
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
     if (scon->updates && scon->real_window) {
         scon->updates = 0;
         sdl2_gl_render_surface(scon);
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 105e1ee9399..48f0a564c2f 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -599,13 +599,11 @@ static void handle_windowevent(SDL_Event *ev)
 
     switch (ev->window.event) {
     case SDL_WINDOWEVENT_RESIZED:
-        {
-            dpy_set_ui_info(scon->dcl.con,
-                &(QemuUIInfo) {
-                    .width = ev->window.data1,
-                    .height = ev->window.data2,
-                }, true);
-        }
+        qemu_console_set_ui_info(scon->dcl.con,
+            &(QemuUIInfo) {
+                .width = ev->window.data1,
+                .height = ev->window.data2,
+            }, true);
         sdl2_redraw(scon);
         break;
     case SDL_WINDOWEVENT_EXPOSED:
@@ -632,10 +630,10 @@ static void handle_windowevent(SDL_Event *ev)
         }
         break;
     case SDL_WINDOWEVENT_RESTORED:
-        update_displaychangelistener(&scon->dcl, GUI_REFRESH_INTERVAL_DEFAULT);
+        qemu_console_listener_set_refresh(&scon->dcl, GUI_REFRESH_INTERVAL_DEFAULT);
         break;
     case SDL_WINDOWEVENT_MINIMIZED:
-        update_displaychangelistener(&scon->dcl, 500);
+        qemu_console_listener_set_refresh(&scon->dcl, 500);
         break;
     case SDL_WINDOWEVENT_CLOSE:
         if (qemu_console_is_graphic(scon->dcl.con)) {
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 44e8637ea4f..c5b86c42d52 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -468,7 +468,7 @@ void qemu_spice_cursor_refresh_bh(void *opaque)
         assert(ssd->dcl.con);
         cursor_ref(c);
         qemu_mutex_unlock(&ssd->lock);
-        dpy_cursor_define(ssd->dcl.con, c);
+        qemu_console_set_cursor(ssd->dcl.con, c);
         qemu_mutex_lock(&ssd->lock);
         cursor_unref(c);
     }
@@ -481,7 +481,7 @@ void qemu_spice_cursor_refresh_bh(void *opaque)
         ssd->mouse_x = -1;
         ssd->mouse_y = -1;
         qemu_mutex_unlock(&ssd->lock);
-        dpy_mouse_set(ssd->dcl.con, x, y, true);
+        qemu_console_set_mouse(ssd->dcl.con, x, y, true);
     } else {
         qemu_mutex_unlock(&ssd->lock);
     }
@@ -489,7 +489,7 @@ void qemu_spice_cursor_refresh_bh(void *opaque)
 
 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
 {
-    graphic_hw_update(ssd->dcl.con);
+    qemu_console_hw_update(ssd->dcl.con);
 
     WITH_QEMU_LOCK_GUARD(&ssd->lock) {
         if (QTAILQ_EMPTY(&ssd->updates) && ssd->ds) {
@@ -668,7 +668,7 @@ static int interface_client_monitors_config(QXLInstance *sin,
     QemuUIInfo info;
     int head;
 
-    if (!dpy_ui_info_supported(ssd->dcl.con)) {
+    if (!qemu_console_ui_info_supported(ssd->dcl.con)) {
         return 0; /* == not supported by guest */
     }
 
@@ -676,7 +676,7 @@ static int interface_client_monitors_config(QXLInstance *sin,
         return 1;
     }
 
-    info = *dpy_get_ui_info(ssd->dcl.con);
+    info = *qemu_console_get_ui_info(ssd->dcl.con);
 
     head = qemu_console_get_index(ssd->dcl.con);
     if (mc->num_of_monitors > head) {
@@ -690,7 +690,7 @@ static int interface_client_monitors_config(QXLInstance *sin,
     }
 
     trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height);
-    dpy_set_ui_info(ssd->dcl.con, &info, false);
+    qemu_console_set_ui_info(ssd->dcl.con, &info, false);
     return 1;
 }
 
@@ -817,7 +817,7 @@ static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block)
     } else {
         timer_del(ssd->gl_unblock_timer);
     }
-    graphic_hw_gl_block(ssd->dcl.con, block);
+    qemu_console_hw_gl_block(ssd->dcl.con, block);
 }
 
 static void qemu_spice_gl_unblock_bh(void *opaque)
@@ -861,7 +861,7 @@ static void spice_gl_refresh(DisplayChangeListener *dcl)
         return;
     }
 
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
     if (ssd->gl_updates && ssd->have_surface) {
         qemu_spice_gl_block(ssd, true);
         glFlush();
diff --git a/ui/vnc.c b/ui/vnc.c
index 9daf295a763..de33b03ff92 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2323,8 +2323,8 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel,
 
     set_pixel_conversion(vs);
 
-    graphic_hw_invalidate(vs->vd->dcl.con);
-    graphic_hw_update(vs->vd->dcl.con);
+    qemu_console_hw_invalidate(vs->vd->dcl.con);
+    qemu_console_hw_update(vs->vd->dcl.con);
 }
 
 static void pixel_format_message (VncState *vs) {
@@ -2382,7 +2382,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
     VncDisplay *vd = vs->vd;
 
     if (data[0] > 3) {
-        update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
+        qemu_console_listener_set_refresh(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
     }
 
     switch (data[0]) {
@@ -2636,8 +2636,8 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
         h = read_u16(data, 4);
 
         trace_vnc_msg_client_set_desktop_size(vs, vs->ioc, w, h, screens);
-        if (dpy_ui_info_supported(vs->vd->dcl.con)) {
-            dpy_set_ui_info(vs->vd->dcl.con,
+        if (qemu_console_ui_info_supported(vs->vd->dcl.con)) {
+            qemu_console_set_ui_info(vs->vd->dcl.con,
                 &(QemuUIInfo){ .width = w, .height = h }, false);
             vnc_desktop_resize_ext(vs, 4 /* Request forwarded */);
         } else {
@@ -3240,14 +3240,14 @@ static void vnc_refresh(DisplayChangeListener *dcl)
     int has_dirty, rects = 0;
 
     if (QTAILQ_EMPTY(&vd->clients)) {
-        update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_MAX);
+        qemu_console_listener_set_refresh(&vd->dcl, VNC_REFRESH_INTERVAL_MAX);
         return;
     }
 
-    graphic_hw_update(vd->dcl.con);
+    qemu_console_hw_update(vd->dcl.con);
 
     if (vnc_trylock_display(vd)) {
-        update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
+        qemu_console_listener_set_refresh(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
         return;
     }
 
@@ -3321,7 +3321,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
               sioc, websocket, vs->auth, vs->subauth);
 
     VNC_DEBUG("New client on socket %p\n", vs->sioc);
-    update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
+    qemu_console_listener_set_refresh(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
     qio_channel_set_blocking(vs->ioc, false, &error_abort);
     g_clear_handle_id(&vs->ioc_tag, g_source_remove);
     if (websocket) {
@@ -3361,7 +3361,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
         vnc_update_server_surface(vd);
     }
 
-    graphic_hw_update(vd->dcl.con);
+    qemu_console_hw_update(vd->dcl.con);
 
     if (!vs->websocket) {
         vnc_start_protocol(vs);
@@ -3417,7 +3417,7 @@ static void vmstate_change_handler(void *opaque, bool running, RunState state)
     if (state != RUN_STATE_RUNNING) {
         return;
     }
-    update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
+    qemu_console_listener_set_refresh(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
 }
 
 static bool vnc_display_open(VncDisplay *vd, Error **errp);
diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m
index 77d80fb7cef..be0061b9db2 100644
--- a/hw/display/apple-gfx.m
+++ b/hw/display/apple-gfx.m
@@ -317,8 +317,8 @@ static void apple_gfx_render_frame_completed_bh(void *opaque)
             copy_mtl_texture_to_surface_mem(s->texture, surface_data(s->surface));
             if (s->gfx_update_requested) {
                 s->gfx_update_requested = false;
-                dpy_gfx_update_full(s->con);
-                graphic_hw_update_done(s->con);
+                qemu_console_update_full(s->con);
+                qemu_console_hw_update_done(s->con);
                 s->new_frame_ready = false;
             } else {
                 s->new_frame_ready = true;
@@ -337,7 +337,7 @@ static bool apple_gfx_fb_update_display(void *opaque)
 
     assert(bql_locked());
     if (s->new_frame_ready) {
-        dpy_gfx_update_full(s->con);
+        qemu_console_update_full(s->con);
         s->new_frame_ready = false;
     } else if (s->pending_frames > 0) {
         s->gfx_update_requested = true;
@@ -380,14 +380,14 @@ static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height)
             (s->texture.storageMode == MTLStorageModeManaged);
     }
 
-    dpy_gfx_replace_surface(s->con, s->surface);
+    qemu_console_set_surface(s->con, s->surface);
 }
 
 static void update_cursor(AppleGFXState *s)
 {
     assert(bql_locked());
-    dpy_mouse_set(s->con, s->pgdisp.cursorPosition.x,
-                  s->pgdisp.cursorPosition.y, qatomic_read(&s->cursor_show));
+    qemu_console_set_mouse(s->con, s->pgdisp.cursorPosition.x,
+                           s->pgdisp.cursorPosition.y, qatomic_read(&s->cursor_show));
 }
 
 static void update_cursor_bh(void *opaque)
@@ -443,7 +443,7 @@ static void set_cursor_glyph(void *opaque)
             }
             px_data += padding_bytes_per_row;
         }
-        dpy_cursor_define(s->con, s->cursor);
+        qemu_console_set_cursor(s->con, s->cursor);
         update_cursor(s);
     }
     [glyph release];
@@ -792,7 +792,7 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
         apple_gfx_create_display_mode_array(display_modes, num_display_modes);
     [mode_array release];
 
-    s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s);
+    s->con = qemu_graphic_console_create(dev, 0, &apple_gfx_fb_ops, s);
     return true;
 }
 
diff --git a/ui/cocoa.m b/ui/cocoa.m
index aaf82421589..98394cdc507 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -421,7 +421,7 @@ - (void) selectConsoleLocked:(unsigned int)index
         return;
     }
 
-    unregister_displaychangelistener(&dcl);
+    qemu_console_unregister_listener(&dcl);
     qkbd_state_switch_console(kbd, con);
     qemu_console_register_listener(con, &dcl, &dcl_ops);
     [self notifyMouseModeChange];
@@ -669,8 +669,8 @@ - (void) updateUIInfoLocked
             CVTime period = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLink);
             CVDisplayLinkRelease(displayLink);
             if (!(period.flags & kCVTimeIsIndefinite)) {
-                update_displaychangelistener(&dcl,
-                                             1000 * period.timeValue / period.timeScale);
+                qemu_console_listener_set_refresh(&dcl,
+                                                  1000 * period.timeValue / period.timeScale);
                 info.refresh_rate = (int64_t)1000 * period.timeScale / period.timeValue;
             }
         }
@@ -688,7 +688,7 @@ - (void) updateUIInfoLocked
     info.width = frameSize.width * [[self window] backingScaleFactor];
     info.height = frameSize.height * [[self window] backingScaleFactor];
 
-    dpy_set_ui_info(dcl.con, &info, TRUE);
+    qemu_console_set_ui_info(dcl.con, &info, TRUE);
 }
 
 #pragma clang diagnostic pop
@@ -2056,7 +2056,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
     COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
-    graphic_hw_update(dcl->con);
+    qemu_console_hw_update(dcl->con);
 
     if (cbchangecount != [[NSPasteboard generalPasteboard] changeCount]) {
         qemu_clipboard_info_unref(cbinfo);

-- 
2.53.0



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

* [PATCH 55/60] ui/console: move console_handle_touch_event() to input
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (53 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 54/60] ui/console: rename public API to use consistent qemu_console_ prefix Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-17  8:51 ` [PATCH 56/60] ui: extract common sources into a static library Marc-André Lureau
                   ` (5 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

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.

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 f8163ef96a8..6968a1e35f3 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 05c72e19c02..d1079348970 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -550,71 +550,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 88f58e88efb..b8e5c57b148 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -423,9 +423,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 bcb67db7ee7..3b84224d8e8 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.53.0



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

* [PATCH 56/60] ui: extract common sources into a static library
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (54 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 55/60] ui/console: move console_handle_touch_event() to input Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-17  8:51 ` [PATCH 57/60] tests: rename the dbus-daemon helper script Marc-André Lureau
                   ` (4 subsequent siblings)
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

Move clipboard, cursor, display-surface, input-keymap, kbd-state,
keymaps, vt100, and qemu-pixman into a separate static library 'qemuui'.
This allows these common UI sources to be linked by targets outside of
the system emulator build, such as standalone VNC or D-Bus display
binaries.

keymaps generation has to be moved earlier, so that header dependency
are resolved first.

The library objects are re-exported via a dependency so existing
system_ss consumers are unaffected.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/meson.build | 103 ++++++++++++++++++++++++++++++---------------------------
 1 file changed, 55 insertions(+), 48 deletions(-)

diff --git a/ui/meson.build b/ui/meson.build
index f959f8972b5..c3f4d03eaf8 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -1,25 +1,67 @@
-system_ss.add(pixman)
+keymaps = [
+  ['atset1', 'qcode'],
+  ['linux', 'qcode'],
+  ['qcode', 'atset1'],
+  ['qcode', 'atset2'],
+  ['qcode', 'atset3'],
+  ['qcode', 'linux'],
+  ['qcode', 'qnum'],
+  ['qcode', 'sun'],
+  ['qnum', 'qcode'],
+  ['usb', 'qcode'],
+  ['win32', 'qcode'],
+  ['x11', 'qcode'],
+  ['xorgevdev', 'qcode'],
+  ['xorgkbd', 'qcode'],
+  ['xorgxquartz', 'qcode'],
+  ['xorgxwin', 'qcode'],
+  ['osx', 'qcode'],
+]
+
+if have_system or xkbcommon.found()
+  keycodemapdb_proj = subproject('keycodemapdb', required: true)
+  foreach e : keymaps
+    output = 'input-keymap-@0@-to-@1@.c.inc'.format(e[0], e[1])
+    genh += custom_target(output,
+                  output: output,
+                  capture: true,
+                  input: keycodemapdb_proj.get_variable('keymaps_csv'),
+                  command: [python, keycodemapdb_proj.get_variable('keymap_gen').full_path(),
+                            'code-map', '--lang', 'glib2',
+                            '--varname', 'qemu_input_map_@0@_to_@1@'.format(e[0], e[1]),
+                            '@INPUT0@', e[0], e[1]])
+  endforeach
+endif
+
+libui_sources = files(
+    'clipboard.c',
+    'console.c',
+    'cursor.c',
+    'dmabuf.c',
+    'display-surface.c',
+    'input-keymap.c',
+    'kbd-state.c',
+    'keymaps.c',
+    'qemu-pixman.c',
+    'vgafont.c',
+  )
+if pixman.found()
+  libui_sources += files('cp437.c', 'vt100.c')
+endif
+libui = static_library('qemuui', libui_sources + genh,
+  dependencies: [pixman],
+  build_by_default: false)
+ui = declare_dependency(objects: libui.extract_all_objects(recursive: false), dependencies: [pixman])
 system_ss.add(png)
 system_ss.add(files(
-  'clipboard.c',
-  'console.c',
-  'cp437.c',
-  'cursor.c',
-  'display-surface.c',
-  'dmabuf.c',
-  'input-keymap.c',
   'input-legacy.c',
   'input-barrier.c',
   'input.c',
-  'kbd-state.c',
-  'keymaps.c',
-  'qemu-pixman.c',
   'ui-hmp-cmds.c',
   'ui-qmp-cmds.c',
   'util.c',
-  'vgafont.c',
-  'vt100.c',
 ))
+system_ss.add(ui)
 system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c'))
 if dbus_display
   system_ss.add(files('dbus-module.c'))
@@ -151,41 +193,6 @@ if spice.found()
   endif
 endif
 
-keymaps = [
-  ['atset1', 'qcode'],
-  ['linux', 'qcode'],
-  ['qcode', 'atset1'],
-  ['qcode', 'atset2'],
-  ['qcode', 'atset3'],
-  ['qcode', 'linux'],
-  ['qcode', 'qnum'],
-  ['qcode', 'sun'],
-  ['qnum', 'qcode'],
-  ['usb', 'qcode'],
-  ['win32', 'qcode'],
-  ['x11', 'qcode'],
-  ['xorgevdev', 'qcode'],
-  ['xorgkbd', 'qcode'],
-  ['xorgxquartz', 'qcode'],
-  ['xorgxwin', 'qcode'],
-  ['osx', 'qcode'],
-]
-
-if have_system or xkbcommon.found()
-  keycodemapdb_proj = subproject('keycodemapdb', required: true)
-  foreach e : keymaps
-    output = 'input-keymap-@0@-to-@1@.c.inc'.format(e[0], e[1])
-    genh += custom_target(output,
-                  output: output,
-                  capture: true,
-                  input: keycodemapdb_proj.get_variable('keymaps_csv'),
-                  command: [python, keycodemapdb_proj.get_variable('keymap_gen').full_path(),
-                            'code-map', '--lang', 'glib2',
-                            '--varname', 'qemu_input_map_@0@_to_@1@'.format(e[0], e[1]),
-                            '@INPUT0@', e[0], e[1]])
-  endforeach
-endif
-
 subdir('shader')
 
 if have_system

-- 
2.53.0



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

* [PATCH 57/60] tests: rename the dbus-daemon helper script
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (55 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 56/60] ui: extract common sources into a static library Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-24 15:05   ` Daniel P. Berrangé
  2026-03-17  8:51 ` [PATCH 58/60] tests/qtest: fix dbus-vmstate-test compilation Marc-André Lureau
                   ` (3 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

The following patches are going to use it for qemu-vnc.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/{dbus-vmstate-daemon.sh => dbus-daemon.sh} | 0
 tests/qtest/meson.build                          | 2 +-
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/dbus-vmstate-daemon.sh b/tests/dbus-daemon.sh
similarity index 100%
rename from tests/dbus-vmstate-daemon.sh
rename to tests/dbus-daemon.sh
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index be4fa627b5f..b735f55fc40 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -435,7 +435,7 @@ foreach dir : target_dirs
     qtest_env.set('QTEST_QEMU_IMG', './qemu-img')
     test_deps += [qemu_img]
   endif
-  qtest_env.set('G_TEST_DBUS_DAEMON', meson.project_source_root() / 'tests/dbus-vmstate-daemon.sh')
+  qtest_env.set('G_TEST_DBUS_DAEMON', meson.project_source_root() / 'tests/dbus-daemon.sh')
   qtest_env.set('QTEST_QEMU_BINARY', './qemu-system-' + target_base)
   if have_tools and have_vhost_user_blk_server
     qtest_env.set('QTEST_QEMU_STORAGE_DAEMON_BINARY', './storage-daemon/qemu-storage-daemon')

-- 
2.53.0



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

* [PATCH 58/60] tests/qtest: fix dbus-vmstate-test compilation
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (56 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 57/60] tests: rename the dbus-daemon helper script Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-17 12:28   ` Fabiano Rosas
  2026-03-17  8:51 ` [PATCH 59/60] tests/qtest: drop DBUS_VMSTATE_TEST_TMPDIR Marc-André Lureau
                   ` (2 subsequent siblings)
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

The test is still disabled, and trying to compile it will fail now.

Fix it, and remove the G_TEST_DBUS_DAEMON setting, since it is passed by
meson test. Fwiw, the test passes here.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/qtest/dbus-vmstate-test.c | 11 ++---------
 tests/qtest/meson.build         |  3 ++-
 2 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c
index 6c990864e3e..ed4a52d0255 100644
--- a/tests/qtest/dbus-vmstate-test.c
+++ b/tests/qtest/dbus-vmstate-test.c
@@ -3,7 +3,7 @@
 #include <gio/gio.h>
 #include "libqtest.h"
 #include "dbus-vmstate1.h"
-#include "migration-helpers.h"
+#include "migration/migration-qmp.h"
 
 static char *workdir;
 
@@ -229,7 +229,7 @@ test_dbus_vmstate(Test *test)
 
     thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop);
 
-    migrate_qmp(src_qemu, uri, "{}");
+    migrate_qmp(src_qemu, dst_qemu, uri, NULL, "{}");
     test->src_qemu = src_qemu;
     if (test->migrate_fail) {
         wait_for_migration_fail(src_qemu, true);
@@ -342,15 +342,8 @@ int
 main(int argc, char **argv)
 {
     GError *err = NULL;
-    g_autofree char *dbus_daemon = NULL;
     int ret;
 
-    dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR),
-                                   "tests",
-                                   "dbus-vmstate-daemon.sh",
-                                   NULL);
-    g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true);
-
     g_test_init(&argc, &argv, NULL);
 
     workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err);
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index b735f55fc40..5f8cff172c8 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -130,7 +130,8 @@ if dbus_daemon.found() and gdbus_codegen.found()
                                 input: meson.project_source_root() / 'backends/dbus-vmstate1.xml',
                                 command: [gdbus_codegen, '@INPUT@',
                                           '--interface-prefix', 'org.qemu',
-                                          '--generate-c-code', '@BASENAME@']).to_list()
+                                          '--generate-c-code', '@BASENAME@',
+                                          '--output-directory', meson.current_build_dir()]).to_list()
 else
   dbus_vmstate1 = []
 endif

-- 
2.53.0



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

* [PATCH 59/60] tests/qtest: drop DBUS_VMSTATE_TEST_TMPDIR
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (57 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 58/60] tests/qtest: fix dbus-vmstate-test compilation Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-17  8:51 ` [PATCH 60/60] contrib/qemu-vnc: add standalone VNC server over D-Bus Marc-André Lureau
  2026-03-24 17:36 ` [PATCH 00/60] ui: " Daniel P. Berrangé
  60 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

It can rely on the location of the temporary configuration instead.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/qtest/dbus-vmstate-test.c | 2 --
 tests/dbus-daemon.sh            | 2 +-
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c
index ed4a52d0255..23d1c872f71 100644
--- a/tests/qtest/dbus-vmstate-test.c
+++ b/tests/qtest/dbus-vmstate-test.c
@@ -352,8 +352,6 @@ main(int argc, char **argv)
         exit(1);
     }
 
-    g_setenv("DBUS_VMSTATE_TEST_TMPDIR", workdir, true);
-
     qtest_add_func("/dbus-vmstate/without-list",
                    test_dbus_vmstate_without_list);
     qtest_add_func("/dbus-vmstate/with-list",
diff --git a/tests/dbus-daemon.sh b/tests/dbus-daemon.sh
index 474e2501548..c4a50c73774 100755
--- a/tests/dbus-daemon.sh
+++ b/tests/dbus-daemon.sh
@@ -26,7 +26,7 @@ write_config()
     cat > "$CONF" <<EOF
 <busconfig>
   <type>session</type>
-  <listen>unix:tmpdir=$DBUS_VMSTATE_TEST_TMPDIR</listen>
+  <listen>unix:tmpdir=$(dirname "$CONF")</listen>
 
   <policy context="default">
      <!-- Holes must be punched in service configuration files for

-- 
2.53.0



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

* [PATCH 60/60] contrib/qemu-vnc: add standalone VNC server over D-Bus
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (58 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 59/60] tests/qtest: drop DBUS_VMSTATE_TEST_TMPDIR Marc-André Lureau
@ 2026-03-17  8:51 ` Marc-André Lureau
  2026-03-24 15:24   ` Daniel P. Berrangé
  2026-03-24 17:36 ` [PATCH 00/60] ui: " Daniel P. Berrangé
  60 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17  8:51 UTC (permalink / raw)
  To: qemu-devel

Add a standalone VNC server binary that connects to a running QEMU
instance via the D-Bus display interface (org.qemu.Display1, via the bus
or directly p2p). This allows serving a VNC display without compiling
VNC support directly into the QEMU system emulator, and enables running
the VNC server as a separate process with independent lifecycle and
privilege domain.

Built only when both VNC and D-Bus display support are enabled.
If we wanted to have qemu -vnc disabled, and qemu-vnc built, we would
need to split CONFIG_VNC. This is left as a future exercise.

I left out for now:
 - sasl & tls authz
 - some runtime functionalities (better done by restarting)
 - a few legacy options
 - Windows support

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 MAINTAINERS                    |   5 +
 docs/conf.py                   |   3 +
 docs/interop/dbus-display.rst  |   2 +
 docs/interop/dbus-vnc.rst      |  26 ++
 docs/interop/index.rst         |   1 +
 docs/meson.build               |   1 +
 docs/tools/index.rst           |   1 +
 docs/tools/qemu-vnc.rst        | 199 +++++++++++
 meson.build                    |  17 +
 contrib/qemu-vnc/qemu-vnc.h    |  46 +++
 contrib/qemu-vnc/trace.h       |   4 +
 contrib/qemu-vnc/audio.c       | 307 +++++++++++++++++
 contrib/qemu-vnc/chardev.c     | 127 +++++++
 contrib/qemu-vnc/clipboard.c   | 378 +++++++++++++++++++++
 contrib/qemu-vnc/console.c     | 168 ++++++++++
 contrib/qemu-vnc/dbus.c        | 439 ++++++++++++++++++++++++
 contrib/qemu-vnc/display.c     | 456 +++++++++++++++++++++++++
 contrib/qemu-vnc/input.c       | 239 ++++++++++++++
 contrib/qemu-vnc/qemu-vnc.c    | 450 +++++++++++++++++++++++++
 contrib/qemu-vnc/stubs.c       |  66 ++++
 contrib/qemu-vnc/utils.c       |  59 ++++
 tests/qtest/dbus-vnc-test.c    | 733 +++++++++++++++++++++++++++++++++++++++++
 contrib/qemu-vnc/meson.build   |  26 ++
 contrib/qemu-vnc/qemu-vnc1.xml | 174 ++++++++++
 contrib/qemu-vnc/trace-events  |  20 ++
 meson_options.txt              |   2 +
 scripts/meson-buildoptions.sh  |   3 +
 tests/dbus-daemon.sh           |  14 +-
 tests/qtest/meson.build        |   8 +
 29 files changed, 3971 insertions(+), 3 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 97f2759138d..aa2d87dca82 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2823,6 +2823,11 @@ F: docs/interop/vhost-user-gpu.rst
 F: contrib/vhost-user-gpu
 F: hw/display/vhost-user-*
 
+qemu-vnc:
+M: Marc-André Lureau <marcandre.lureau@redhat.com>
+S: Maintained
+F: contrib/qemu-vnc
+
 Cirrus VGA
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Odd Fixes
diff --git a/docs/conf.py b/docs/conf.py
index f835904ba1e..7e35d2158d3 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -333,6 +333,9 @@
     ('tools/qemu-trace-stap', 'qemu-trace-stap',
      'QEMU SystemTap trace tool',
      [], 1),
+    ('tools/qemu-vnc', 'qemu-vnc',
+     'QEMU standalone VNC server',
+     [], 1),
 ]
 man_make_section_directory = False
 
diff --git a/docs/interop/dbus-display.rst b/docs/interop/dbus-display.rst
index 8c6e8e0f5a8..87648e91dc0 100644
--- a/docs/interop/dbus-display.rst
+++ b/docs/interop/dbus-display.rst
@@ -1,3 +1,5 @@
+.. _dbus-display:
+
 D-Bus display
 =============
 
diff --git a/docs/interop/dbus-vnc.rst b/docs/interop/dbus-vnc.rst
new file mode 100644
index 00000000000..88b1c4ea50f
--- /dev/null
+++ b/docs/interop/dbus-vnc.rst
@@ -0,0 +1,26 @@
+D-Bus VNC
+=========
+
+The ``qemu-vnc`` standalone VNC server exposes a D-Bus interface for management
+and monitoring of VNC connections.
+
+The service is available on the bus under the well-known name ``org.qemu.vnc``.
+Objects are exported under ``/org/qemu/Vnc1/``.
+
+.. contents::
+   :local:
+   :depth: 1
+
+.. only:: sphinx4
+
+   .. dbus-doc:: contrib/qemu-vnc/qemu-vnc1.xml
+
+.. only:: not sphinx4
+
+   .. warning::
+      Sphinx 4 is required to build D-Bus documentation.
+
+      This is the content of ``contrib/qemu-vnc/qemu-vnc1.xml``:
+
+   .. literalinclude:: ../../contrib/qemu-vnc/qemu-vnc1.xml
+      :language: xml
diff --git a/docs/interop/index.rst b/docs/interop/index.rst
index d830c5c4104..2cf3a8c9aa3 100644
--- a/docs/interop/index.rst
+++ b/docs/interop/index.rst
@@ -13,6 +13,7 @@ are useful for making QEMU interoperate with other software.
    dbus
    dbus-vmstate
    dbus-display
+   dbus-vnc
    live-block-operations
    nbd
    parallels
diff --git a/docs/meson.build b/docs/meson.build
index 7e54b01e6a0..c3e9fb05846 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -54,6 +54,7 @@ if build_docs
         'qemu-pr-helper.8': (have_tools ? 'man8' : ''),
         'qemu-storage-daemon.1': (have_tools ? 'man1' : ''),
         'qemu-trace-stap.1': (stap.found() ? 'man1' : ''),
+        'qemu-vnc.1': (have_qemu_vnc ? 'man1' : ''),
         'qemu.1': 'man1',
         'qemu-block-drivers.7': 'man7',
         'qemu-cpu-models.7': 'man7'
diff --git a/docs/tools/index.rst b/docs/tools/index.rst
index 1e88ae48cdc..868c3c4d9d8 100644
--- a/docs/tools/index.rst
+++ b/docs/tools/index.rst
@@ -16,3 +16,4 @@ command line utilities and other standalone programs.
    qemu-pr-helper
    qemu-trace-stap
    qemu-vmsr-helper
+   qemu-vnc
diff --git a/docs/tools/qemu-vnc.rst b/docs/tools/qemu-vnc.rst
new file mode 100644
index 00000000000..d7207cc49e5
--- /dev/null
+++ b/docs/tools/qemu-vnc.rst
@@ -0,0 +1,199 @@
+=============================
+QEMU standalone VNC server
+=============================
+
+Synopsis
+--------
+
+**qemu-vnc** [*OPTION*]
+
+Description
+-----------
+
+``qemu-vnc`` is a standalone VNC server that connects to a running QEMU
+instance via the D-Bus display interface
+(:ref:`dbus-display`).  It re-exports the
+guest display, keyboard, mouse, audio, clipboard, and serial console
+chardevs over the VNC protocol, allowing VNC clients to interact with
+the virtual machine without QEMU itself binding a VNC socket.
+
+The server connects to a QEMU instance that has been started with
+``-display dbus`` and registers as a D-Bus display listener.
+
+The following features are supported:
+
+* Graphical console display (scanout and incremental updates)
+* Shared-memory scanout via Unix file-descriptor passing
+* Hardware cursor
+* Keyboard input (translated to QEMU key codes)
+* Absolute and relative mouse input
+* Mouse button events
+* Audio playback forwarding to VNC clients
+* Clipboard sharing (text) between guest and VNC client
+* Serial console chardevs exposed as VNC text consoles
+* TLS encryption (x509 credentials)
+* VNC password authentication (``--password`` flag or systemd credentials)
+* Lossy (JPEG) compression
+* WebSocket transport
+
+Options
+-------
+
+.. program:: qemu-vnc
+
+.. option:: -a ADDRESS, --dbus-address=ADDRESS
+
+  D-Bus address to connect to.  When not specified, ``qemu-vnc``
+  connects to the session bus.
+
+.. option:: -p FD, --dbus-p2p-fd=FD
+
+  File descriptor of an inherited Unix socket for a peer-to-peer D-Bus
+  connection to QEMU.  This is mutually exclusive with
+  ``--dbus-address`` and ``--bus-name``.
+
+.. option:: -n NAME, --bus-name=NAME
+
+  D-Bus bus name of the QEMU instance to connect to.  The default is
+  ``org.qemu``.  When a custom ``--dbus-address`` is given without a
+  bus name, peer-to-peer D-Bus is used.
+
+.. option:: -c N, --console=N
+
+  Console number to attach to (default 0).
+
+.. option:: -l ADDR, --vnc-addr=ADDR
+
+  VNC listen address in the same format as the QEMU ``-vnc`` option
+  (default ``localhost:0``, i.e. TCP port 5900).
+
+.. option:: -w ADDR, --websocket=ADDR
+
+  Enable WebSocket transport on the given address.  *ADDR* can be a
+  port number or an *address:port* pair.
+
+.. option:: -t DIR, --tls-creds=DIR
+
+  Directory containing TLS x509 credentials (``ca-cert.pem``,
+  ``server-cert.pem``, ``server-key.pem``).  When specified, the VNC
+  server requires TLS from connecting clients.
+
+.. option:: -s POLICY, --share=POLICY
+
+  Set display sharing policy.  *POLICY* is one of
+  ``allow-exclusive``, ``force-shared``, or ``ignore``.
+
+  ``allow-exclusive`` allows clients to ask for exclusive access.
+  As suggested by the RFB spec this is implemented by dropping other
+  connections.  Connecting multiple clients in parallel requires all
+  clients asking for a shared session (vncviewer: -shared switch).
+  This is the default.
+
+  ``force-shared`` disables exclusive client access.  Useful for
+  shared desktop sessions, where you don't want someone forgetting to
+  specify -shared disconnect everybody else.
+
+  ``ignore`` completely ignores the shared flag and allows everybody
+  to connect unconditionally.  Doesn't conform to the RFB spec but
+  is traditional QEMU behavior.
+
+.. option:: -k LAYOUT, --keyboard-layout=LAYOUT
+
+  Keyboard layout (e.g. ``en-us``).  Passed through to the VNC server
+  for key-code translation.
+
+.. option:: -C NAME, --vt-chardev=NAME
+
+  Chardev D-Bus name to expose as a VNC text console.  This option may
+  be given multiple times to expose several chardevs.  When not
+  specified, the defaults ``org.qemu.console.serial.0`` and
+  ``org.qemu.monitor.hmp.0`` are used.
+
+.. option:: -N, --no-vt
+
+  Do not expose any chardevs as text consoles.  This overrides the
+  default chardev list and any ``--vt-chardev`` options.
+
+.. option:: -T PATTERN, --trace=PATTERN
+
+  Trace options, same syntax as the QEMU ``-trace`` option.
+
+.. option:: --password
+
+  Require VNC password authentication from connecting clients.  The
+  password is set at runtime via the D-Bus ``SetPassword`` method (see
+  :doc:`/interop/dbus-vnc`).  Clients will not be able to connect
+  until a password has been set.
+
+  This option is ignored when a systemd credential password is
+  present, since password authentication is already enabled via
+  ``password-secret`` in that case.
+
+.. option:: --lossy
+
+  Enable  lossy  compression methods (gradient, JPEG, ...). If this option
+  is set, VNC client may receive lossy framebuffer updates depending on its
+  encoding settings. Enabling this option can save a lot of bandwidth at
+  the expense of quality.
+
+.. option:: --non-adaptive
+
+  Disable adaptive encodings. Adaptive encodings are enabled by default.
+  An adaptive encoding will try to detect frequently updated screen regions,
+  and send updates in these  regions  using  a lossy encoding (like JPEG).
+  This can be really helpful to save bandwidth when playing videos.
+  Disabling adaptive encodings restores the original static behavior of
+  encodings like Tight.
+
+.. option:: -V, --version
+
+  Print version information and exit.
+
+Examples
+--------
+
+Start QEMU with the D-Bus display backend::
+
+    qemu-system-x86_64 -display dbus -drive file=disk.qcow2
+
+Then attach ``qemu-vnc``::
+
+    qemu-vnc
+
+A VNC client can now connect to ``localhost:5900``.
+
+To listen on a different port with TLS::
+
+    qemu-vnc --vnc-addr localhost:1 --tls-creds /etc/pki/qemu-vnc
+
+To connect to a specific D-Bus address (peer-to-peer)::
+
+    qemu-vnc --dbus-address unix:path=/tmp/qemu-dbus.sock
+
+VNC Password Authentication
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two ways to enable VNC password authentication:
+
+1. **``--password`` flag** — start ``qemu-vnc`` with ``--password`` and
+   then set the password at runtime using the D-Bus ``SetPassword``
+   method.  Clients will be rejected until a password is set.
+
+2. **systemd credentials** — if the ``CREDENTIALS_DIRECTORY``
+   environment variable is set (see :manpage:`systemd.exec(5)`) and
+   contains a file named ``vnc-password``, the VNC server will use
+   that file's contents as the password automatically.  The
+   ``--password`` flag is not needed in this case.
+
+D-Bus interface
+---------------
+
+``qemu-vnc`` exposes a D-Bus interface for management and monitoring of
+VNC connections.  See :doc:`/interop/dbus-vnc` for the full interface
+reference.
+
+See also
+--------
+
+:manpage:`qemu(1)`,
+`The RFB Protocol <https://github.com/rfbproto/rfbproto>`_
diff --git a/meson.build b/meson.build
index b2154bb9287..e0f2d5d0b87 100644
--- a/meson.build
+++ b/meson.build
@@ -2329,6 +2329,17 @@ dbus_display = get_option('dbus_display') \
            error_message: gdbus_codegen_error.format('-display dbus')) \
   .allowed()
 
+have_qemu_vnc = get_option('qemu_vnc') \
+  .require(have_tools,
+           error_message: 'qemu-vnc requires tools support') \
+  .require(dbus_display,
+           error_message: 'qemu-vnc requires dbus-display support') \
+  .require(vnc.found(),
+           error_message: 'qemu-vnc requires vnc support') \
+  .require(host_os != 'windows',
+           error_message: 'qemu-vnc is not currently supported on Windows') \
+  .allowed()
+
 have_virtfs = get_option('virtfs') \
     .require(host_os == 'linux' or host_os == 'darwin' or host_os == 'freebsd',
              error_message: 'virtio-9p (virtfs) requires Linux or macOS or FreeBSD') \
@@ -3583,6 +3594,7 @@ trace_events_subdirs = [
   'monitor',
   'util',
   'gdbstub',
+  'contrib/qemu-vnc',
 ]
 if have_linux_user
   trace_events_subdirs += [ 'linux-user' ]
@@ -4550,6 +4562,10 @@ if have_tools
     subdir('contrib/ivshmem-client')
     subdir('contrib/ivshmem-server')
   endif
+
+  if have_qemu_vnc
+    subdir('contrib/qemu-vnc')
+  endif
 endif
 
 if stap.found()
@@ -4885,6 +4901,7 @@ if vnc.found()
   summary_info += {'VNC SASL support':  sasl}
   summary_info += {'VNC JPEG support':  jpeg}
 endif
+summary_info += {'VNC D-Bus server (qemu-vnc)': have_qemu_vnc}
 summary_info += {'spice protocol support': spice_protocol}
 if spice_protocol.found()
   summary_info += {'  spice server support': spice}
diff --git a/contrib/qemu-vnc/qemu-vnc.h b/contrib/qemu-vnc/qemu-vnc.h
new file mode 100644
index 00000000000..420d5f66d42
--- /dev/null
+++ b/contrib/qemu-vnc/qemu-vnc.h
@@ -0,0 +1,46 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef CONTRIB_QEMU_VNC_H
+#define CONTRIB_QEMU_VNC_H
+
+#include "qemu/osdep.h"
+
+#include <gio/gunixfdlist.h>
+#include "qemu/dbus.h"
+#include "ui/console.h"
+#include "ui/dbus-display1.h"
+
+#define TEXT_COLS 80
+#define TEXT_ROWS 24
+#define TEXT_FONT_WIDTH  8
+#define TEXT_FONT_HEIGHT 16
+
+
+QemuTextConsole *qemu_vnc_text_console_new(const char *name,
+                                           int fd, bool echo);
+
+void input_setup(QemuDBusDisplay1Keyboard *kbd,
+                 QemuDBusDisplay1Mouse *mouse);
+bool console_setup(GDBusConnection *bus, const char *bus_name,
+                   const char *console_path);
+QemuDBusDisplay1Keyboard *console_get_keyboard(QemuConsole *con);
+QemuDBusDisplay1Mouse *console_get_mouse(QemuConsole *con);
+
+void audio_setup(GDBusObjectManager *manager);
+void clipboard_setup(GDBusObjectManager *manager, GDBusConnection *bus);
+void chardev_setup(const char * const *chardev_names,
+                   GDBusObjectManager *manager);
+
+GThread *p2p_dbus_thread_new(int fd);
+
+void vnc_dbus_setup(GDBusConnection *bus);
+void vnc_dbus_cleanup(void);
+void vnc_dbus_client_connected(const char *host, const char *service,
+                               const char *family, bool websocket);
+void vnc_dbus_client_initialized(const char *host, const char *service,
+                                 const char *x509_dname,
+                                 const char *sasl_username);
+void vnc_dbus_client_disconnected(const char *host, const char *service);
+
+#endif /* CONTRIB_QEMU_VNC_H */
diff --git a/contrib/qemu-vnc/trace.h b/contrib/qemu-vnc/trace.h
new file mode 100644
index 00000000000..8c0bfa963ed
--- /dev/null
+++ b/contrib/qemu-vnc/trace.h
@@ -0,0 +1,4 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "trace/trace-contrib_qemu_vnc.h"
diff --git a/contrib/qemu-vnc/audio.c b/contrib/qemu-vnc/audio.c
new file mode 100644
index 00000000000..b55b04bc92a
--- /dev/null
+++ b/contrib/qemu-vnc/audio.c
@@ -0,0 +1,307 @@
+/*
+ * Standalone VNC server connecting to QEMU via D-Bus display interface.
+ * Audio support. Only one audio stream is tracked. Mixing/resampling could be added.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/audio.h"
+#include "qemu/audio-capture.h"
+#include "qemu/sockets.h"
+#include "qemu/error-report.h"
+#include "ui/dbus-display1.h"
+#include "trace.h"
+#include "qemu-vnc.h"
+
+struct CaptureVoiceOut {
+    struct audsettings as;
+    struct audio_capture_ops ops;
+    void *opaque;
+    QLIST_ENTRY(CaptureVoiceOut) entries;
+};
+
+typedef struct AudioOut {
+    guint64 id;
+    struct audsettings as;
+} AudioOut;
+
+static QLIST_HEAD(, CaptureVoiceOut) capture_list =
+    QLIST_HEAD_INITIALIZER(capture_list);
+static GDBusConnection *audio_listener_conn;
+static AudioOut audio_out;
+
+static bool audsettings_eq(const struct audsettings *a,
+                           const struct audsettings *b)
+{
+    return a->freq == b->freq &&
+           a->nchannels == b->nchannels &&
+           a->fmt == b->fmt &&
+           a->big_endian == b->big_endian;
+}
+
+static gboolean
+on_audio_out_init(QemuDBusDisplay1AudioOutListener *listener,
+                  GDBusMethodInvocation *invocation,
+                  guint64 id, guchar bits, gboolean is_signed,
+                  gboolean is_float, guint freq, guchar nchannels,
+                  guint bytes_per_frame, guint bytes_per_second,
+                  gboolean be, gpointer user_data)
+{
+    AudioFormat fmt;
+
+    switch (bits) {
+    case 8:
+        fmt = is_signed ? AUDIO_FORMAT_S8 : AUDIO_FORMAT_U8;
+        break;
+    case 16:
+        fmt = is_signed ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U16;
+        break;
+    case 32:
+        fmt = is_float ? AUDIO_FORMAT_F32 :
+              is_signed ? AUDIO_FORMAT_S32 : AUDIO_FORMAT_U32;
+        break;
+    default:
+        g_return_val_if_reached(DBUS_METHOD_INVOCATION_HANDLED);
+    }
+
+    struct audsettings as = {
+        .freq = freq,
+        .nchannels = nchannels,
+        .fmt = fmt,
+        .big_endian = be,
+    };
+    audio_out = (AudioOut) {
+        .id = id,
+        .as = as,
+    };
+
+    trace_qemu_vnc_audio_out_init(id, freq, nchannels, bits);
+
+    qemu_dbus_display1_audio_out_listener_complete_init(
+        listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_audio_out_fini(QemuDBusDisplay1AudioOutListener *listener,
+                  GDBusMethodInvocation *invocation,
+                  guint64 id, gpointer user_data)
+{
+    trace_qemu_vnc_audio_out_fini(id);
+
+    qemu_dbus_display1_audio_out_listener_complete_fini(
+        listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_audio_out_set_enabled(QemuDBusDisplay1AudioOutListener *listener,
+                         GDBusMethodInvocation *invocation,
+                         guint64 id, gboolean enabled,
+                         gpointer user_data)
+{
+    CaptureVoiceOut *cap;
+
+    trace_qemu_vnc_audio_out_set_enabled(id, enabled);
+
+    if (id == audio_out.id) {
+        QLIST_FOREACH(cap, &capture_list, entries) {
+            cap->ops.notify(cap->opaque,
+                            enabled ? AUD_CNOTIFY_ENABLE
+                                : AUD_CNOTIFY_DISABLE);
+        }
+    }
+
+    qemu_dbus_display1_audio_out_listener_complete_set_enabled(
+        listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_audio_out_set_volume(QemuDBusDisplay1AudioOutListener *listener,
+                        GDBusMethodInvocation *invocation,
+                        guint64 id, gboolean mute,
+                        GVariant *volume, gpointer user_data)
+{
+    qemu_dbus_display1_audio_out_listener_complete_set_volume(
+        listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_audio_out_write(QemuDBusDisplay1AudioOutListener *listener,
+                   GDBusMethodInvocation *invocation,
+                   guint64 id, GVariant *data,
+                   gpointer user_data)
+{
+    CaptureVoiceOut *cap;
+    gsize size;
+    const void *buf;
+
+    if (id == audio_out.id) {
+        buf = g_variant_get_fixed_array(data, &size, 1);
+
+        trace_qemu_vnc_audio_out_write(id, size);
+
+        QLIST_FOREACH(cap, &capture_list, entries) {
+            /* we don't handle audio resampling/format conversion */
+            if (audsettings_eq(&cap->as, &audio_out.as)) {
+                cap->ops.capture(cap->opaque, buf, size);
+            }
+        }
+    }
+
+    qemu_dbus_display1_audio_out_listener_complete_write(
+        listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+CaptureVoiceOut *audio_be_add_capture(
+    AudioBackend *be,
+    const struct audsettings *as,
+    const struct audio_capture_ops *ops,
+    void *opaque)
+{
+    CaptureVoiceOut *cap;
+
+    if (!audio_listener_conn) {
+        return NULL;
+    }
+
+    cap = g_new0(CaptureVoiceOut, 1);
+    cap->ops = *ops;
+    cap->opaque = opaque;
+    cap->as = *as;
+    QLIST_INSERT_HEAD(&capture_list, cap, entries);
+
+    return cap;
+}
+
+void audio_be_del_capture(
+    AudioBackend *be,
+    CaptureVoiceOut *cap,
+    void *cb_opaque)
+{
+    if (!cap) {
+        return;
+    }
+
+    cap->ops.destroy(cap->opaque);
+    QLIST_REMOVE(cap, entries);
+    g_free(cap);
+}
+
+/*
+ * Dummy audio backend — the VNC server only needs a non-NULL pointer
+ * so that audio capture registration doesn't bail out.  The pointer
+ * is never dereferenced by our code (audio_be_add_capture ignores it).
+ */
+static AudioBackend dummy_audio_be;
+
+AudioBackend *audio_get_default_audio_be(Error **errp)
+{
+    return &dummy_audio_be;
+}
+
+AudioBackend *audio_be_by_name(const char *name, Error **errp)
+{
+    return NULL;
+}
+
+static void
+on_register_audio_listener_finished(GObject *source_object,
+                                    GAsyncResult *res,
+                                    gpointer user_data)
+{
+    GThread *thread = user_data;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GDBusObjectSkeleton) obj = NULL;
+    GDBusObjectManagerServer *server;
+    QemuDBusDisplay1AudioOutListener *audio_skel;
+
+    qemu_dbus_display1_audio_call_register_out_listener_finish(
+        QEMU_DBUS_DISPLAY1_AUDIO(source_object),
+        NULL, res, &err);
+
+    if (err) {
+        error_report("RegisterOutListener failed: %s", err->message);
+        g_thread_join(thread);
+        return;
+    }
+
+    audio_listener_conn = g_thread_join(thread);
+    if (!audio_listener_conn) {
+        return;
+    }
+
+    server = g_dbus_object_manager_server_new(DBUS_DISPLAY1_ROOT);
+    obj = g_dbus_object_skeleton_new(
+        DBUS_DISPLAY1_ROOT "/AudioOutListener");
+
+    audio_skel = qemu_dbus_display1_audio_out_listener_skeleton_new();
+    g_object_connect(audio_skel,
+                     "signal::handle-init",
+                     on_audio_out_init, NULL,
+                     "signal::handle-fini",
+                     on_audio_out_fini, NULL,
+                     "signal::handle-set-enabled",
+                     on_audio_out_set_enabled, NULL,
+                     "signal::handle-set-volume",
+                     on_audio_out_set_volume, NULL,
+                     "signal::handle-write",
+                     on_audio_out_write, NULL,
+                     NULL);
+    g_dbus_object_skeleton_add_interface(
+        obj, G_DBUS_INTERFACE_SKELETON(audio_skel));
+
+    g_dbus_object_manager_server_export(server, obj);
+    g_dbus_object_manager_server_set_connection(
+        server, audio_listener_conn);
+
+    g_dbus_connection_start_message_processing(audio_listener_conn);
+}
+
+void audio_setup(GDBusObjectManager *manager)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GUnixFDList) fd_list = NULL;
+    g_autoptr(GDBusInterface) iface = NULL;
+    GThread *thread;
+    int pair[2];
+    int idx;
+
+    iface = g_dbus_object_manager_get_interface(
+        manager, DBUS_DISPLAY1_ROOT "/Audio",
+        "org.qemu.Display1.Audio");
+    if (!iface) {
+        return;
+    }
+
+    if (qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
+        error_report("audio socketpair failed: %s", strerror(errno));
+        return;
+    }
+
+    fd_list = g_unix_fd_list_new();
+    idx = g_unix_fd_list_append(fd_list, pair[1], &err);
+    close(pair[1]);
+    if (idx < 0) {
+        close(pair[0]);
+        error_report("Failed to append fd: %s", err->message);
+        return;
+    }
+
+    thread = p2p_dbus_thread_new(pair[0]);
+
+    qemu_dbus_display1_audio_call_register_out_listener(
+        QEMU_DBUS_DISPLAY1_AUDIO(iface),
+        g_variant_new_handle(idx),
+        G_DBUS_CALL_FLAGS_NONE, -1,
+        fd_list, NULL,
+        on_register_audio_listener_finished,
+        thread);
+}
diff --git a/contrib/qemu-vnc/chardev.c b/contrib/qemu-vnc/chardev.c
new file mode 100644
index 00000000000..d9d51973724
--- /dev/null
+++ b/contrib/qemu-vnc/chardev.c
@@ -0,0 +1,127 @@
+/*
+ * Standalone VNC server connecting to QEMU via D-Bus display interface.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/sockets.h"
+#include "qemu/error-report.h"
+#include "ui/dbus-display1.h"
+#include "trace.h"
+#include "qemu-vnc.h"
+
+typedef struct ChardevRegisterData {
+    QemuDBusDisplay1Chardev *proxy;
+    int local_fd;
+    char *name;
+    bool echo;
+} ChardevRegisterData;
+
+static void
+on_chardev_register_finished(GObject *source_object,
+                             GAsyncResult *res,
+                             gpointer user_data)
+{
+    ChardevRegisterData *data = user_data;
+    g_autoptr(GError) err = NULL;
+    QemuTextConsole *tc;
+
+    if (!qemu_dbus_display1_chardev_call_register_finish(
+            data->proxy, NULL, res, &err)) {
+        error_report("Chardev Register failed for %s: %s",
+                     data->name, err->message);
+        close(data->local_fd);
+        goto out;
+    }
+
+    tc = qemu_vnc_text_console_new(data->name, data->local_fd, data->echo);
+    if (!tc) {
+        close(data->local_fd);
+        goto out;
+    }
+
+    trace_qemu_vnc_chardev_connected(data->name);
+
+out:
+    g_object_unref(data->proxy);
+    g_free(data->name);
+    g_free(data);
+}
+
+/* Default chardevs to expose as VNC text consoles */
+static const char * const default_names[] = {
+    "org.qemu.console.serial.0",
+    "org.qemu.monitor.hmp.0",
+    NULL,
+};
+
+/* Active chardev names list (points to CLI args or default_names) */
+static const char * const *names;
+
+static void
+chardev_register(QemuDBusDisplay1Chardev *proxy)
+{
+    g_autoptr(GUnixFDList) fd_list = NULL;
+    ChardevRegisterData *data;
+    const char *name;
+    int pair[2];
+    int idx;
+
+    name = qemu_dbus_display1_chardev_get_name(proxy);
+    if (!name || !g_strv_contains(names, name)) {
+        return;
+    }
+
+    if (qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
+        error_report("chardev socketpair failed: %s", strerror(errno));
+        return;
+    }
+
+    fd_list = g_unix_fd_list_new();
+    idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
+    close(pair[1]);
+
+    data = g_new0(ChardevRegisterData, 1);
+    data->proxy = g_object_ref(proxy);
+    data->local_fd = pair[0];
+    data->name = g_strdup(name);
+    data->echo = qemu_dbus_display1_chardev_get_echo(proxy);
+
+    qemu_dbus_display1_chardev_call_register(
+        proxy, g_variant_new_handle(idx),
+        G_DBUS_CALL_FLAGS_NONE, -1,
+        fd_list, NULL,
+        on_chardev_register_finished, data);
+}
+
+void chardev_setup(const char * const *chardev_names,
+                   GDBusObjectManager *manager)
+{
+    GList *objects, *l;
+
+    names = chardev_names ? chardev_names : default_names;
+
+    objects = g_dbus_object_manager_get_objects(manager);
+    for (l = objects; l; l = l->next) {
+        GDBusObject *obj = l->data;
+        const char *path = g_dbus_object_get_object_path(obj);
+        g_autoptr(GDBusInterface) iface = NULL;
+
+        if (!g_str_has_prefix(path, DBUS_DISPLAY1_ROOT "/Chardev_")) {
+            continue;
+        }
+
+        iface = g_dbus_object_get_interface(
+            obj, "org.qemu.Display1.Chardev");
+        if (!iface) {
+            continue;
+        }
+
+        chardev_register(QEMU_DBUS_DISPLAY1_CHARDEV(iface));
+    }
+    g_list_free_full(objects, g_object_unref);
+}
diff --git a/contrib/qemu-vnc/clipboard.c b/contrib/qemu-vnc/clipboard.c
new file mode 100644
index 00000000000..d1673b97899
--- /dev/null
+++ b/contrib/qemu-vnc/clipboard.c
@@ -0,0 +1,378 @@
+/*
+ * Standalone VNC server connecting to QEMU via D-Bus display interface.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/error-report.h"
+#include "ui/clipboard.h"
+#include "ui/dbus-display1.h"
+#include "trace.h"
+#include "qemu-vnc.h"
+
+#define MIME_TEXT_PLAIN_UTF8 "text/plain;charset=utf-8"
+
+typedef struct {
+    GDBusMethodInvocation *invocation;
+    QemuClipboardType type;
+    guint timeout_id;
+} VncDBusClipboardRequest;
+
+static QemuDBusDisplay1Clipboard *clipboard_proxy;
+static QemuDBusDisplay1Clipboard *clipboard_skel;
+static QemuClipboardPeer clipboard_peer;
+static uint32_t clipboard_serial;
+static VncDBusClipboardRequest
+    clipboard_request[QEMU_CLIPBOARD_SELECTION__COUNT];
+
+static void
+vnc_dbus_clipboard_complete_request(
+    GDBusMethodInvocation *invocation,
+    QemuClipboardInfo *info,
+    QemuClipboardType type)
+{
+    GVariant *v_data = g_variant_new_from_data(
+        G_VARIANT_TYPE("ay"),
+        info->types[type].data,
+        info->types[type].size,
+        TRUE,
+        (GDestroyNotify)qemu_clipboard_info_unref,
+        qemu_clipboard_info_ref(info));
+
+    qemu_dbus_display1_clipboard_complete_request(
+        clipboard_skel, invocation,
+        MIME_TEXT_PLAIN_UTF8, v_data);
+}
+
+static void
+vnc_dbus_clipboard_request_cancelled(VncDBusClipboardRequest *req)
+{
+    if (!req->invocation) {
+        return;
+    }
+
+    g_dbus_method_invocation_return_error(
+        req->invocation,
+        G_DBUS_ERROR,
+        G_DBUS_ERROR_FAILED,
+        "Cancelled clipboard request");
+
+    g_clear_object(&req->invocation);
+    g_source_remove(req->timeout_id);
+    req->timeout_id = 0;
+}
+
+static gboolean
+vnc_dbus_clipboard_request_timeout(gpointer user_data)
+{
+    vnc_dbus_clipboard_request_cancelled(user_data);
+    return G_SOURCE_REMOVE;
+}
+
+static void
+vnc_dbus_clipboard_request(QemuClipboardInfo *info,
+                           QemuClipboardType type)
+{
+    g_autofree char *mime = NULL;
+    g_autoptr(GVariant) v_data = NULL;
+    g_autoptr(GError) err = NULL;
+    const char *data = NULL;
+    const char *mimes[] = { MIME_TEXT_PLAIN_UTF8, NULL };
+    size_t n;
+
+    if (type != QEMU_CLIPBOARD_TYPE_TEXT) {
+        return;
+    }
+
+    if (!clipboard_proxy) {
+        return;
+    }
+
+    if (!qemu_dbus_display1_clipboard_call_request_sync(
+            clipboard_proxy,
+            info->selection,
+            mimes,
+            G_DBUS_CALL_FLAGS_NONE, -1, &mime, &v_data, NULL, &err)) {
+        error_report("Failed to request clipboard: %s", err->message);
+        return;
+    }
+
+    if (!g_str_equal(mime, MIME_TEXT_PLAIN_UTF8)) {
+        error_report("Unsupported returned MIME: %s", mime);
+        return;
+    }
+
+    data = g_variant_get_fixed_array(v_data, &n, 1);
+    qemu_clipboard_set_data(&clipboard_peer, info, type,
+                            n, data, true);
+}
+
+static void
+vnc_dbus_clipboard_update_info(QemuClipboardInfo *info)
+{
+    bool self_update = info->owner == &clipboard_peer;
+    const char *mime[QEMU_CLIPBOARD_TYPE__COUNT + 1] = { 0, };
+    VncDBusClipboardRequest *req;
+    int i = 0;
+
+    if (info->owner == NULL) {
+        if (clipboard_proxy) {
+            qemu_dbus_display1_clipboard_call_release(
+                clipboard_proxy,
+                info->selection,
+                G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        }
+        return;
+    }
+
+    if (self_update) {
+        return;
+    }
+
+    req = &clipboard_request[info->selection];
+    if (req->invocation && info->types[req->type].data) {
+        vnc_dbus_clipboard_complete_request(
+            req->invocation, info, req->type);
+        g_clear_object(&req->invocation);
+        g_source_remove(req->timeout_id);
+        req->timeout_id = 0;
+        return;
+    }
+
+    if (info->types[QEMU_CLIPBOARD_TYPE_TEXT].available) {
+        mime[i++] = MIME_TEXT_PLAIN_UTF8;
+    }
+
+    if (i > 0 && clipboard_proxy) {
+        uint32_t serial = info->has_serial ?
+            info->serial : ++clipboard_serial;
+        qemu_dbus_display1_clipboard_call_grab(
+            clipboard_proxy,
+            info->selection,
+            serial,
+            mime,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
+}
+
+static void
+vnc_dbus_clipboard_notify(Notifier *notifier, void *data)
+{
+    QemuClipboardNotify *notify = data;
+
+    switch (notify->type) {
+    case QEMU_CLIPBOARD_UPDATE_INFO:
+        vnc_dbus_clipboard_update_info(notify->info);
+        return;
+    case QEMU_CLIPBOARD_RESET_SERIAL:
+        if (clipboard_proxy) {
+            qemu_dbus_display1_clipboard_call_register(
+                clipboard_proxy,
+                G_DBUS_CALL_FLAGS_NONE,
+                -1, NULL, NULL, NULL);
+        }
+        return;
+    }
+}
+
+static gboolean
+on_clipboard_register(QemuDBusDisplay1Clipboard *clipboard,
+                      GDBusMethodInvocation *invocation,
+                      gpointer user_data)
+{
+    clipboard_serial = 0;
+    qemu_clipboard_reset_serial();
+
+    qemu_dbus_display1_clipboard_complete_register(
+        clipboard, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_clipboard_unregister(QemuDBusDisplay1Clipboard *clipboard,
+                        GDBusMethodInvocation *invocation,
+                        gpointer user_data)
+{
+    int i;
+
+    for (i = 0; i < G_N_ELEMENTS(clipboard_request); ++i) {
+        vnc_dbus_clipboard_request_cancelled(&clipboard_request[i]);
+    }
+
+    qemu_dbus_display1_clipboard_complete_unregister(
+        clipboard, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_clipboard_grab(QemuDBusDisplay1Clipboard *clipboard,
+                  GDBusMethodInvocation *invocation,
+                  gint arg_selection,
+                  guint arg_serial,
+                  const gchar *const *arg_mimes,
+                  gpointer user_data)
+{
+    QemuClipboardSelection s = arg_selection;
+    g_autoptr(QemuClipboardInfo) info = NULL;
+
+    if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) {
+        g_dbus_method_invocation_return_error(
+            invocation,
+            G_DBUS_ERROR,
+            G_DBUS_ERROR_FAILED,
+            "Invalid clipboard selection: %d", arg_selection);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    trace_qemu_vnc_clipboard_grab(arg_selection, arg_serial);
+
+    info = qemu_clipboard_info_new(&clipboard_peer, s);
+    if (g_strv_contains(arg_mimes, MIME_TEXT_PLAIN_UTF8)) {
+        info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
+    }
+    info->serial = arg_serial;
+    info->has_serial = true;
+    if (qemu_clipboard_check_serial(info, true)) {
+        qemu_clipboard_update(info);
+    }
+
+    qemu_dbus_display1_clipboard_complete_grab(
+        clipboard, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_clipboard_release(QemuDBusDisplay1Clipboard *clipboard,
+                     GDBusMethodInvocation *invocation,
+                     gint arg_selection,
+                     gpointer user_data)
+{
+    trace_qemu_vnc_clipboard_release(arg_selection);
+
+    qemu_clipboard_peer_release(&clipboard_peer, arg_selection);
+
+    qemu_dbus_display1_clipboard_complete_release(
+        clipboard, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_clipboard_request(QemuDBusDisplay1Clipboard *clipboard,
+                     GDBusMethodInvocation *invocation,
+                     gint arg_selection,
+                     const gchar *const *arg_mimes,
+                     gpointer user_data)
+{
+    QemuClipboardSelection s = arg_selection;
+    QemuClipboardType type = QEMU_CLIPBOARD_TYPE_TEXT;
+    QemuClipboardInfo *info = NULL;
+
+    trace_qemu_vnc_clipboard_request(arg_selection);
+
+    if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) {
+        g_dbus_method_invocation_return_error(
+            invocation,
+            G_DBUS_ERROR,
+            G_DBUS_ERROR_FAILED,
+            "Invalid clipboard selection: %d", arg_selection);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    if (clipboard_request[s].invocation) {
+        g_dbus_method_invocation_return_error(
+            invocation,
+            G_DBUS_ERROR,
+            G_DBUS_ERROR_FAILED,
+            "Pending request");
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    info = qemu_clipboard_info(s);
+    if (!info || !info->owner || info->owner == &clipboard_peer) {
+        g_dbus_method_invocation_return_error(
+            invocation,
+            G_DBUS_ERROR,
+            G_DBUS_ERROR_FAILED,
+            "Empty clipboard");
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    if (!g_strv_contains(arg_mimes, MIME_TEXT_PLAIN_UTF8) ||
+        !info->types[type].available) {
+        g_dbus_method_invocation_return_error(
+            invocation,
+            G_DBUS_ERROR,
+            G_DBUS_ERROR_FAILED,
+            "Unhandled MIME types requested");
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    if (info->types[type].data) {
+        vnc_dbus_clipboard_complete_request(invocation, info, type);
+    } else {
+        qemu_clipboard_request(info, type);
+
+        clipboard_request[s].invocation = g_object_ref(invocation);
+        clipboard_request[s].type = type;
+        clipboard_request[s].timeout_id =
+            g_timeout_add_seconds(5,
+                                  vnc_dbus_clipboard_request_timeout,
+                                  &clipboard_request[s]);
+    }
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+void clipboard_setup(GDBusObjectManager *manager, GDBusConnection *bus)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GDBusInterface) iface = NULL;
+
+    iface = g_dbus_object_manager_get_interface(
+        manager, DBUS_DISPLAY1_ROOT "/Clipboard",
+        "org.qemu.Display1.Clipboard");
+    if (!iface) {
+        return;
+    }
+
+    clipboard_proxy = g_object_ref(QEMU_DBUS_DISPLAY1_CLIPBOARD(iface));
+
+    clipboard_skel = qemu_dbus_display1_clipboard_skeleton_new();
+    g_object_connect(clipboard_skel,
+                     "signal::handle-register",
+                     on_clipboard_register, NULL,
+                     "signal::handle-unregister",
+                     on_clipboard_unregister, NULL,
+                     "signal::handle-grab",
+                     on_clipboard_grab, NULL,
+                     "signal::handle-release",
+                     on_clipboard_release, NULL,
+                     "signal::handle-request",
+                     on_clipboard_request, NULL,
+                     NULL);
+
+    if (!g_dbus_interface_skeleton_export(
+            G_DBUS_INTERFACE_SKELETON(clipboard_skel),
+            bus,
+            DBUS_DISPLAY1_ROOT "/Clipboard",
+            &err)) {
+        error_report("Failed to export clipboard: %s", err->message);
+        g_clear_object(&clipboard_skel);
+        g_clear_object(&clipboard_proxy);
+        return;
+    }
+
+    clipboard_peer.name = "dbus";
+    clipboard_peer.notifier.notify = vnc_dbus_clipboard_notify;
+    clipboard_peer.request = vnc_dbus_clipboard_request;
+    qemu_clipboard_peer_register(&clipboard_peer);
+
+    qemu_dbus_display1_clipboard_call_register(
+        clipboard_proxy,
+        G_DBUS_CALL_FLAGS_NONE,
+        -1, NULL, NULL, NULL);
+}
diff --git a/contrib/qemu-vnc/console.c b/contrib/qemu-vnc/console.c
new file mode 100644
index 00000000000..076365adf77
--- /dev/null
+++ b/contrib/qemu-vnc/console.c
@@ -0,0 +1,168 @@
+/*
+ * Minimal QemuConsole helpers for the standalone qemu-vnc binary.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "ui/console.h"
+#include "ui/console-priv.h"
+#include "ui/vt100.h"
+#include "qemu-vnc.h"
+#include "trace.h"
+
+/*
+ * Our own QemuTextConsole definition — the one in console-vc.c uses
+ * a Chardev* backend which is not available in the standalone binary.
+ * Here we drive the VT100 emulator directly over a raw file descriptor.
+ */
+typedef struct QemuTextConsole {
+    QemuConsole parent;
+    QemuVT100 vt;
+    int chardev_fd;
+    guint io_watch_id;
+    char *name;
+} QemuTextConsole;
+
+typedef QemuConsoleClass QemuTextConsoleClass;
+
+OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console,
+                   QEMU_TEXT_CONSOLE, QEMU_CONSOLE)
+
+static void qemu_text_console_class_init(ObjectClass *oc, const void *data)
+{
+}
+
+static void text_console_invalidate(void *opaque)
+{
+    QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
+
+    vt100_set_image(&s->vt, QEMU_CONSOLE(s)->surface->image);
+    vt100_refresh(&s->vt);
+}
+
+static const GraphicHwOps text_console_ops = {
+    .invalidate  = text_console_invalidate,
+};
+
+static void qemu_text_console_init(Object *obj)
+{
+    QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
+
+    QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
+    QEMU_CONSOLE(c)->hw = c;
+}
+
+static void qemu_text_console_finalize(Object *obj)
+{
+    QemuTextConsole *tc = QEMU_TEXT_CONSOLE(obj);
+
+    vt100_fini(&tc->vt);
+    if (tc->io_watch_id) {
+        g_source_remove(tc->io_watch_id);
+    }
+    if (tc->chardev_fd >= 0) {
+        close(tc->chardev_fd);
+    }
+    g_free(tc->name);
+}
+
+
+static void text_console_out_flush(QemuVT100 *vt)
+{
+    QemuTextConsole *tc = container_of(vt, QemuTextConsole, vt);
+    const uint8_t *data;
+    uint32_t len;
+
+    while (!fifo8_is_empty(&vt->out_fifo)) {
+        ssize_t ret;
+
+        data = fifo8_pop_bufptr(&vt->out_fifo,
+                                fifo8_num_used(&vt->out_fifo), &len);
+        ret = write(tc->chardev_fd, data, len);
+        if (ret < 0) {
+            trace_qemu_vnc_console_io_error(tc->name);
+            break;
+        }
+    }
+}
+
+static void text_console_image_update(QemuVT100 *vt, int x, int y, int w, int h)
+{
+    QemuTextConsole *tc = container_of(vt, QemuTextConsole, vt);
+    QemuConsole *con = QEMU_CONSOLE(tc);
+
+    qemu_console_update(con, x, y, w, h);
+}
+
+static gboolean text_console_io_cb(GIOChannel *source,
+    GIOCondition cond, gpointer data)
+{
+    QemuTextConsole *tc = data;
+    uint8_t buf[4096];
+    ssize_t n;
+
+    if (cond & (G_IO_HUP | G_IO_ERR)) {
+        tc->io_watch_id = 0;
+        return G_SOURCE_REMOVE;
+    }
+
+    n = read(tc->chardev_fd, buf, sizeof(buf));
+    if (n <= 0) {
+        trace_qemu_vnc_console_io_error(tc->name);
+        tc->io_watch_id = 0;
+        return G_SOURCE_REMOVE;
+    }
+
+    vt100_input(&tc->vt, buf, n);
+    return G_SOURCE_CONTINUE;
+}
+
+QemuTextConsole *qemu_vnc_text_console_new(const char *name,
+                                           int fd, bool echo)
+{
+    int w = TEXT_COLS * TEXT_FONT_WIDTH;
+    int h = TEXT_ROWS * TEXT_FONT_HEIGHT;
+    QemuTextConsole *tc;
+    QemuConsole *con;
+    pixman_image_t *image;
+    GIOChannel *chan;
+
+    tc = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE));
+    con = QEMU_CONSOLE(tc);
+
+    tc->name = g_strdup(name);
+    tc->chardev_fd = fd;
+
+    image = pixman_image_create_bits(PIXMAN_x8r8g8b8, w, h, NULL, 0);
+    con->surface = qemu_create_displaysurface_pixman(image);
+    con->scanout.kind = SCANOUT_SURFACE;
+    qemu_pixman_image_unref(image);
+
+    vt100_init(&tc->vt, con->surface->image,
+               text_console_image_update, text_console_out_flush);
+    tc->vt.echo = echo;
+    vt100_refresh(&tc->vt);
+
+    chan = g_io_channel_unix_new(fd);
+    g_io_channel_set_encoding(chan, NULL, NULL);
+    tc->io_watch_id = g_io_add_watch(chan,
+                                      G_IO_IN | G_IO_HUP | G_IO_ERR,
+                                      text_console_io_cb, tc);
+    g_io_channel_unref(chan);
+
+    return tc;
+}
+
+void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
+{
+    vt100_keysym(&s->vt, keysym);
+}
+
+void qemu_text_console_update_size(QemuTextConsole *c)
+{
+    qemu_console_text_resize(QEMU_CONSOLE(c), c->vt.width, c->vt.height);
+}
diff --git a/contrib/qemu-vnc/dbus.c b/contrib/qemu-vnc/dbus.c
new file mode 100644
index 00000000000..0e5f52623ea
--- /dev/null
+++ b/contrib/qemu-vnc/dbus.c
@@ -0,0 +1,439 @@
+/*
+ * D-Bus interface for qemu-vnc standalone VNC server.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/cutils.h"
+#include "qapi-types-trace.h"
+#include "system/system.h"
+#include "qapi/qapi-types-ui.h"
+#include "qapi/qapi-commands-ui.h"
+#include "qemu-vnc.h"
+#include "qemu-vnc1.h"
+#include "qapi/qapi-emit-events.h"
+#include "qobject/qdict.h"
+#include "ui/vnc.h"
+#include "trace.h"
+
+typedef struct VncDbusClient {
+    QemuVnc1ClientSkeleton *skeleton;
+    char *path;
+    char *host;
+    char *service;
+    unsigned int id;
+    QTAILQ_ENTRY(VncDbusClient) next;
+} VncDbusClient;
+
+static QemuVnc1ServerSkeleton *server_skeleton;
+static GDBusObjectManagerServer *obj_manager;
+static unsigned int next_client_id;
+
+static QTAILQ_HEAD(, VncDbusClient)
+    dbus_clients = QTAILQ_HEAD_INITIALIZER(dbus_clients);
+
+static VncDbusClient *vnc_dbus_find_client(const char *host,
+                                           const char *service)
+{
+    VncDbusClient *c;
+
+    QTAILQ_FOREACH(c, &dbus_clients, next) {
+        if (g_str_equal(c->host, host) &&
+            g_str_equal(c->service, service)) {
+            return c;
+        }
+    }
+    return NULL;
+}
+
+static void vnc_dbus_update_clients_property(void)
+{
+    VncDbusClient *c;
+    GPtrArray *paths;
+    const char **strv;
+
+    paths = g_ptr_array_new();
+    QTAILQ_FOREACH(c, &dbus_clients, next) {
+        g_ptr_array_add(paths, c->path);
+    }
+    g_ptr_array_add(paths, NULL);
+
+    strv = (const char **)paths->pdata;
+    qemu_vnc1_server_set_clients(QEMU_VNC1_SERVER(server_skeleton), strv);
+    g_ptr_array_free(paths, TRUE);
+}
+
+void vnc_dbus_client_connected(const char *host, const char *service,
+                               const char *family, bool websocket)
+{
+    VncDbusClient *c;
+    g_autoptr(GDBusObjectSkeleton) obj = NULL;
+
+    if (!server_skeleton) {
+        return;
+    }
+
+    c = g_new0(VncDbusClient, 1);
+    c->id = next_client_id++;
+    c->host = g_strdup(host);
+    c->service = g_strdup(service);
+    c->path = g_strdup_printf("/org/qemu/Vnc1/Client_%u", c->id);
+
+    c->skeleton = QEMU_VNC1_CLIENT_SKELETON(qemu_vnc1_client_skeleton_new());
+    qemu_vnc1_client_set_host(QEMU_VNC1_CLIENT(c->skeleton), host);
+    qemu_vnc1_client_set_service(QEMU_VNC1_CLIENT(c->skeleton), service);
+    qemu_vnc1_client_set_family(QEMU_VNC1_CLIENT(c->skeleton), family);
+    qemu_vnc1_client_set_web_socket(QEMU_VNC1_CLIENT(c->skeleton), websocket);
+    qemu_vnc1_client_set_x509_dname(QEMU_VNC1_CLIENT(c->skeleton), "");
+    qemu_vnc1_client_set_sasl_username(QEMU_VNC1_CLIENT(c->skeleton), "");
+
+    obj = g_dbus_object_skeleton_new(c->path);
+    g_dbus_object_skeleton_add_interface(
+        obj, G_DBUS_INTERFACE_SKELETON(c->skeleton));
+    g_dbus_object_manager_server_export(obj_manager, obj);
+
+    QTAILQ_INSERT_TAIL(&dbus_clients, c, next);
+    vnc_dbus_update_clients_property();
+
+    qemu_vnc1_server_emit_client_connected(
+        QEMU_VNC1_SERVER(server_skeleton), c->path);
+}
+
+void vnc_dbus_client_initialized(const char *host, const char *service,
+                                 const char *x509_dname,
+                                 const char *sasl_username)
+{
+    VncDbusClient *c;
+
+    if (!server_skeleton) {
+        return;
+    }
+
+    c = vnc_dbus_find_client(host, service);
+    if (!c) {
+        trace_qemu_vnc_client_not_found(host, service);
+        return;
+    }
+
+    if (x509_dname) {
+        qemu_vnc1_client_set_x509_dname(
+            QEMU_VNC1_CLIENT(c->skeleton), x509_dname);
+    }
+    if (sasl_username) {
+        qemu_vnc1_client_set_sasl_username(
+            QEMU_VNC1_CLIENT(c->skeleton), sasl_username);
+    }
+
+    qemu_vnc1_server_emit_client_initialized(
+        QEMU_VNC1_SERVER(server_skeleton), c->path);
+}
+
+void vnc_dbus_client_disconnected(const char *host, const char *service)
+{
+    VncDbusClient *c;
+
+    if (!server_skeleton) {
+        return;
+    }
+
+    c = vnc_dbus_find_client(host, service);
+    if (!c) {
+        trace_qemu_vnc_client_not_found(host, service);
+        return;
+    }
+
+    qemu_vnc1_server_emit_client_disconnected(
+        QEMU_VNC1_SERVER(server_skeleton), c->path);
+
+    g_dbus_object_manager_server_unexport(obj_manager, c->path);
+    QTAILQ_REMOVE(&dbus_clients, c, next);
+    vnc_dbus_update_clients_property();
+
+    g_object_unref(c->skeleton);
+    g_free(c->path);
+    g_free(c->host);
+    g_free(c->service);
+    g_free(c);
+}
+
+static gboolean
+on_set_password(QemuVnc1Server *iface,
+                GDBusMethodInvocation *invocation,
+                const gchar *password,
+                gpointer user_data)
+{
+    Error *err = NULL;
+
+    if (vnc_display_password("default", password, &err) < 0) {
+        g_dbus_method_invocation_return_error(
+            invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+            "%s", error_get_pretty(err));
+        error_free(err);
+        return TRUE;
+    }
+
+    qemu_vnc1_server_complete_set_password(iface, invocation);
+    return TRUE;
+}
+
+static gboolean
+on_expire_password(QemuVnc1Server *iface,
+                   GDBusMethodInvocation *invocation,
+                   const gchar *time_str,
+                   gpointer user_data)
+{
+    time_t when;
+
+    if (g_str_equal(time_str, "now")) {
+        when = 0;
+    } else if (g_str_equal(time_str, "never")) {
+        when = TIME_MAX;
+    } else if (time_str[0] == '+') {
+        int seconds;
+        if (qemu_strtoi(time_str + 1, NULL, 10, &seconds) < 0) {
+            g_dbus_method_invocation_return_error(
+                invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                "Invalid time format: %s", time_str);
+            return TRUE;
+        }
+        when = time(NULL) + seconds;
+    } else {
+        int64_t epoch;
+        if (qemu_strtoi64(time_str, NULL, 10, &epoch) < 0) {
+            g_dbus_method_invocation_return_error(
+                invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                "Invalid time format: %s", time_str);
+            return TRUE;
+        }
+        when = epoch;
+    }
+
+    if (vnc_display_pw_expire("default", when) < 0) {
+        g_dbus_method_invocation_return_error(
+            invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+            "Failed to set password expiry");
+        return TRUE;
+    }
+
+    qemu_vnc1_server_complete_expire_password(iface, invocation);
+    return TRUE;
+}
+
+static gboolean
+on_reload_certificates(QemuVnc1Server *iface,
+                       GDBusMethodInvocation *invocation,
+                       gpointer user_data)
+{
+    Error *err = NULL;
+
+    if (!vnc_display_reload_certs("default", &err)) {
+        g_dbus_method_invocation_return_error(
+            invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+            "%s", error_get_pretty(err));
+        error_free(err);
+        return TRUE;
+    }
+
+    qemu_vnc1_server_complete_reload_certificates(iface, invocation);
+    return TRUE;
+}
+
+static void vnc_dbus_add_listeners(VncInfo2 *info)
+{
+    GVariantBuilder builder;
+    VncServerInfo2List *entry;
+
+    g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
+
+    for (entry = info->server; entry; entry = entry->next) {
+        VncServerInfo2 *s = entry->value;
+        const char *vencrypt_str = "";
+
+        if (s->has_vencrypt) {
+            vencrypt_str = VncVencryptSubAuth_str(s->vencrypt);
+        }
+
+        g_variant_builder_open(&builder, G_VARIANT_TYPE("a{sv}"));
+        g_variant_builder_add(&builder, "{sv}", "Host",
+                              g_variant_new_string(s->host));
+        g_variant_builder_add(&builder, "{sv}", "Service",
+                              g_variant_new_string(s->service));
+        g_variant_builder_add(&builder, "{sv}", "Family",
+                              g_variant_new_string(
+                                  NetworkAddressFamily_str(s->family)));
+        g_variant_builder_add(&builder, "{sv}", "WebSocket",
+                              g_variant_new_boolean(s->websocket));
+        g_variant_builder_add(&builder, "{sv}", "Auth",
+                              g_variant_new_string(
+                                  VncPrimaryAuth_str(s->auth)));
+        g_variant_builder_add(&builder, "{sv}", "VencryptSubAuth",
+                              g_variant_new_string(vencrypt_str));
+        g_variant_builder_close(&builder);
+    }
+
+    qemu_vnc1_server_set_listeners(
+        QEMU_VNC1_SERVER(server_skeleton),
+        g_variant_builder_end(&builder));
+}
+
+void vnc_dbus_setup(GDBusConnection *bus)
+{
+    g_autoptr(GDBusObjectSkeleton) server_obj = NULL;
+    VncInfo2List *info_list;
+    Error *err = NULL;
+    const char *auth_str = "none";
+    const char *vencrypt_str = "";
+
+    obj_manager = g_dbus_object_manager_server_new("/org/qemu/Vnc1");
+
+    server_skeleton = QEMU_VNC1_SERVER_SKELETON(
+        qemu_vnc1_server_skeleton_new());
+
+    qemu_vnc1_server_set_name(QEMU_VNC1_SERVER(server_skeleton),
+                              qemu_name ? qemu_name : "");
+    qemu_vnc1_server_set_clients(QEMU_VNC1_SERVER(server_skeleton), NULL);
+
+    /* Query auth info from the VNC display */
+    info_list = qmp_query_vnc_servers(&err);
+    if (info_list) {
+        VncInfo2 *info = info_list->value;
+        auth_str = VncPrimaryAuth_str(info->auth);
+        if (info->has_vencrypt) {
+            vencrypt_str = VncVencryptSubAuth_str(info->vencrypt);
+        }
+        vnc_dbus_add_listeners(info);
+    }
+
+    qemu_vnc1_server_set_auth(QEMU_VNC1_SERVER(server_skeleton), auth_str);
+    qemu_vnc1_server_set_vencrypt_sub_auth(
+        QEMU_VNC1_SERVER(server_skeleton), vencrypt_str);
+
+    qapi_free_VncInfo2List(info_list);
+
+    g_signal_connect(server_skeleton, "handle-set-password",
+                     G_CALLBACK(on_set_password), NULL);
+    g_signal_connect(server_skeleton, "handle-expire-password",
+                     G_CALLBACK(on_expire_password), NULL);
+    g_signal_connect(server_skeleton, "handle-reload-certificates",
+                     G_CALLBACK(on_reload_certificates), NULL);
+
+    server_obj = g_dbus_object_skeleton_new("/org/qemu/Vnc1/Server");
+    g_dbus_object_skeleton_add_interface(
+        server_obj, G_DBUS_INTERFACE_SKELETON(server_skeleton));
+    g_dbus_object_manager_server_export(obj_manager, server_obj);
+
+    g_dbus_object_manager_server_set_connection(obj_manager, bus);
+
+    if (g_dbus_connection_get_flags(bus)
+        & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) {
+        g_bus_own_name_on_connection(
+            bus, "org.qemu.vnc",
+            G_BUS_NAME_OWNER_FLAGS_NONE,
+            NULL, NULL, NULL, NULL);
+    }
+}
+
+void vnc_action_shutdown(VncState *vs)
+{
+    VncDbusClient *c;
+
+    c = vnc_dbus_find_client(vs->info->host, vs->info->service);
+    if (!c) {
+        trace_qemu_vnc_client_not_found(vs->info->host, vs->info->service);
+        return;
+    }
+
+    qemu_vnc1_client_emit_shutdown_request(QEMU_VNC1_CLIENT(c->skeleton));
+}
+
+void vnc_action_reset(VncState *vs)
+{
+    VncDbusClient *c;
+
+    c = vnc_dbus_find_client(vs->info->host, vs->info->service);
+    if (!c) {
+        trace_qemu_vnc_client_not_found(vs->info->host, vs->info->service);
+        return;
+    }
+
+    qemu_vnc1_client_emit_reset_request(QEMU_VNC1_CLIENT(c->skeleton));
+}
+
+/*
+ * Override the stub qapi_event_emit() to capture VNC events
+ * and forward them to the D-Bus interface.
+ */
+void qapi_event_emit(QAPIEvent event, QDict *qdict)
+{
+    QDict *data, *client;
+    const char *host, *service, *family;
+    bool websocket;
+
+    if (event != QAPI_EVENT_VNC_CONNECTED &&
+        event != QAPI_EVENT_VNC_INITIALIZED &&
+        event != QAPI_EVENT_VNC_DISCONNECTED) {
+        return;
+    }
+
+    data = qdict_get_qdict(qdict, "data");
+    if (!data) {
+        return;
+    }
+
+    client = qdict_get_qdict(data, "client");
+    if (!client) {
+        return;
+    }
+
+    host = qdict_get_str(client, "host");
+    service = qdict_get_str(client, "service");
+    family = qdict_get_str(client, "family");
+    websocket = qdict_get_bool(client, "websocket");
+
+    switch (event) {
+    case QAPI_EVENT_VNC_CONNECTED:
+        vnc_dbus_client_connected(host, service, family, websocket);
+        break;
+    case QAPI_EVENT_VNC_INITIALIZED: {
+        const char *x509_dname = NULL;
+        const char *sasl_username = NULL;
+
+        if (qdict_haskey(client, "x509_dname")) {
+            x509_dname = qdict_get_str(client, "x509_dname");
+        }
+        if (qdict_haskey(client, "sasl_username")) {
+            sasl_username = qdict_get_str(client, "sasl_username");
+        }
+        vnc_dbus_client_initialized(host, service,
+                                    x509_dname, sasl_username);
+        break;
+    }
+    case QAPI_EVENT_VNC_DISCONNECTED:
+        vnc_dbus_client_disconnected(host, service);
+        break;
+    default:
+        break;
+    }
+}
+
+void vnc_dbus_cleanup(void)
+{
+    VncDbusClient *c, *next;
+
+    QTAILQ_FOREACH_SAFE(c, &dbus_clients, next, next) {
+        g_dbus_object_manager_server_unexport(obj_manager, c->path);
+        QTAILQ_REMOVE(&dbus_clients, c, next);
+        g_object_unref(c->skeleton);
+        g_free(c->path);
+        g_free(c->host);
+        g_free(c->service);
+        g_free(c);
+    }
+
+    g_clear_object(&server_skeleton);
+    g_clear_object(&obj_manager);
+}
diff --git a/contrib/qemu-vnc/display.c b/contrib/qemu-vnc/display.c
new file mode 100644
index 00000000000..8fe9b6fc898
--- /dev/null
+++ b/contrib/qemu-vnc/display.c
@@ -0,0 +1,456 @@
+/*
+ * D-Bus display listener — scanout, update and cursor handling.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/sockets.h"
+#include "qemu/error-report.h"
+#include "ui/console-priv.h"
+#include "ui/dbus-display1.h"
+#include "ui/surface.h"
+#include "trace.h"
+#include "qemu-vnc.h"
+
+typedef struct ConsoleData {
+    QemuDBusDisplay1Console *console_proxy;
+    QemuDBusDisplay1Keyboard *keyboard_proxy;
+    QemuDBusDisplay1Mouse *mouse_proxy;
+    QemuGraphicConsole *gfx_con;
+    GDBusConnection *listener_conn;
+    /*
+     * When true the surface is backed by a read-only mmap (ScanoutMap path)
+     * and Update messages must be rejected because compositing into the
+     * surface is not possible.  The plain Scanout path provides a writable
+     * copy and clears this flag.
+     */
+    bool read_only;
+} ConsoleData;
+
+static void display_ui_info(void *opaque, uint32_t head, QemuUIInfo *info)
+{
+    ConsoleData *cd = opaque;
+    g_autoptr(GError) err = NULL;
+
+    if (!cd || !cd->console_proxy) {
+        return;
+    }
+
+    qemu_dbus_display1_console_call_set_uiinfo_sync(
+        cd->console_proxy,
+        info->width_mm, info->height_mm,
+        info->xoff, info->yoff,
+        info->width, info->height,
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
+    if (err) {
+        error_report("SetUIInfo failed: %s", err->message);
+    }
+}
+
+static void
+scanout_image_destroy(pixman_image_t *image, void *data)
+{
+    g_variant_unref(data);
+}
+
+typedef struct {
+    void *addr;
+    size_t len;
+} ScanoutMapData;
+
+static void
+scanout_map_destroy(pixman_image_t *image, void *data)
+{
+    ScanoutMapData *map = data;
+    munmap(map->addr, map->len);
+    g_free(map);
+}
+
+static gboolean
+on_scanout(QemuDBusDisplay1Listener *listener,
+           GDBusMethodInvocation *invocation,
+           guint width, guint height, guint stride,
+           guint pixman_format, GVariant *data,
+           gpointer user_data)
+{
+    ConsoleData *cd = user_data;
+    QemuConsole *con = QEMU_CONSOLE(cd->gfx_con);
+    gsize size;
+    const uint8_t *pixels;
+    pixman_image_t *image;
+    DisplaySurface *surface;
+
+    trace_qemu_vnc_scanout(width, height, stride, pixman_format);
+
+    pixels = g_variant_get_fixed_array(data, &size, 1);
+
+    image = pixman_image_create_bits((pixman_format_code_t)pixman_format,
+        width, height, (uint32_t *)pixels, stride);
+    assert(image);
+
+    g_variant_ref(data);
+    pixman_image_set_destroy_function(image, scanout_image_destroy, data);
+
+    cd->read_only = false;
+    surface = qemu_create_displaysurface_pixman(image);
+    qemu_console_set_surface(con, surface);
+
+    qemu_dbus_display1_listener_complete_scanout(listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_update(QemuDBusDisplay1Listener *listener,
+          GDBusMethodInvocation *invocation,
+          gint x, gint y, gint w, gint h,
+          guint stride, guint pixman_format, GVariant *data,
+          gpointer user_data)
+{
+    ConsoleData *cd = user_data;
+    QemuConsole *con = QEMU_CONSOLE(cd->gfx_con);
+    DisplaySurface *surface = qemu_console_surface(con);
+    gsize size;
+    const uint8_t *pixels;
+    pixman_image_t *src;
+
+    trace_qemu_vnc_update(x, y, w, h, stride, pixman_format);
+    if (!surface || cd->read_only) {
+        g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
+            G_DBUS_ERROR_FAILED, "No active or writable console");
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    pixels = g_variant_get_fixed_array(data, &size, 1);
+    src = pixman_image_create_bits((pixman_format_code_t)pixman_format,
+        w, h, (uint32_t *)pixels, stride);
+    assert(src);
+    pixman_image_composite(PIXMAN_OP_SRC, src, NULL,
+            surface->image,
+            0, 0, 0, 0, x, y, w, h);
+    pixman_image_unref(src);
+
+    qemu_console_update(con, x, y, w, h);
+
+    qemu_dbus_display1_listener_complete_update(listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_scanout_map(QemuDBusDisplay1ListenerUnixMap *listener,
+               GDBusMethodInvocation *invocation,
+               GUnixFDList *fd_list,
+               GVariant *arg_handle,
+               guint offset, guint width, guint height,
+               guint stride, guint pixman_format,
+               gpointer user_data)
+{
+    ConsoleData *cd = user_data;
+    gint32 handle = g_variant_get_handle(arg_handle);
+    g_autoptr(GError) err = NULL;
+    DisplaySurface *surface;
+    int fd;
+    void *addr;
+    size_t len = (size_t)height * stride;
+    pixman_image_t *image;
+
+    trace_qemu_vnc_scanout_map(width, height, stride, pixman_format, offset);
+
+    fd = g_unix_fd_list_get(fd_list, handle, &err);
+    if (fd < 0) {
+        g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
+            G_DBUS_ERROR_FAILED, "Failed to get fd: %s", err->message);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    /* MAP_PRIVATE: we only read; avoid propagating writes back to QEMU */
+    addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, offset);
+    close(fd);
+    if (addr == MAP_FAILED) {
+        g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
+            G_DBUS_ERROR_FAILED, "mmap failed: %s", g_strerror(errno));
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    image = pixman_image_create_bits((pixman_format_code_t)pixman_format,
+                                     width, height, addr, stride);
+    assert(image);
+    {
+        ScanoutMapData *map = g_new0(ScanoutMapData, 1);
+        map->addr = addr;
+        map->len = len;
+        pixman_image_set_destroy_function(image, scanout_map_destroy, map);
+    }
+
+    cd->read_only = true;
+    surface = qemu_create_displaysurface_pixman(image);
+    qemu_console_set_surface(QEMU_CONSOLE(cd->gfx_con), surface);
+
+    qemu_dbus_display1_listener_unix_map_complete_scanout_map(
+        listener, invocation, NULL);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_update_map(QemuDBusDisplay1ListenerUnixMap *listener,
+              GDBusMethodInvocation *invocation,
+              guint x, guint y, guint w, guint h,
+              gpointer user_data)
+{
+    ConsoleData *cd = user_data;
+
+    trace_qemu_vnc_update_map(x, y, w, h);
+
+    qemu_console_update(QEMU_CONSOLE(cd->gfx_con), x, y, w, h);
+
+    qemu_dbus_display1_listener_unix_map_complete_update_map(
+        listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+on_cursor_define(QemuDBusDisplay1Listener *listener,
+                 GDBusMethodInvocation *invocation,
+                 gint width, gint height,
+                 gint hot_x, gint hot_y,
+                 GVariant *data,
+                 gpointer user_data)
+{
+    ConsoleData *cd = user_data;
+    gsize size;
+    const uint8_t *pixels;
+    QEMUCursor *c;
+
+    trace_qemu_vnc_cursor_define(width, height, hot_x, hot_y);
+
+    c = cursor_alloc(width, height);
+    if (!c) {
+        qemu_dbus_display1_listener_complete_cursor_define(
+            listener, invocation);
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    c->hot_x = hot_x;
+    c->hot_y = hot_y;
+
+    pixels = g_variant_get_fixed_array(data, &size, 1);
+    memcpy(c->data, pixels, MIN(size, (gsize)width * height * 4));
+
+    qemu_console_set_cursor(QEMU_CONSOLE(cd->gfx_con), c);
+    cursor_unref(c);
+
+    qemu_dbus_display1_listener_complete_cursor_define(
+        listener, invocation);
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+typedef struct {
+    GMainLoop *loop;
+    GThread *thread;
+    GDBusConnection *listener_conn;
+} ListenerSetupData;
+
+static void
+on_register_listener_finished(GObject *source_object,
+                              GAsyncResult *res,
+                              gpointer user_data)
+{
+    ListenerSetupData *data = user_data;
+    g_autoptr(GError) err = NULL;
+
+    qemu_dbus_display1_console_call_register_listener_finish(
+        QEMU_DBUS_DISPLAY1_CONSOLE(source_object),
+        NULL,
+        res, &err);
+
+    if (err) {
+        error_report("RegisterListener failed: %s", err->message);
+        g_main_loop_quit(data->loop);
+        return;
+    }
+
+    data->listener_conn = g_thread_join(data->thread);
+    g_main_loop_quit(data->loop);
+}
+
+static GDBusConnection *
+console_register_display_listener(QemuDBusDisplay1Console *console)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GMainLoop) loop = NULL;
+    g_autoptr(GUnixFDList) fd_list = NULL;
+    ListenerSetupData data = { 0 };
+    int pair[2];
+    int idx;
+
+    if (qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
+        error_report("socketpair failed: %s", strerror(errno));
+        return NULL;
+    }
+
+    fd_list = g_unix_fd_list_new();
+    idx = g_unix_fd_list_append(fd_list, pair[1], &err);
+    close(pair[1]);
+    if (idx < 0) {
+        close(pair[0]);
+        error_report("Failed to append fd: %s", err->message);
+        return NULL;
+    }
+
+    loop = g_main_loop_new(NULL, FALSE);
+    data.loop = loop;
+    data.thread = p2p_dbus_thread_new(pair[0]);
+
+    qemu_dbus_display1_console_call_register_listener(
+        console,
+        g_variant_new_handle(idx),
+        G_DBUS_CALL_FLAGS_NONE,
+        -1,
+        fd_list,
+        NULL,
+        on_register_listener_finished,
+        &data);
+
+    g_main_loop_run(loop);
+
+    return data.listener_conn;
+}
+
+static void
+setup_display_listener(ConsoleData *cd)
+{
+    g_autoptr(GDBusObjectSkeleton) obj = NULL;
+    GDBusObjectManagerServer *server;
+    QemuDBusDisplay1Listener *iface;
+    QemuDBusDisplay1ListenerUnixMap *iface_map;
+
+    server = g_dbus_object_manager_server_new(DBUS_DISPLAY1_ROOT);
+    obj = g_dbus_object_skeleton_new(DBUS_DISPLAY1_ROOT "/Listener");
+
+    /* Main listener interface */
+    iface = qemu_dbus_display1_listener_skeleton_new();
+    g_object_connect(iface,
+                     "signal::handle-scanout", on_scanout, cd,
+                     "signal::handle-update", on_update, cd,
+                     "signal::handle-cursor-define", on_cursor_define, cd,
+                     NULL);
+    g_dbus_object_skeleton_add_interface(obj,
+                                         G_DBUS_INTERFACE_SKELETON(iface));
+
+    /* Unix shared memory map interface */
+    iface_map = qemu_dbus_display1_listener_unix_map_skeleton_new();
+    g_object_connect(iface_map,
+                     "signal::handle-scanout-map", on_scanout_map, cd,
+                     "signal::handle-update-map", on_update_map, cd,
+                     NULL);
+    g_dbus_object_skeleton_add_interface(obj,
+                                         G_DBUS_INTERFACE_SKELETON(iface_map));
+
+    {
+        const gchar *ifaces[] = {
+            "org.qemu.Display1.Listener.Unix.Map", NULL
+        };
+        g_object_set(iface, "interfaces", ifaces, NULL);
+    }
+
+    g_dbus_object_manager_server_export(server, obj);
+    g_dbus_object_manager_server_set_connection(server,
+                                                 cd->listener_conn);
+
+    g_dbus_connection_start_message_processing(cd->listener_conn);
+}
+
+static const GraphicHwOps vnc_hw_ops = {
+    .ui_info = display_ui_info,
+};
+
+bool console_setup(GDBusConnection *bus, const char *bus_name,
+                   const char *console_path)
+{
+    g_autoptr(GError) err = NULL;
+    ConsoleData *cd;
+    QemuConsole *con;
+
+    cd = g_new0(ConsoleData, 1);
+
+    cd->console_proxy = qemu_dbus_display1_console_proxy_new_sync(
+        bus, G_DBUS_PROXY_FLAGS_NONE, bus_name,
+        console_path, NULL, &err);
+    if (!cd->console_proxy) {
+        error_report("Failed to create console proxy for %s: %s",
+                     console_path, err->message);
+        g_free(cd);
+        return false;
+    }
+
+    cd->keyboard_proxy = QEMU_DBUS_DISPLAY1_KEYBOARD(
+        qemu_dbus_display1_keyboard_proxy_new_sync(
+            bus, G_DBUS_PROXY_FLAGS_NONE, bus_name,
+            console_path, NULL, &err));
+    if (!cd->keyboard_proxy) {
+        error_report("Failed to create keyboard proxy for %s: %s",
+                     console_path, err->message);
+        g_object_unref(cd->console_proxy);
+        g_free(cd);
+        return false;
+    }
+
+    g_clear_error(&err);
+    cd->mouse_proxy = QEMU_DBUS_DISPLAY1_MOUSE(
+        qemu_dbus_display1_mouse_proxy_new_sync(
+            bus, G_DBUS_PROXY_FLAGS_NONE, bus_name,
+            console_path, NULL, &err));
+    if (!cd->mouse_proxy) {
+        error_report("Failed to create mouse proxy for %s: %s",
+                     console_path, err->message);
+        g_object_unref(cd->keyboard_proxy);
+        g_object_unref(cd->console_proxy);
+        g_free(cd);
+        return false;
+    }
+
+    con = qemu_graphic_console_create(NULL, 0, &vnc_hw_ops, cd);
+    cd->gfx_con = QEMU_GRAPHIC_CONSOLE(con);
+
+    cd->listener_conn = console_register_display_listener(
+        cd->console_proxy);
+    if (!cd->listener_conn) {
+        error_report("Failed to setup D-Bus listener for %s",
+                     console_path);
+        g_object_unref(cd->mouse_proxy);
+        g_object_unref(cd->keyboard_proxy);
+        g_object_unref(cd->console_proxy);
+        g_free(cd);
+        return false;
+    }
+
+    setup_display_listener(cd);
+    input_setup(cd->keyboard_proxy, cd->mouse_proxy);
+
+    return true;
+}
+
+QemuDBusDisplay1Keyboard *console_get_keyboard(QemuConsole *con)
+{
+    ConsoleData *cd;
+
+    if (!QEMU_IS_GRAPHIC_CONSOLE(con)) {
+        return NULL;
+    }
+    cd = con->hw;
+    return cd ? cd->keyboard_proxy : NULL;
+}
+
+QemuDBusDisplay1Mouse *console_get_mouse(QemuConsole *con)
+{
+    ConsoleData *cd;
+
+    if (!QEMU_IS_GRAPHIC_CONSOLE(con)) {
+        return NULL;
+    }
+    cd = con->hw;
+    return cd ? cd->mouse_proxy : NULL;
+}
diff --git a/contrib/qemu-vnc/input.c b/contrib/qemu-vnc/input.c
new file mode 100644
index 00000000000..2313b0a7c77
--- /dev/null
+++ b/contrib/qemu-vnc/input.c
@@ -0,0 +1,239 @@
+/*
+ * Keyboard and mouse input dispatch via D-Bus.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "ui/dbus-display1.h"
+#include "ui/input.h"
+#include "trace.h"
+#include "qemu-vnc.h"
+
+struct QEMUPutLEDEntry {
+    QEMUPutLEDEvent *put_led;
+    void *opaque;
+    QTAILQ_ENTRY(QEMUPutLEDEntry) next;
+};
+
+static NotifierList mouse_mode_notifiers =
+    NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
+static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers =
+    QTAILQ_HEAD_INITIALIZER(led_handlers);
+
+/* Track the target console for pending mouse events (used by sync) */
+static QemuConsole *mouse_target;
+
+QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func,
+                                            void *opaque)
+{
+    QEMUPutLEDEntry *s;
+
+    s = g_new0(QEMUPutLEDEntry, 1);
+    s->put_led = func;
+    s->opaque = opaque;
+    QTAILQ_INSERT_TAIL(&led_handlers, s, next);
+    return s;
+}
+
+void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry)
+{
+    if (!entry) {
+        return;
+    }
+    QTAILQ_REMOVE(&led_handlers, entry, next);
+    g_free(entry);
+}
+
+static void
+on_keyboard_modifiers_changed(GObject *gobject, GParamSpec *pspec,
+                              gpointer user_data)
+{
+    guint modifiers;
+    QEMUPutLEDEntry *cursor;
+
+    modifiers = qemu_dbus_display1_keyboard_get_modifiers(
+        QEMU_DBUS_DISPLAY1_KEYBOARD(gobject));
+
+    /*
+     * The D-Bus Keyboard.Modifiers property uses the same
+     * bit layout as QEMU's LED constants.
+     */
+    QTAILQ_FOREACH(cursor, &led_handlers, next) {
+        cursor->put_led(cursor->opaque, modifiers);
+    }
+}
+
+void qemu_add_mouse_mode_change_notifier(Notifier *notify)
+{
+    notifier_list_add(&mouse_mode_notifiers, notify);
+}
+
+void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
+{
+    notifier_remove(notify);
+}
+
+void qemu_input_event_send_key_delay(uint32_t delay_ms)
+{
+}
+
+void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down)
+{
+    QemuDBusDisplay1Keyboard *kbd;
+    guint qnum;
+
+    trace_qemu_vnc_key_event(q, down);
+
+    if (!src) {
+        return;
+    }
+    kbd = console_get_keyboard(src);
+    if (!kbd) {
+        return;
+    }
+
+    if (q >= qemu_input_map_qcode_to_qnum_len) {
+        return;
+    }
+    qnum = qemu_input_map_qcode_to_qnum[q];
+
+    if (down) {
+        qemu_dbus_display1_keyboard_call_press(
+            kbd, qnum,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    } else {
+        qemu_dbus_display1_keyboard_call_release(
+            kbd, qnum,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
+}
+
+static guint abs_x, abs_y;
+static bool abs_pending;
+static gint rel_dx, rel_dy;
+static bool rel_pending;
+
+void qemu_input_queue_abs(QemuConsole *src, InputAxis axis,
+                          int value, int min_in, int max_in)
+{
+    if (axis == INPUT_AXIS_X) {
+        abs_x = value;
+    } else if (axis == INPUT_AXIS_Y) {
+        abs_y = value;
+    }
+    abs_pending = true;
+    mouse_target = src;
+}
+
+void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value)
+{
+    if (axis == INPUT_AXIS_X) {
+        rel_dx += value;
+    } else if (axis == INPUT_AXIS_Y) {
+        rel_dy += value;
+    }
+    rel_pending = true;
+    mouse_target = src;
+}
+
+void qemu_input_event_sync(void)
+{
+    QemuDBusDisplay1Mouse *mouse;
+
+    if (!mouse_target) {
+        return;
+    }
+
+    mouse = console_get_mouse(mouse_target);
+    if (!mouse) {
+        abs_pending = false;
+        rel_pending = false;
+        return;
+    }
+
+    if (abs_pending) {
+        trace_qemu_vnc_input_abs(abs_x, abs_y);
+        abs_pending = false;
+        qemu_dbus_display1_mouse_call_set_abs_position(
+            mouse, abs_x, abs_y,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+    }
+
+    if (rel_pending) {
+        trace_qemu_vnc_input_rel(rel_dx, rel_dy);
+        rel_pending = false;
+        qemu_dbus_display1_mouse_call_rel_motion(
+            mouse, rel_dx, rel_dy,
+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        rel_dx = 0;
+        rel_dy = 0;
+    }
+}
+
+bool qemu_input_is_absolute(QemuConsole *con)
+{
+    QemuDBusDisplay1Mouse *mouse;
+
+    if (!con) {
+        return false;
+    }
+    mouse = console_get_mouse(con);
+
+    if (!mouse) {
+        return false;
+    }
+    return qemu_dbus_display1_mouse_get_is_absolute(mouse);
+}
+
+static void
+on_mouse_is_absolute_changed(GObject *gobject, GParamSpec *pspec,
+                              gpointer user_data)
+{
+    notifier_list_notify(&mouse_mode_notifiers, NULL);
+}
+
+void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map,
+                               uint32_t button_old, uint32_t button_new)
+{
+    QemuDBusDisplay1Mouse *mouse;
+    uint32_t changed;
+    int i;
+
+    if (!src) {
+        return;
+    }
+    mouse = console_get_mouse(src);
+    if (!mouse) {
+        return;
+    }
+
+    changed = button_old ^ button_new;
+    for (i = 0; i < 32; i++) {
+        if (!(changed & (1u << i))) {
+            continue;
+        }
+        trace_qemu_vnc_input_btn(i, !!(button_new & (1u << i)));
+        if (button_new & (1u << i)) {
+            qemu_dbus_display1_mouse_call_press(
+                mouse, i,
+                G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        } else {
+            qemu_dbus_display1_mouse_call_release(
+                mouse, i,
+                G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+        }
+    }
+}
+
+void input_setup(QemuDBusDisplay1Keyboard *kbd,
+                 QemuDBusDisplay1Mouse *mouse)
+{
+    g_signal_connect(kbd, "notify::modifiers",
+                     G_CALLBACK(on_keyboard_modifiers_changed), NULL);
+    g_signal_connect(mouse, "notify::is-absolute",
+                     G_CALLBACK(on_mouse_is_absolute_changed), NULL);
+}
diff --git a/contrib/qemu-vnc/qemu-vnc.c b/contrib/qemu-vnc/qemu-vnc.c
new file mode 100644
index 00000000000..0e1d7bbf159
--- /dev/null
+++ b/contrib/qemu-vnc/qemu-vnc.c
@@ -0,0 +1,450 @@
+/*
+ * Standalone VNC server connecting to QEMU via D-Bus display interface.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/cutils.h"
+#include "qemu/datadir.h"
+#include "qemu/error-report.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
+#include "qemu/log.h"
+#include "qemu/main-loop.h"
+#include "qemu-version.h"
+#include "ui/vnc.h"
+#include "crypto/secret.h"
+#include "crypto/tlscredsx509.h"
+#include "trace.h"
+#include "qemu-vnc.h"
+
+const char *qemu_name;
+const char *keyboard_layout;
+
+static bool terminate;
+static VncDisplay *vd;
+
+static GType
+dbus_display_get_proxy_type(GDBusObjectManagerClient *manager,
+                            const gchar *object_path,
+                            const gchar *interface_name,
+                            gpointer user_data)
+{
+    static const struct {
+        const char *iface;
+        GType (*get_type)(void);
+    } types[] = {
+        { "org.qemu.Display1.Clipboard",
+          qemu_dbus_display1_clipboard_proxy_get_type },
+        { "org.qemu.Display1.Audio",
+          qemu_dbus_display1_audio_proxy_get_type },
+        { "org.qemu.Display1.Chardev",
+          qemu_dbus_display1_chardev_proxy_get_type },
+    };
+
+    if (!interface_name) {
+        return G_TYPE_DBUS_OBJECT_PROXY;
+    }
+
+    for (int i = 0; i < G_N_ELEMENTS(types); i++) {
+        if (g_str_equal(interface_name, types[i].iface)) {
+            return types[i].get_type();
+        }
+    }
+
+    return G_TYPE_DBUS_PROXY;
+}
+
+static void
+on_bus_closed(GDBusConnection *connection,
+              gboolean remote_peer_vanished,
+              GError *error,
+              gpointer user_data)
+{
+    terminate = true;
+    qemu_notify_event();
+}
+
+static void
+on_owner_vanished(GDBusConnection *connection,
+                  const gchar *name,
+                  gpointer user_data)
+{
+    trace_qemu_vnc_owner_vanished(name);
+    error_report("D-Bus peer %s vanished, terminating", name);
+    terminate = true;
+    qemu_notify_event();
+}
+
+typedef struct {
+    GDBusConnection *bus;
+    const char *bus_name;
+    const char * const *chardev_names;
+    bool no_vt;
+} ManagerSetupData;
+
+static int
+compare_console_paths(const void *a, const void *b)
+{
+    const char *pa = *(const char **)a;
+    const char *pb = *(const char **)b;
+    return strcmp(pa, pb);
+}
+
+static void
+on_manager_ready(GObject *source_object,
+                 GAsyncResult *res,
+                 gpointer user_data)
+{
+    ManagerSetupData *data = user_data;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GDBusObjectManager) manager = NULL;
+    GList *objects, *l;
+    g_autoptr(GPtrArray) console_paths = NULL;
+    bool found = false;
+
+    manager = G_DBUS_OBJECT_MANAGER(
+        g_dbus_object_manager_client_new_finish(res, &err));
+    if (!manager) {
+        error_report("Failed to create object manager: %s",
+                     err->message);
+        terminate = true;
+        qemu_notify_event();
+        g_free(data);
+        return;
+    }
+
+    /*
+     * Discover all Console objects and sort them so that console
+     * indices are assigned in a predictable order matching QEMU's.
+     */
+    console_paths = g_ptr_array_new_with_free_func(g_free);
+    objects = g_dbus_object_manager_get_objects(manager);
+    for (l = objects; l; l = l->next) {
+        GDBusObject *obj = l->data;
+        const char *path = g_dbus_object_get_object_path(obj);
+
+        if (g_str_has_prefix(path, DBUS_DISPLAY1_ROOT "/Console_")) {
+            g_ptr_array_add(console_paths, g_strdup(path));
+        }
+    }
+    g_list_free_full(objects, g_object_unref);
+
+    g_ptr_array_sort(console_paths, compare_console_paths);
+
+    for (guint i = 0; i < console_paths->len; i++) {
+        const char *path = g_ptr_array_index(console_paths, i);
+
+        if (!console_setup(data->bus, data->bus_name, path)) {
+            error_report("Failed to setup console %s", path);
+            continue;
+        }
+        found = true;
+    }
+
+    if (!found) {
+        error_report("No consoles found");
+        terminate = true;
+        qemu_notify_event();
+        g_free(data);
+        return;
+    }
+
+    /*
+     * Create the VNC display now that consoles exist, so that the
+     * display change listener is registered against a valid console.
+     */
+    {
+        Error *local_err = NULL;
+
+        vd = vnc_display_new("default", &local_err);
+        if (!vd) {
+            error_report_err(local_err);
+            terminate = true;
+            qemu_notify_event();
+            g_free(data);
+            return;
+        }
+    }
+
+    vnc_dbus_setup(data->bus);
+
+    clipboard_setup(manager, data->bus);
+    audio_setup(manager);
+    if (!data->no_vt) {
+        chardev_setup(data->chardev_names, manager);
+    }
+    g_free(data);
+}
+
+int main(int argc, char *argv[])
+{
+    Error *local_err = NULL;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GDBusConnection) bus = NULL;
+    g_autofree char *dbus_address = NULL;
+    g_autofree char *bus_name = NULL;
+    int dbus_p2p_fd = -1;
+    g_autofree char *vnc_addr = NULL;
+    g_autofree char *ws_addr = NULL;
+    g_autofree char *share = NULL;
+    g_autofree char *tls_creds_dir = NULL;
+    g_autofree char *trace_opt = NULL;
+    g_auto(GStrv) chardev_names = NULL;
+    const char *creds_dir;
+    bool has_vnc_password = false;
+    bool show_version = false;
+    bool no_vt = false;
+    bool password = false;
+    bool lossy = false;
+    bool non_adaptive = false;
+    g_autoptr(GOptionContext) context = NULL;
+    GOptionEntry entries[] = {
+        { "dbus-address", 'a', 0, G_OPTION_ARG_STRING, &dbus_address,
+          "D-Bus address to connect to (default: session bus)", "ADDRESS" },
+        { "dbus-p2p-fd", 'p', 0, G_OPTION_ARG_INT, &dbus_p2p_fd,
+          "D-Bus peer-to-peer socket file descriptor", "FD" },
+        { "bus-name", 'n', 0, G_OPTION_ARG_STRING, &bus_name,
+          "D-Bus bus name (default: org.qemu)", "NAME" },
+       { "vnc-addr", 'l', 0, G_OPTION_ARG_STRING, &vnc_addr,
+          "VNC display address (default localhost:0)", "ADDR" },
+        { "websocket", 'w', 0, G_OPTION_ARG_STRING, &ws_addr,
+          "WebSocket address (e.g. port number or addr:port)", "ADDR" },
+        { "share", 's', 0, G_OPTION_ARG_STRING, &share,
+          "Display sharing policy "
+          "(allow-exclusive|force-shared|ignore)", "POLICY" },
+        { "tls-creds", 't', 0, G_OPTION_ARG_STRING, &tls_creds_dir,
+          "TLS x509 credentials directory", "DIR" },
+        { "vt-chardev", 'C', 0, G_OPTION_ARG_STRING_ARRAY, &chardev_names,
+          "Chardev type names to expose as text console (repeatable, "
+          "default: serial & hmp)", "NAME" },
+        { "no-vt", 'N', 0, G_OPTION_ARG_NONE, &no_vt,
+          "Do not expose any chardevs as text consoles", NULL },
+        { "keyboard-layout", 'k', 0, G_OPTION_ARG_STRING, &keyboard_layout,
+          "Keyboard layout", "LAYOUT" },
+        { "trace", 'T', 0, G_OPTION_ARG_STRING, &trace_opt,
+          "Trace options (same as QEMU -trace)", "PATTERN" },
+        { "version", 'V', 0, G_OPTION_ARG_NONE, &show_version,
+          "Print version information and exit", NULL },
+        { "password", 0, 0, G_OPTION_ARG_NONE, &password,
+          "Require password authentication (use D-Bus SetPassword to set)",
+          NULL },
+        { "lossy", 0, 0, G_OPTION_ARG_NONE, &lossy,
+          "Enable lossy compression", NULL },
+        { "non-adaptive", 0, 0, G_OPTION_ARG_NONE, &non_adaptive,
+          "Disable adaptive encodings", NULL },
+        { NULL }
+    };
+
+    qemu_init_exec_dir(argv[0]);
+    qemu_add_data_dir(get_relocated_path(CONFIG_QEMU_DATADIR));
+
+    module_call_init(MODULE_INIT_TRACE);
+    module_call_init(MODULE_INIT_QOM);
+    module_call_init(MODULE_INIT_OPTS);
+    qemu_add_opts(&qemu_trace_opts);
+
+    context = g_option_context_new("- standalone VNC server for QEMU");
+    g_option_context_add_main_entries(context, entries, NULL);
+    if (!g_option_context_parse(context, &argc, &argv, &err)) {
+        error_report("Option parsing failed: %s", err->message);
+        return 1;
+    }
+
+    if (show_version) {
+        printf("qemu-vnc " QEMU_FULL_VERSION "\n");
+        return 0;
+    }
+
+    if (trace_opt) {
+        trace_opt_parse(trace_opt);
+        qemu_set_log(LOG_TRACE, &local_err);
+        if (local_err) {
+            error_report_err(local_err);
+            return 1;
+        }
+    }
+    trace_init_file();
+
+    if (qemu_init_main_loop(&local_err)) {
+        error_report_err(local_err);
+        return 1;
+    }
+
+    if (!vnc_addr) {
+        vnc_addr = g_strdup("localhost:0");
+    }
+
+    if (dbus_p2p_fd >= 0 && dbus_address) {
+        error_report("--dbus-p2p-fd and --dbus-address are"
+                     " mutually exclusive");
+        return 1;
+    }
+
+    if (dbus_p2p_fd >= 0) {
+        g_autoptr(GSocket) socket = NULL;
+        g_autoptr(GSocketConnection) socketc = NULL;
+
+        if (bus_name) {
+            error_report("--bus-name is not supported with --dbus-p2p-fd");
+            return 1;
+        }
+
+        socket = g_socket_new_from_fd(dbus_p2p_fd, &err);
+        if (!socket) {
+            error_report("Failed to create socket from fd %d: %s",
+                         dbus_p2p_fd, err->message);
+            return 1;
+        }
+
+        socketc = g_socket_connection_factory_create_connection(socket);
+        if (!socketc) {
+            error_report("Failed to create socket connection");
+            return 1;
+        }
+
+        bus = g_dbus_connection_new_sync(
+            G_IO_STREAM(socketc), NULL,
+            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+            NULL, NULL, &err);
+    } else if (dbus_address) {
+        GDBusConnectionFlags flags =
+            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT;
+        if (bus_name) {
+            flags |= G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION;
+        }
+        bus = g_dbus_connection_new_for_address_sync(
+            dbus_address, flags, NULL, NULL, &err);
+    } else {
+        bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &err);
+        if (!bus_name) {
+            bus_name = g_strdup("org.qemu");
+        }
+    }
+    if (!bus) {
+        error_report("Failed to connect to D-Bus: %s", err->message);
+        return 1;
+    }
+
+    {
+        g_autoptr(QemuDBusDisplay1VMProxy) vm_proxy = QEMU_DBUS_DISPLAY1_VM_PROXY(
+            qemu_dbus_display1_vm_proxy_new_sync(
+                bus, G_DBUS_PROXY_FLAGS_NONE, bus_name,
+                DBUS_DISPLAY1_ROOT "/VM", NULL, &err));
+        if (vm_proxy) {
+            qemu_name = g_strdup(qemu_dbus_display1_vm_get_name(
+                QEMU_DBUS_DISPLAY1_VM(vm_proxy)));
+        }
+    }
+
+    /*
+     * Set up TLS credentials if requested.  The object must exist
+     * before vnc_display_open() which looks it up by ID.
+     */
+    if (tls_creds_dir) {
+        if (!object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_X509,
+                                   object_get_objects_root(),
+                                   "tlscreds0",
+                                   &local_err,
+                                   "endpoint", "server",
+                                   "dir", tls_creds_dir,
+                                   "verify-peer", "no",
+                                   NULL)) {
+            error_report_err(local_err);
+            return 1;
+        }
+    }
+
+    /*
+     * Check for systemd credentials: if a vnc-password credential
+     * file exists, create a QCryptoSecret and enable VNC password auth.
+     */
+    creds_dir = g_getenv("CREDENTIALS_DIRECTORY");
+    if (creds_dir) {
+        g_autofree char *password_path =
+            g_build_filename(creds_dir, "vnc-password", NULL);
+        if (g_file_test(password_path, G_FILE_TEST_EXISTS)) {
+            if (!object_new_with_props(TYPE_QCRYPTO_SECRET,
+                                       object_get_objects_root(),
+                                       "vncsecret0",
+                                       &local_err,
+                                       "file", password_path,
+                                       NULL)) {
+                error_report_err(local_err);
+                return 1;
+            }
+            has_vnc_password = true;
+        }
+    }
+
+    {
+        g_autoptr(GString) vnc_opts = g_string_new(vnc_addr);
+        QemuOptsList *olist = qemu_find_opts("vnc");
+        QemuOpts *opts;
+
+        if (tls_creds_dir) {
+            g_string_append(vnc_opts, ",tls-creds=tlscreds0");
+        }
+        if (has_vnc_password) {
+            g_string_append(vnc_opts, ",password-secret=vncsecret0");
+        }
+        if (ws_addr) {
+            g_string_append_printf(vnc_opts, ",websocket=%s", ws_addr);
+        }
+        if (share) {
+            g_string_append_printf(vnc_opts, ",share=%s", share);
+        }
+        if (password && !has_vnc_password) {
+            g_string_append(vnc_opts, ",password=on");
+        }
+        if (lossy) {
+            g_string_append(vnc_opts, ",lossy=on");
+        }
+        if (non_adaptive) {
+            g_string_append(vnc_opts, ",non-adaptive=on");
+        }
+
+        opts = qemu_opts_parse_noisily(olist, vnc_opts->str, true);
+        if (!opts) {
+            return 1;
+        }
+        qemu_opts_set_id(opts, g_strdup("default"));
+    }
+
+    {
+        ManagerSetupData *mgr_data = g_new0(ManagerSetupData, 1);
+        mgr_data->bus = bus;
+        mgr_data->bus_name = bus_name;
+        mgr_data->chardev_names = (const char * const *)chardev_names;
+        mgr_data->no_vt = no_vt;
+
+        g_dbus_object_manager_client_new(
+            bus, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+            bus_name, DBUS_DISPLAY1_ROOT,
+            dbus_display_get_proxy_type,
+            NULL, NULL, NULL,
+            on_manager_ready, mgr_data);
+    }
+
+    g_signal_connect(bus, "closed", G_CALLBACK(on_bus_closed), NULL);
+
+    if (bus_name) {
+        g_bus_watch_name_on_connection(bus, bus_name,
+                                       G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                       NULL, on_owner_vanished,
+                                       NULL, NULL);
+    }
+
+    while (!terminate) {
+        main_loop_wait(false);
+    }
+
+    vnc_dbus_cleanup();
+    vnc_display_free(vd);
+
+    return 0;
+}
diff --git a/contrib/qemu-vnc/stubs.c b/contrib/qemu-vnc/stubs.c
new file mode 100644
index 00000000000..4a6332ba580
--- /dev/null
+++ b/contrib/qemu-vnc/stubs.c
@@ -0,0 +1,66 @@
+/*
+ * Stubs for qemu-vnc standalone binary.
+ *
+ * These provide dummy implementations of QEMU subsystem functions
+ * that the VNC code references but which are not needed (yet) for the
+ * standalone VNC server.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "system/runstate.h"
+#include "hw/core/qdev.h"
+#include "monitor/monitor.h"
+#include "migration/vmstate.h"
+
+bool runstate_is_running(void)
+{
+    return true;
+}
+
+bool phase_check(MachineInitPhase phase)
+{
+    return true;
+}
+
+DeviceState *qdev_find_recursive(BusState *bus, const char *id)
+{
+    return NULL;
+}
+
+/*
+ * Provide the monitor stubs locally so that the linker does not
+ * pull stubs/monitor-core.c.o from libqemuutil.a (which would
+ * bring a conflicting qapi_event_emit definition).
+ */
+Monitor *monitor_cur(void)
+{
+    return NULL;
+}
+
+bool monitor_cur_is_qmp(void)
+{
+    return false;
+}
+
+Monitor *monitor_set_cur(Coroutine *co, Monitor *mon)
+{
+    return NULL;
+}
+
+int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
+{
+    return -1;
+}
+
+/*
+ * Link-time stubs for VMState symbols referenced by VNC code.
+ * The standalone binary never performs migration, so these are
+ * never actually used at runtime.
+ */
+const VMStateInfo vmstate_info_bool = {};
+const VMStateInfo vmstate_info_int32 = {};
+const VMStateInfo vmstate_info_uint32 = {};
+const VMStateInfo vmstate_info_buffer = {};
diff --git a/contrib/qemu-vnc/utils.c b/contrib/qemu-vnc/utils.c
new file mode 100644
index 00000000000..d261aa9eaf0
--- /dev/null
+++ b/contrib/qemu-vnc/utils.c
@@ -0,0 +1,59 @@
+/*
+ * Standalone VNC server connecting to QEMU via D-Bus display interface.
+ *
+ * Copyright (C) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/error-report.h"
+#include "qemu-vnc.h"
+
+static GDBusConnection *
+dbus_p2p_from_fd(int fd)
+{
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GSocket) socket = NULL;
+    g_autoptr(GSocketConnection) socketc = NULL;
+    GDBusConnection *conn;
+
+    socket = g_socket_new_from_fd(fd, &err);
+    if (!socket) {
+        error_report("Failed to create socket: %s", err->message);
+        return NULL;
+    }
+
+    socketc = g_socket_connection_factory_create_connection(socket);
+    if (!socketc) {
+        error_report("Failed to create socket connection");
+        return NULL;
+    }
+
+    conn = g_dbus_connection_new_sync(
+        G_IO_STREAM(socketc), NULL,
+        G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+        G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
+        NULL, NULL, &err);
+    if (!conn) {
+        error_report("Failed to create D-Bus connection: %s", err->message);
+        return NULL;
+    }
+
+    return conn;
+}
+
+static gpointer
+p2p_server_setup_thread(gpointer data)
+{
+    return dbus_p2p_from_fd(GPOINTER_TO_INT(data));
+}
+
+GThread *
+p2p_dbus_thread_new(int fd)
+{
+    return g_thread_new("p2p-server-setup",
+                         p2p_server_setup_thread,
+                         GINT_TO_POINTER(fd));
+}
diff --git a/tests/qtest/dbus-vnc-test.c b/tests/qtest/dbus-vnc-test.c
new file mode 100644
index 00000000000..19d48ad49b4
--- /dev/null
+++ b/tests/qtest/dbus-vnc-test.c
@@ -0,0 +1,733 @@
+/*
+ * D-Bus VNC server (qemu-vnc) end-to-end test
+ *
+ * Starts QEMU with D-Bus display, connects qemu-vnc via p2p,
+ * then verifies a gvnc client can connect and read the VM name.
+ *
+ * Copyright (c) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include <gio/gio.h>
+#include <gvnc.h>
+#include <sys/un.h>
+#include "qemu/sockets.h"
+#include "libqtest.h"
+#include "qemu-vnc1.h"
+
+typedef struct Test {
+    QTestState *qts;
+    GSubprocess *vnc_subprocess;
+    VncConnection *conn;
+    GMainLoop *loop;
+    char *vnc_sock_path;
+    char *tmp_dir;
+} Test;
+
+typedef struct DbusTest {
+    QTestState *qts;
+    GSubprocess *vnc_subprocess;
+    GTestDBus *bus;
+    GDBusConnection *bus_conn;
+    GMainLoop *loop;
+    char *vnc_sock_path;
+    char *tmp_dir;
+    char *bus_addr;
+} DbusTest;
+
+typedef struct LifecycleData {
+    DbusTest *dt;
+    QemuVnc1Server *server_proxy;
+    VncConnection *conn;
+    char *client_path;
+    gboolean got_connected;
+    gboolean got_initialized;
+    gboolean got_disconnected;
+} LifecycleData;
+
+static void
+on_vnc_error(VncConnection *self, const char *msg)
+{
+    g_error("vnc-error: %s", msg);
+}
+
+static void
+on_vnc_auth_failure(VncConnection *self, const char *msg)
+{
+    g_error("vnc-auth-failure: %s", msg);
+}
+
+static void
+on_vnc_initialized(VncConnection *self, Test *test)
+{
+    const char *name = vnc_connection_get_name(test->conn);
+
+    g_assert_cmpstr(name, ==, "QEMU (dbus-vnc-test)");
+    g_main_loop_quit(test->loop);
+}
+
+static gboolean
+timeout_cb(gpointer data)
+{
+    g_error("test timed out");
+    return G_SOURCE_REMOVE;
+}
+
+static int
+connect_unix_socket(const char *path)
+{
+    int fd;
+    struct sockaddr_un addr = { .sun_family = AF_UNIX };
+
+    fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    g_assert(fd >= 0);
+
+    snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
+
+    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+        close(fd);
+        return -1;
+    }
+    return fd;
+}
+
+static bool
+wait_for_vnc_socket(const char *path, int timeout_ms)
+{
+    int elapsed = 0;
+    const int interval = 50;
+
+    while (elapsed < timeout_ms) {
+        int fd = connect_unix_socket(path);
+
+        if (fd >= 0) {
+            close(fd);
+            return true;
+        }
+
+        g_usleep(interval * 1000);
+        elapsed += interval;
+    }
+    return false;
+}
+
+static GSubprocess *
+spawn_qemu_vnc(int dbus_fd, const char *sock_path)
+{
+    const char *binary;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GSubprocessLauncher) launcher = NULL;
+    GSubprocess *proc;
+    g_autofree char *fd_str = NULL;
+    g_autofree char *vnc_addr = NULL;
+
+    binary = g_getenv("QTEST_QEMU_VNC_BINARY");
+    g_assert(binary != NULL);
+
+    fd_str = g_strdup_printf("%d", dbus_fd);
+    vnc_addr = g_strdup_printf("unix:%s", sock_path);
+
+    launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE);
+    g_subprocess_launcher_take_fd(launcher, dbus_fd, dbus_fd);
+
+    proc = g_subprocess_launcher_spawn(launcher, &err,
+                                       binary,
+                                       "--dbus-p2p-fd", fd_str,
+                                       "--vnc-addr", vnc_addr,
+                                       NULL);
+    g_assert_no_error(err);
+    g_assert(proc != NULL);
+
+    return proc;
+}
+
+static GSubprocess *
+spawn_qemu_vnc_bus_full(const char *dbus_addr, const char *sock_path,
+                        const char *const *extra_args)
+{
+    const char *binary;
+    g_autoptr(GError) err = NULL;
+    g_autoptr(GSubprocessLauncher) launcher = NULL;
+    g_autoptr(GPtrArray) argv = NULL;
+    GSubprocess *proc;
+    g_autofree char *vnc_addr = NULL;
+
+    binary = g_getenv("QTEST_QEMU_VNC_BINARY");
+    g_assert(binary != NULL);
+
+    vnc_addr = g_strdup_printf("unix:%s", sock_path);
+
+    argv = g_ptr_array_new();
+    g_ptr_array_add(argv, (gpointer)binary);
+    g_ptr_array_add(argv, (gpointer)"--dbus-address");
+    g_ptr_array_add(argv, (gpointer)dbus_addr);
+    g_ptr_array_add(argv, (gpointer)"--bus-name");
+    g_ptr_array_add(argv, (gpointer)"org.qemu");
+    g_ptr_array_add(argv, (gpointer)"--vnc-addr");
+    g_ptr_array_add(argv, (gpointer)vnc_addr);
+
+    if (extra_args) {
+        for (int i = 0; extra_args[i]; i++) {
+            g_ptr_array_add(argv, (gpointer)extra_args[i]);
+        }
+    }
+
+    g_ptr_array_add(argv, NULL);
+
+    launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE);
+    proc = g_subprocess_launcher_spawnv(launcher, (const char *const *)argv->pdata, &err);
+    g_assert_no_error(err);
+    g_assert(proc != NULL);
+
+    return proc;
+}
+
+
+static void
+name_appeared_cb(GDBusConnection *connection,
+                 const gchar *name,
+                 const gchar *name_owner,
+                 gpointer user_data)
+{
+    gboolean *appeared = user_data;
+    *appeared = TRUE;
+}
+
+static bool
+setup_dbus_test_full(DbusTest *dt, const char *const *vnc_extra_args)
+{
+    g_autoptr(GError) err = NULL;
+    g_auto(GStrv) addr_parts = NULL;
+    g_autofree char *qemu_args = NULL;
+
+    if (!g_getenv("QTEST_QEMU_VNC_BINARY")) {
+        g_test_skip("QTEST_QEMU_VNC_BINARY not set");
+        return false;
+    }
+
+    dt->bus = g_test_dbus_new(G_TEST_DBUS_NONE);
+    g_test_dbus_up(dt->bus);
+
+    /* remove ,guid=foo part */
+    addr_parts = g_strsplit(g_test_dbus_get_bus_address(dt->bus), ",", 2);
+    dt->bus_addr = g_strdup(addr_parts[0]);
+
+    dt->bus_conn = g_dbus_connection_new_for_address_sync(
+        g_test_dbus_get_bus_address(dt->bus),
+        G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+        G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
+        NULL, NULL, &err);
+    g_assert_no_error(err);
+
+    qemu_args = g_strdup_printf("-display dbus,addr=%s "
+                                "-name dbus-vnc-test", dt->bus_addr);
+    dt->qts = qtest_init(qemu_args);
+
+    dt->tmp_dir = g_dir_make_tmp("dbus-vnc-test-XXXXXX", NULL);
+    g_assert(dt->tmp_dir != NULL);
+    dt->vnc_sock_path = g_build_filename(dt->tmp_dir, "vnc.sock", NULL);
+    dt->vnc_subprocess = spawn_qemu_vnc_bus_full(dt->bus_addr,
+                                                 dt->vnc_sock_path,
+                                                 vnc_extra_args);
+
+    /*
+     * Wait for the org.qemu.vnc bus name to appear, which indicates
+     * qemu-vnc has fully initialized (connected to QEMU, set up the
+     * display, exported its D-Bus interfaces, and opened the VNC
+     * socket).
+     */
+    {
+        guint watch_id, timeout_id;
+        gboolean appeared = FALSE;
+
+        watch_id = g_bus_watch_name_on_connection(
+            dt->bus_conn, "org.qemu.vnc",
+            G_BUS_NAME_WATCHER_FLAGS_NONE,
+            name_appeared_cb, NULL, &appeared, NULL);
+        timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
+
+        while (!appeared &&
+               g_main_context_iteration(NULL, TRUE)) {
+            /* spin until name appears or timeout fires */
+        }
+
+        g_bus_unwatch_name(watch_id);
+        g_source_remove(timeout_id);
+
+        if (!appeared) {
+            g_test_fail();
+            g_test_message("Timed out waiting for org.qemu.vnc bus name");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool
+setup_dbus_test(DbusTest *dt)
+{
+    return setup_dbus_test_full(dt, NULL);
+}
+
+static void
+cleanup_dbus_test(DbusTest *dt)
+{
+    if (dt->bus_conn) {
+        g_dbus_connection_close_sync(dt->bus_conn, NULL, NULL);
+        g_object_unref(dt->bus_conn);
+    }
+    if (dt->vnc_subprocess) {
+        g_subprocess_force_exit(dt->vnc_subprocess);
+        g_subprocess_wait(dt->vnc_subprocess, NULL, NULL);
+        g_object_unref(dt->vnc_subprocess);
+    }
+    if (dt->vnc_sock_path) {
+        unlink(dt->vnc_sock_path);
+        g_free(dt->vnc_sock_path);
+    }
+    if (dt->tmp_dir) {
+        rmdir(dt->tmp_dir);
+        g_free(dt->tmp_dir);
+    }
+    if (dt->qts) {
+        qtest_quit(dt->qts);
+    }
+    if (dt->bus) {
+        g_test_dbus_down(dt->bus);
+        g_object_unref(dt->bus);
+    }
+    g_free(dt->bus_addr);
+}
+
+static void
+test_dbus_vnc_basic(void)
+{
+    Test test = { 0 };
+    int pair[2];
+    int vnc_fd;
+    guint timeout_id;
+
+    if (!g_getenv("QTEST_QEMU_VNC_BINARY")) {
+        g_test_skip("QTEST_QEMU_VNC_BINARY not set");
+        return;
+    }
+
+    test.qts = qtest_init("-display dbus,p2p=yes -name dbus-vnc-test");
+
+    g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
+    qtest_qmp_add_client(test.qts, "@dbus-display", pair[1]);
+    close(pair[1]);
+
+    test.tmp_dir = g_dir_make_tmp("dbus-vnc-test-XXXXXX", NULL);
+    g_assert(test.tmp_dir != NULL);
+    test.vnc_sock_path = g_build_filename(test.tmp_dir, "vnc.sock", NULL);
+
+    test.vnc_subprocess = spawn_qemu_vnc(pair[0], test.vnc_sock_path);
+
+    if (!wait_for_vnc_socket(test.vnc_sock_path, 10000)) {
+        g_test_fail();
+        g_test_message("Timed out waiting for qemu-vnc socket");
+        goto cleanup;
+    }
+
+    vnc_fd = connect_unix_socket(test.vnc_sock_path);
+    g_assert(vnc_fd >= 0);
+
+    test.conn = vnc_connection_new();
+    g_signal_connect(test.conn, "vnc-error",
+                     G_CALLBACK(on_vnc_error), NULL);
+    g_signal_connect(test.conn, "vnc-auth-failure",
+                     G_CALLBACK(on_vnc_auth_failure), NULL);
+    g_signal_connect(test.conn, "vnc-initialized",
+                     G_CALLBACK(on_vnc_initialized), &test);
+    vnc_connection_set_auth_type(test.conn, VNC_CONNECTION_AUTH_NONE);
+    vnc_connection_open_fd(test.conn, vnc_fd);
+
+    test.loop = g_main_loop_new(NULL, FALSE);
+    timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
+    g_main_loop_run(test.loop);
+    g_source_remove(timeout_id);
+
+cleanup:
+    if (test.conn) {
+        vnc_connection_shutdown(test.conn);
+        g_signal_handlers_disconnect_by_data(test.conn, NULL);
+        g_object_unref(test.conn);
+    }
+    if (test.loop) {
+        g_main_loop_unref(test.loop);
+    }
+    if (test.vnc_subprocess) {
+        g_subprocess_force_exit(test.vnc_subprocess);
+        g_subprocess_wait(test.vnc_subprocess, NULL, NULL);
+        g_object_unref(test.vnc_subprocess);
+    }
+    if (test.vnc_sock_path) {
+        unlink(test.vnc_sock_path);
+        g_free(test.vnc_sock_path);
+    }
+    if (test.tmp_dir) {
+        rmdir(test.tmp_dir);
+        g_free(test.tmp_dir);
+    }
+    qtest_quit(test.qts);
+}
+
+static void
+test_dbus_vnc_server_props(void)
+{
+    DbusTest dt = { 0 };
+    QemuVnc1Server *proxy = NULL;
+    g_autoptr(GError) err = NULL;
+    const gchar *const *clients;
+    GVariant *listeners;
+
+    if (!setup_dbus_test(&dt)) {
+        goto cleanup;
+    }
+
+    proxy = qemu_vnc1_server_proxy_new_sync(
+        dt.bus_conn,
+        G_DBUS_PROXY_FLAGS_NONE,
+        "org.qemu.vnc",
+        "/org/qemu/Vnc1/Server",
+        NULL, &err);
+    g_assert_no_error(err);
+    g_assert(proxy != NULL);
+
+    g_assert_cmpstr(qemu_vnc1_server_get_name(proxy), ==,
+                    "dbus-vnc-test");
+    g_assert_cmpstr(qemu_vnc1_server_get_auth(proxy), ==,
+                    "none");
+    g_assert_cmpstr(qemu_vnc1_server_get_vencrypt_sub_auth(proxy), ==,
+                    "");
+
+    clients = qemu_vnc1_server_get_clients(proxy);
+    g_assert_nonnull(clients);
+    g_assert_cmpint(g_strv_length((gchar **)clients), ==, 0);
+
+    listeners = qemu_vnc1_server_get_listeners(proxy);
+    g_assert_nonnull(listeners);
+    g_assert_cmpint(g_variant_n_children(listeners), >, 0);
+
+cleanup:
+    g_clear_object(&proxy);
+    cleanup_dbus_test(&dt);
+}
+
+static void
+on_client_connected(QemuVnc1Server *proxy,
+                    const gchar *client_path,
+                    LifecycleData *data)
+{
+    data->got_connected = TRUE;
+    data->client_path = g_strdup(client_path);
+}
+
+static void
+on_lifecycle_vnc_initialized(VncConnection *self, LifecycleData *data)
+{
+    /* VNC handshake done, wait for ClientInitialized D-Bus signal */
+}
+
+static void
+on_client_initialized(QemuVnc1Server *proxy,
+                      const gchar *client_path,
+                      LifecycleData *data)
+{
+    data->got_initialized = TRUE;
+    g_main_loop_quit(data->dt->loop);
+}
+
+static void
+on_client_disconnected(QemuVnc1Server *proxy,
+                       const gchar *client_path,
+                       LifecycleData *data)
+{
+    data->got_disconnected = TRUE;
+    g_main_loop_quit(data->dt->loop);
+}
+
+static void
+test_dbus_vnc_client_lifecycle(void)
+{
+    DbusTest dt = { 0 };
+    QemuVnc1Server *server_proxy = NULL;
+    QemuVnc1Client *client_proxy = NULL;
+    g_autoptr(GError) err = NULL;
+    LifecycleData ldata = { 0 };
+    int vnc_fd;
+    guint timeout_id;
+
+    if (!setup_dbus_test(&dt)) {
+        goto cleanup;
+    }
+
+    server_proxy = qemu_vnc1_server_proxy_new_sync(
+        dt.bus_conn,
+        G_DBUS_PROXY_FLAGS_NONE,
+        "org.qemu.vnc",
+        "/org/qemu/Vnc1/Server",
+        NULL, &err);
+    g_assert_no_error(err);
+
+    ldata.dt = &dt;
+    ldata.server_proxy = server_proxy;
+
+    g_signal_connect(server_proxy, "client-connected",
+                     G_CALLBACK(on_client_connected), &ldata);
+    g_signal_connect(server_proxy, "client-initialized",
+                     G_CALLBACK(on_client_initialized), &ldata);
+    g_signal_connect(server_proxy, "client-disconnected",
+                     G_CALLBACK(on_client_disconnected), &ldata);
+
+    vnc_fd = connect_unix_socket(dt.vnc_sock_path);
+    g_assert(vnc_fd >= 0);
+
+    ldata.conn = vnc_connection_new();
+    g_signal_connect(ldata.conn, "vnc-error",
+                     G_CALLBACK(on_vnc_error), NULL);
+    g_signal_connect(ldata.conn, "vnc-auth-failure",
+                     G_CALLBACK(on_vnc_auth_failure), NULL);
+    g_signal_connect(ldata.conn, "vnc-initialized",
+                     G_CALLBACK(on_lifecycle_vnc_initialized), &ldata);
+    vnc_connection_set_auth_type(ldata.conn, VNC_CONNECTION_AUTH_NONE);
+    vnc_connection_open_fd(ldata.conn, vnc_fd);
+
+    /* Phase 1: wait for ClientInitialized */
+    dt.loop = g_main_loop_new(NULL, FALSE);
+    timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
+    g_main_loop_run(dt.loop);
+    g_source_remove(timeout_id);
+
+    g_assert_true(ldata.got_connected);
+    g_assert_true(ldata.got_initialized);
+    g_assert_nonnull(ldata.client_path);
+
+    /* Check client properties while still connected */
+    client_proxy = qemu_vnc1_client_proxy_new_sync(
+        dt.bus_conn,
+        G_DBUS_PROXY_FLAGS_NONE,
+        "org.qemu.vnc",
+        ldata.client_path,
+        NULL, &err);
+    g_assert_no_error(err);
+
+    g_assert_cmpstr(qemu_vnc1_client_get_family(client_proxy), ==,
+                    "unix");
+    g_assert_false(qemu_vnc1_client_get_web_socket(client_proxy));
+    g_assert_cmpstr(qemu_vnc1_client_get_x509_dname(client_proxy), ==,
+                    "");
+    g_assert_cmpstr(qemu_vnc1_client_get_sasl_username(client_proxy),
+                    ==, "");
+
+    /* Phase 2: disconnect and wait for ClientDisconnected */
+    vnc_connection_shutdown(ldata.conn);
+    timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
+    g_main_loop_run(dt.loop);
+    g_source_remove(timeout_id);
+
+    g_assert_true(ldata.got_disconnected);
+
+    g_object_unref(ldata.conn);
+    g_main_loop_unref(dt.loop);
+    dt.loop = NULL;
+    g_free(ldata.client_path);
+
+cleanup:
+    g_clear_object(&server_proxy);
+    g_clear_object(&client_proxy);
+    cleanup_dbus_test(&dt);
+}
+
+static void
+test_dbus_vnc_no_password(void)
+{
+    DbusTest dt = { 0 };
+    QemuVnc1Server *proxy = NULL;
+    g_autoptr(GError) err = NULL;
+    gboolean ret;
+
+    if (!setup_dbus_test(&dt)) {
+        goto cleanup;
+    }
+
+    proxy = qemu_vnc1_server_proxy_new_sync(
+        dt.bus_conn,
+        G_DBUS_PROXY_FLAGS_NONE,
+        "org.qemu.vnc",
+        "/org/qemu/Vnc1/Server",
+        NULL, &err);
+    g_assert_no_error(err);
+
+    /*
+     * With default auth=none, SetPassword should return an error
+     * because VNC password authentication is not enabled.
+     */
+    ret = qemu_vnc1_server_call_set_password_sync(
+        proxy, "secret",
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
+    g_assert_false(ret);
+    g_assert_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED);
+    g_clear_error(&err);
+
+    /*
+     * ExpirePassword succeeds even without password auth —
+     * it just sets the expiry timestamp.
+     */
+    ret = qemu_vnc1_server_call_expire_password_sync(
+        proxy, "never",
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
+    g_assert_no_error(err);
+    g_assert_true(ret);
+
+    ret = qemu_vnc1_server_call_expire_password_sync(
+        proxy, "+3600",
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
+    g_assert_no_error(err);
+    g_assert_true(ret);
+
+cleanup:
+    g_clear_object(&proxy);
+    cleanup_dbus_test(&dt);
+}
+
+typedef struct PasswordData {
+    DbusTest *dt;
+    VncConnection *conn;
+    const char *password;
+    gboolean auth_succeeded;
+    gboolean auth_failed;
+} PasswordData;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+static void
+on_pw_vnc_auth_credential(VncConnection *conn, GValueArray *creds,
+                          PasswordData *data)
+{
+    for (guint i = 0; i < creds->n_values; i++) {
+        int type = g_value_get_enum(g_value_array_get_nth(creds, i));
+
+        if (type == VNC_CONNECTION_CREDENTIAL_PASSWORD) {
+            vnc_connection_set_credential(conn, type, data->password);
+        }
+    }
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+static void
+on_pw_vnc_initialized(VncConnection *conn, PasswordData *data)
+{
+    data->auth_succeeded = TRUE;
+    g_main_loop_quit(data->dt->loop);
+}
+
+static void
+on_pw_vnc_auth_failure(VncConnection *conn, const char *msg,
+                       PasswordData *data)
+{
+    data->auth_failed = TRUE;
+    g_main_loop_quit(data->dt->loop);
+}
+
+static void
+on_pw_vnc_error(VncConnection *conn, const char *msg,
+                PasswordData *data)
+{
+    data->auth_failed = TRUE;
+    g_main_loop_quit(data->dt->loop);
+}
+
+static void
+test_dbus_vnc_password_auth(void)
+{
+    DbusTest dt = { 0 };
+    QemuVnc1Server *proxy = NULL;
+    g_autoptr(GError) err = NULL;
+    PasswordData pdata = { 0 };
+    const char *extra_args[] = { "--password", NULL };
+    int vnc_fd;
+    guint timeout_id;
+    gboolean ret;
+
+    if (!setup_dbus_test_full(&dt, extra_args)) {
+        goto cleanup;
+    }
+
+    proxy = qemu_vnc1_server_proxy_new_sync(
+        dt.bus_conn,
+        G_DBUS_PROXY_FLAGS_NONE,
+        "org.qemu.vnc",
+        "/org/qemu/Vnc1/Server",
+        NULL, &err);
+    g_assert_no_error(err);
+
+    /* Verify auth type is "vnc" when --password is used */
+    g_assert_cmpstr(qemu_vnc1_server_get_auth(proxy), ==, "vnc");
+
+    /* Set password via D-Bus — should succeed with --password */
+    ret = qemu_vnc1_server_call_set_password_sync(
+        proxy, "testpass123",
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
+    g_assert_no_error(err);
+    g_assert_true(ret);
+
+    /* Connect with the correct password */
+    vnc_fd = connect_unix_socket(dt.vnc_sock_path);
+    g_assert(vnc_fd >= 0);
+
+    pdata.dt = &dt;
+    pdata.password = "testpass123";
+    pdata.conn = vnc_connection_new();
+
+    g_signal_connect(pdata.conn, "vnc-error",
+                     G_CALLBACK(on_pw_vnc_error), &pdata);
+    g_signal_connect(pdata.conn, "vnc-auth-failure",
+                     G_CALLBACK(on_pw_vnc_auth_failure), &pdata);
+    g_signal_connect(pdata.conn, "vnc-auth-credential",
+                     G_CALLBACK(on_pw_vnc_auth_credential), &pdata);
+    g_signal_connect(pdata.conn, "vnc-initialized",
+                     G_CALLBACK(on_pw_vnc_initialized), &pdata);
+    vnc_connection_set_auth_type(pdata.conn, VNC_CONNECTION_AUTH_VNC);
+    vnc_connection_open_fd(pdata.conn, vnc_fd);
+
+    dt.loop = g_main_loop_new(NULL, FALSE);
+    timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
+    g_main_loop_run(dt.loop);
+    g_source_remove(timeout_id);
+
+    g_assert_true(pdata.auth_succeeded);
+    g_assert_false(pdata.auth_failed);
+
+    vnc_connection_shutdown(pdata.conn);
+    g_object_unref(pdata.conn);
+    g_main_loop_unref(dt.loop);
+    dt.loop = NULL;
+
+cleanup:
+    g_clear_object(&proxy);
+    cleanup_dbus_test(&dt);
+}
+
+int
+main(int argc, char **argv)
+{
+    g_log_set_always_fatal(G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
+
+    if (getenv("GTK_VNC_DEBUG")) {
+        vnc_util_set_debug(true);
+    }
+
+    g_test_init(&argc, &argv, NULL);
+
+    qtest_add_func("/dbus-vnc/basic", test_dbus_vnc_basic);
+    qtest_add_func("/dbus-vnc/server-props", test_dbus_vnc_server_props);
+    qtest_add_func("/dbus-vnc/client-lifecycle", test_dbus_vnc_client_lifecycle);
+    qtest_add_func("/dbus-vnc/no-password", test_dbus_vnc_no_password);
+    qtest_add_func("/dbus-vnc/password-auth", test_dbus_vnc_password_auth);
+
+    return g_test_run();
+}
diff --git a/contrib/qemu-vnc/meson.build b/contrib/qemu-vnc/meson.build
new file mode 100644
index 00000000000..08168da0630
--- /dev/null
+++ b/contrib/qemu-vnc/meson.build
@@ -0,0 +1,26 @@
+vnca = vnc_ss.apply({}, strict: false)
+
+qemu_vnc1 = custom_target('qemu-vnc1 gdbus-codegen',
+                           output: ['qemu-vnc1.h', 'qemu-vnc1.c'],
+                           input: files('qemu-vnc1.xml'),
+                           command: [gdbus_codegen, '@INPUT@',
+                                     '--glib-min-required', '2.64',
+                                     '--output-directory', meson.current_build_dir(),
+                                     '--interface-prefix', 'org.qemu.',
+                                     '--c-namespace', 'Qemu',
+                                     '--generate-c-code', '@BASENAME@'])
+
+qemu_vnc = executable('qemu-vnc',
+  sources: ['qemu-vnc.c', 'display.c', 'input.c',
+            'audio.c', 'chardev.c', 'clipboard.c', 'console.c',
+            'dbus.c', 'stubs.c', 'utils.c',
+            vnca.sources(), dbus_display1, qemu_vnc1],
+  dependencies: [vnca.dependencies(), io, crypto, qemuutil, gio, ui])
+
+# The executable lives in a subdirectory of the build tree, but
+# get_relocated_path() looks for qemu-bundle relative to the binary.
+# Create a symlink so that firmware/keymap lookup works during development.
+run_command('ln', '-sfn',
+            '../../qemu-bundle',
+            meson.current_build_dir() / 'qemu-bundle',
+            check: false)
diff --git a/contrib/qemu-vnc/qemu-vnc1.xml b/contrib/qemu-vnc/qemu-vnc1.xml
new file mode 100644
index 00000000000..2037e72ae2a
--- /dev/null
+++ b/contrib/qemu-vnc/qemu-vnc1.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="utf-8"?>
+<node>
+  <!--
+      org.qemu.Vnc1.Server:
+
+      This interface is implemented on ``/org/qemu/Vnc1/Server``.
+      It provides management and monitoring of the VNC server.
+  -->
+  <interface name="org.qemu.Vnc1.Server">
+    <!--
+        Name:
+
+        The VM name.
+    -->
+    <property name="Name" type="s" access="read"/>
+
+    <!--
+        Auth:
+
+        Primary authentication method (none, vnc, vencrypt, sasl, etc.).
+    -->
+    <property name="Auth" type="s" access="read"/>
+
+    <!--
+        VencryptSubAuth:
+
+        VEncrypt sub-authentication method, if applicable.
+        Empty string otherwise.
+    -->
+    <property name="VencryptSubAuth" type="s" access="read"/>
+
+    <!--
+        Clients:
+
+        Object paths of connected VNC clients.
+    -->
+    <property name="Clients" type="ao" access="read"/>
+
+    <!--
+        Listeners:
+
+        List of listening sockets. Each entry is a dictionary with keys:
+        ``Host`` (s), ``Service`` (s), ``Family`` (s),
+        ``WebSocket`` (b), ``Auth`` (s), ``VencryptSubAuth`` (s).
+    -->
+    <property name="Listeners" type="aa{sv}" access="read"/>
+
+    <!--
+        SetPassword:
+        @password: The new VNC password.
+
+        Change the VNC password.  Existing clients are unaffected.
+    -->
+    <method name="SetPassword">
+      <arg type="s" name="password" direction="in"/>
+    </method>
+
+    <!--
+        ExpirePassword:
+        @time: Expiry specification.
+
+        Set password expiry.  Values: ``"now"``, ``"never"``,
+        ``"+N"`` (seconds from now), ``"N"`` (absolute epoch seconds).
+    -->
+    <method name="ExpirePassword">
+      <arg type="s" name="time" direction="in"/>
+    </method>
+
+    <!--
+        ReloadCertificates:
+
+        Reload TLS certificates from disk.
+    -->
+    <method name="ReloadCertificates"/>
+
+    <!--
+        ClientConnected:
+        @client: Object path of the new client.
+
+        Emitted when a VNC client TCP connection is established
+        (before authentication).
+    -->
+    <signal name="ClientConnected">
+      <arg type="o" name="client"/>
+    </signal>
+
+    <!--
+        ClientInitialized:
+        @client: Object path of the client.
+
+        Emitted when a VNC client has completed authentication
+        and is active.
+    -->
+    <signal name="ClientInitialized">
+      <arg type="o" name="client"/>
+    </signal>
+
+    <!--
+        ClientDisconnected:
+        @client: Object path of the client.
+
+        Emitted when a VNC client disconnects.
+    -->
+    <signal name="ClientDisconnected">
+      <arg type="o" name="client"/>
+    </signal>
+  </interface>
+
+  <!--
+      org.qemu.Vnc1.Client:
+
+      This interface is implemented on ``/org/qemu/Vnc1/Client_$id``.
+      It exposes information about a connected VNC client.
+  -->
+  <interface name="org.qemu.Vnc1.Client">
+    <!--
+        Host:
+
+        Client IP address.
+    -->
+    <property name="Host" type="s" access="read"/>
+
+    <!--
+        Service:
+
+        Client port or service name. This may depend on the host system’s
+        service database so symbolic names should not be relied on.
+    -->
+    <property name="Service" type="s" access="read"/>
+
+    <!--
+        Family:
+
+        Address family (ipv4, ipv6, unix).
+    -->
+    <property name="Family" type="s" access="read"/>
+
+    <!--
+        WebSocket:
+
+        Whether this is a WebSocket connection.
+    -->
+    <property name="WebSocket" type="b" access="read"/>
+
+    <!--
+        X509Dname:
+
+        X.509 distinguished name (empty if not applicable).
+    -->
+    <property name="X509Dname" type="s" access="read"/>
+
+    <!--
+        SaslUsername:
+
+        SASL username (empty if not applicable).
+    -->
+    <property name="SaslUsername" type="s" access="read"/>
+
+    <!--
+        ShutdownRequest:
+
+        Emitted when the VNC client requests a guest shutdown.
+    -->
+    <signal name="ShutdownRequest"/>
+
+    <!--
+        ResetRequest:
+
+        Emitted when the VNC client requests a guest reset.
+    -->
+    <signal name="ResetRequest"/>
+  </interface>
+
+</node>
diff --git a/contrib/qemu-vnc/trace-events b/contrib/qemu-vnc/trace-events
new file mode 100644
index 00000000000..f2d66a80986
--- /dev/null
+++ b/contrib/qemu-vnc/trace-events
@@ -0,0 +1,20 @@
+qemu_vnc_audio_out_fini(uint64_t id) "id=%" PRIu64
+qemu_vnc_audio_out_init(uint64_t id, uint32_t freq, uint8_t channels, uint8_t bits) "id=%" PRIu64 " freq=%u ch=%u bits=%u"
+qemu_vnc_audio_out_set_enabled(uint64_t id, bool enabled) "id=%" PRIu64 " enabled=%d"
+qemu_vnc_audio_out_write(uint64_t id, size_t size) "id=%" PRIu64 " size=%zu"
+qemu_vnc_chardev_connected(const char *name) "name=%s"
+qemu_vnc_clipboard_grab(int selection, uint32_t serial) "selection=%d serial=%u"
+qemu_vnc_clipboard_release(int selection) "selection=%d"
+qemu_vnc_clipboard_request(int selection) "selection=%d"
+qemu_vnc_client_not_found(const char *host, const char *service) "host=%s service=%s"
+qemu_vnc_console_io_error(const char *name) "name=%s"
+qemu_vnc_cursor_define(int width, int height, int hot_x, int hot_y) "w=%d h=%d hot=%d,%d"
+qemu_vnc_input_abs(uint32_t x, uint32_t y) "x=%u y=%u"
+qemu_vnc_input_btn(int button, bool press) "button=%d press=%d"
+qemu_vnc_input_rel(int dx, int dy) "dx=%d dy=%d"
+qemu_vnc_key_event(int qcode, bool down) "qcode=%d down=%d"
+qemu_vnc_owner_vanished(const char *name) "peer=%s"
+qemu_vnc_scanout(uint32_t width, uint32_t height, uint32_t stride, uint32_t format) "w=%u h=%u stride=%u fmt=0x%x"
+qemu_vnc_scanout_map(uint32_t width, uint32_t height, uint32_t stride, uint32_t format, uint32_t offset) "w=%u h=%u stride=%u fmt=0x%x offset=%u"
+qemu_vnc_update(int x, int y, int w, int h, uint32_t stride, uint32_t format) "x=%d y=%d w=%d h=%d stride=%u fmt=0x%x"
+qemu_vnc_update_map(uint32_t x, uint32_t y, uint32_t w, uint32_t h) "x=%u y=%u w=%u h=%u"
diff --git a/meson_options.txt b/meson_options.txt
index 31d5916cfce..ef938e74793 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -119,6 +119,8 @@ option('vfio_user_server', type: 'feature', value: 'disabled',
        description: 'vfio-user server support')
 option('dbus_display', type: 'feature', value: 'auto',
        description: '-display dbus support')
+option('qemu_vnc', type: 'feature', value: 'auto',
+       description: 'standalone VNC server over D-Bus')
 option('tpm', type : 'feature', value : 'auto',
        description: 'TPM support')
 option('valgrind', type : 'feature', value: 'auto',
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index ca5b113119a..5f7a351ca4a 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -174,6 +174,7 @@ meson_options_help() {
   printf "%s\n" '  qatzip          QATzip compression support'
   printf "%s\n" '  qcow1           qcow1 image format support'
   printf "%s\n" '  qed             qed image format support'
+  printf "%s\n" '  qemu-vnc        standalone VNC server over D-Bus'
   printf "%s\n" '  qga-vss         build QGA VSS support (broken with MinGW)'
   printf "%s\n" '  qpl             Query Processing Library support'
   printf "%s\n" '  rbd             Ceph block device driver'
@@ -458,6 +459,8 @@ _meson_option_parse() {
     --qemu-ga-manufacturer=*) quote_sh "-Dqemu_ga_manufacturer=$2" ;;
     --qemu-ga-version=*) quote_sh "-Dqemu_ga_version=$2" ;;
     --with-suffix=*) quote_sh "-Dqemu_suffix=$2" ;;
+    --enable-qemu-vnc) printf "%s" -Dqemu_vnc=enabled ;;
+    --disable-qemu-vnc) printf "%s" -Dqemu_vnc=disabled ;;
     --enable-qga-vss) printf "%s" -Dqga_vss=enabled ;;
     --disable-qga-vss) printf "%s" -Dqga_vss=disabled ;;
     --enable-qom-cast-debug) printf "%s" -Dqom_cast_debug=true ;;
diff --git a/tests/dbus-daemon.sh b/tests/dbus-daemon.sh
index c4a50c73774..85f9597db43 100755
--- a/tests/dbus-daemon.sh
+++ b/tests/dbus-daemon.sh
@@ -62,9 +62,17 @@ write_config()
      <deny send_destination="org.freedesktop.DBus"
            send_interface="org.freedesktop.systemd1.Activator"/>
 
-     <allow own="org.qemu.VMState1"/>
-     <allow send_destination="org.qemu.VMState1"/>
-     <allow receive_sender="org.qemu.VMState1"/>
+    <allow own="org.qemu"/>
+    <allow send_destination="org.qemu"/>
+    <allow receive_sender="org.qemu"/>
+
+    <allow own="org.qemu.VMState1"/>
+    <allow send_destination="org.qemu.VMState1"/>
+    <allow receive_sender="org.qemu.VMState1"/>
+
+    <allow own="org.qemu.vnc"/>
+    <allow send_destination="org.qemu.vnc"/>
+    <allow receive_sender="org.qemu.vnc"/>
 
   </policy>
 
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 5f8cff172c8..0eca271abc8 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -411,6 +411,10 @@ if vnc.found()
   if gvnc.found()
     qtests += {'vnc-display-test': [gvnc, keymap_targets]}
     qtests_generic += [ 'vnc-display-test' ]
+    if have_qemu_vnc and dbus_display and config_all_devices.has_key('CONFIG_VGA')
+      qtests += {'dbus-vnc-test': [dbus_display1, qemu_vnc1, gio, gvnc, keymap_targets]}
+      qtests_x86_64 += ['dbus-vnc-test']
+    endif
   endif
 endif
 
@@ -442,6 +446,10 @@ foreach dir : target_dirs
     qtest_env.set('QTEST_QEMU_STORAGE_DAEMON_BINARY', './storage-daemon/qemu-storage-daemon')
     test_deps += [qsd]
   endif
+  if have_qemu_vnc
+    qtest_env.set('QTEST_QEMU_VNC_BINARY', './contrib/qemu-vnc/qemu-vnc')
+    test_deps += [qemu_vnc]
+  endif
 
   qtest_env.set('PYTHON', python.full_path())
 

-- 
2.53.0



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

* Re: [PATCH 53/60] ui/console: return completion status from gfx_update callback
  2026-03-17  8:51 ` [PATCH 53/60] ui/console: return completion status from gfx_update callback Marc-André Lureau
@ 2026-03-17 11:43   ` BALATON Zoltan
  0 siblings, 0 replies; 105+ messages in thread
From: BALATON Zoltan @ 2026-03-17 11:43 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

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

On Tue, 17 Mar 2026, Marc-André Lureau wrote:
> 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>
> ---
> hw/display/qxl.h              |  2 +-
> include/ui/console.h          |  3 +--
> 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/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 +++++-----
> 31 files changed, 121 insertions(+), 85 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 c695b433fe3..2ac9c59e151 100644
> --- a/include/ui/console.h
> +++ b/include/ui/console.h
> @@ -363,8 +363,7 @@ 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() */
> +    bool (*gfx_update)(void *opaque); /* false if deferred update_done */

This short comment (and the commit message) seems to be the only 
documentation. Is it worth documenting better somewhere or at least make 
the comment more descriptive such as "true: graphic_hw_update_done() is 
needed, false: deferred update_done" or something more easily understood 
by somebody not familiar with console internal working.

Also when returning from update functions from an error at the beginning 
without any update is graphic_hw_update_done needed when nothing changed?

Regards,
BALATON Zoltan

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

* Re: [PATCH 54/60] ui/console: rename public API to use consistent qemu_console_ prefix
  2026-03-17  8:51 ` [PATCH 54/60] ui/console: rename public API to use consistent qemu_console_ prefix Marc-André Lureau
@ 2026-03-17 11:46   ` BALATON Zoltan
  0 siblings, 0 replies; 105+ messages in thread
From: BALATON Zoltan @ 2026-03-17 11:46 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

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

On Tue, 17 Mar 2026, Marc-André Lureau wrote:
> Rename the display and graphic console public functions to follow a
> consistent qemu_console_ (or qemu_graphic_console_) naming convention.
>
> The previous API used a mix of prefixes: dpy_, graphic_hw_,
> graphic_console_, console_has_, and update_displaychangelistener().
> Unify them under a common qemu_console_ namespace for better
> discoverability and consistency.
>
> The main renames are:
> - dpy_gfx_*() / dpy_text_*() / dpy_gl_*() → qemu_console_*()
> - dpy_{get,set}_ui_info() → qemu_console_{get,set}_ui_info()
> - graphic_hw_*() → qemu_console_hw_*()
> - graphic_console_*() → qemu_graphic_console_*()
> - console_has_gl() → qemu_console_has_gl()
> - update_displaychangelistener() → qemu_console_listener_set_refresh()

Is the qemu_ prefix really needed here or if so would something shorter 
like qcon_ do instead of qemu_console_? With short line limits long names 
cause a lot of line wraps. I think just console_ as already used by some 
of these already would do and be less churn but no strong opinion on any 
of these.

Regards,
BALATON Zoltan

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

* Re: [PATCH 58/60] tests/qtest: fix dbus-vmstate-test compilation
  2026-03-17  8:51 ` [PATCH 58/60] tests/qtest: fix dbus-vmstate-test compilation Marc-André Lureau
@ 2026-03-17 12:28   ` Fabiano Rosas
  2026-03-17 12:39     ` Marc-André Lureau
  0 siblings, 1 reply; 105+ messages in thread
From: Fabiano Rosas @ 2026-03-17 12:28 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel

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

> The test is still disabled, and trying to compile it will fail now.
>
> Fix it, and remove the G_TEST_DBUS_DAEMON setting, since it is passed by
> meson test. Fwiw, the test passes here.
>

Hi, I have a series re-enabling this test and doing a few fixes, take a
look whether it helps you. I'm a bit short on time right now, but I
could rebase and post it after the freeze.

https://gitlab.com/farosas/qemu/-/commits/migration-test-dbus-vmstate?ref_type=heads

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  tests/qtest/dbus-vmstate-test.c | 11 ++---------
>  tests/qtest/meson.build         |  3 ++-
>  2 files changed, 4 insertions(+), 10 deletions(-)
>
> diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c
> index 6c990864e3e..ed4a52d0255 100644
> --- a/tests/qtest/dbus-vmstate-test.c
> +++ b/tests/qtest/dbus-vmstate-test.c
> @@ -3,7 +3,7 @@
>  #include <gio/gio.h>

I remember some CI job failed due to the test not declaring this
dependency.

>  #include "libqtest.h"
>  #include "dbus-vmstate1.h"
> -#include "migration-helpers.h"
> +#include "migration/migration-qmp.h"
>  
>  static char *workdir;
>  
> @@ -229,7 +229,7 @@ test_dbus_vmstate(Test *test)
>  
>      thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop);
>  
> -    migrate_qmp(src_qemu, uri, "{}");
> +    migrate_qmp(src_qemu, dst_qemu, uri, NULL, "{}");

This works, however I have patches queued for the hardfreeze that change
some details in the migration tests infra:

- dst cmdline should use -incoming defer
- migrate_incoming_qmp needs to be called
- no more setting: qtest_set_expected_status(dst_qemu, EXIT_FAILURE);

>      test->src_qemu = src_qemu;
>      if (test->migrate_fail) {
>          wait_for_migration_fail(src_qemu, true);
> @@ -342,15 +342,8 @@ int
>  main(int argc, char **argv)
>  {
>      GError *err = NULL;
> -    g_autofree char *dbus_daemon = NULL;
>      int ret;
>  
> -    dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR),
> -                                   "tests",
> -                                   "dbus-vmstate-daemon.sh",
> -                                   NULL);
> -    g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true);
> -
>      g_test_init(&argc, &argv, NULL);
>  
>      workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err);
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index b735f55fc40..5f8cff172c8 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -130,7 +130,8 @@ if dbus_daemon.found() and gdbus_codegen.found()
>                                  input: meson.project_source_root() / 'backends/dbus-vmstate1.xml',
>                                  command: [gdbus_codegen, '@INPUT@',
>                                            '--interface-prefix', 'org.qemu',
> -                                          '--generate-c-code', '@BASENAME@']).to_list()
> +                                          '--generate-c-code', '@BASENAME@',
> +                                          '--output-directory', meson.current_build_dir()]).to_list()
>  else
>    dbus_vmstate1 = []
>  endif


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

* Re: [PATCH 58/60] tests/qtest: fix dbus-vmstate-test compilation
  2026-03-17 12:28   ` Fabiano Rosas
@ 2026-03-17 12:39     ` Marc-André Lureau
  0 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-17 12:39 UTC (permalink / raw)
  To: Fabiano Rosas; +Cc: qemu-devel

Hi

On Tue, Mar 17, 2026 at 4:29 PM Fabiano Rosas <farosas@suse.de> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > The test is still disabled, and trying to compile it will fail now.
> >
> > Fix it, and remove the G_TEST_DBUS_DAEMON setting, since it is passed by
> > meson test. Fwiw, the test passes here.
> >
>
> Hi, I have a series re-enabling this test and doing a few fixes, take a
> look whether it helps you. I'm a bit short on time right now, but I
> could rebase and post it after the freeze.
>
> https://gitlab.com/farosas/qemu/-/commits/migration-test-dbus-vmstate?ref_type=heads

Ok, let's get your series merged first. This can indeed wait for the
freeze period, but feel free to send it and ping me!

thanks

>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  tests/qtest/dbus-vmstate-test.c | 11 ++---------
> >  tests/qtest/meson.build         |  3 ++-
> >  2 files changed, 4 insertions(+), 10 deletions(-)
> >
> > diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c
> > index 6c990864e3e..ed4a52d0255 100644
> > --- a/tests/qtest/dbus-vmstate-test.c
> > +++ b/tests/qtest/dbus-vmstate-test.c
> > @@ -3,7 +3,7 @@
> >  #include <gio/gio.h>
>
> I remember some CI job failed due to the test not declaring this
> dependency.
>
> >  #include "libqtest.h"
> >  #include "dbus-vmstate1.h"
> > -#include "migration-helpers.h"
> > +#include "migration/migration-qmp.h"
> >
> >  static char *workdir;
> >
> > @@ -229,7 +229,7 @@ test_dbus_vmstate(Test *test)
> >
> >      thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop);
> >
> > -    migrate_qmp(src_qemu, uri, "{}");
> > +    migrate_qmp(src_qemu, dst_qemu, uri, NULL, "{}");
>
> This works, however I have patches queued for the hardfreeze that change
> some details in the migration tests infra:
>
> - dst cmdline should use -incoming defer
> - migrate_incoming_qmp needs to be called
> - no more setting: qtest_set_expected_status(dst_qemu, EXIT_FAILURE);
>
> >      test->src_qemu = src_qemu;
> >      if (test->migrate_fail) {
> >          wait_for_migration_fail(src_qemu, true);
> > @@ -342,15 +342,8 @@ int
> >  main(int argc, char **argv)
> >  {
> >      GError *err = NULL;
> > -    g_autofree char *dbus_daemon = NULL;
> >      int ret;
> >
> > -    dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR),
> > -                                   "tests",
> > -                                   "dbus-vmstate-daemon.sh",
> > -                                   NULL);
> > -    g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true);
> > -
> >      g_test_init(&argc, &argv, NULL);
> >
> >      workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err);
> > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> > index b735f55fc40..5f8cff172c8 100644
> > --- a/tests/qtest/meson.build
> > +++ b/tests/qtest/meson.build
> > @@ -130,7 +130,8 @@ if dbus_daemon.found() and gdbus_codegen.found()
> >                                  input: meson.project_source_root() / 'backends/dbus-vmstate1.xml',
> >                                  command: [gdbus_codegen, '@INPUT@',
> >                                            '--interface-prefix', 'org.qemu',
> > -                                          '--generate-c-code', '@BASENAME@']).to_list()
> > +                                          '--generate-c-code', '@BASENAME@',
> > +                                          '--output-directory', meson.current_build_dir()]).to_list()
> >  else
> >    dbus_vmstate1 = []
> >  endif
>


-- 
Marc-André Lureau


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

* Re: [PATCH 01/60] ui/vnc-jobs: fix VncRectEntry leak on job cleanup
  2026-03-17  8:50 ` [PATCH 01/60] ui/vnc-jobs: fix VncRectEntry leak on job cleanup Marc-André Lureau
@ 2026-03-24 13:43   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 13:43 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:15PM +0400, Marc-André Lureau wrote:
> When a VncJob is freed, its associated VncRectEntry list must also be
> freed. Previously, vnc_job_push() and the disconnected path in
> vnc_worker_thread_loop() called g_free(job) directly, leaking all
> VncRectEntry allocations.
> 
> Introduce vnc_job_free() which iterates and frees the rectangle entries
> before freeing the job itself, and use it in both paths.
> 
> Also add QLIST_REMOVE() in the worker loop before g_free(entry), so
> that entries processed during normal operation are properly unlinked.
> Without this, vnc_job_free() would iterate dangling pointers to
> already-freed entries, causing use-after-free.
> 
> Fixes: bd023f953e5e ("vnc: threaded VNC server")

... Oppps ...  Wed Jul 7 2010 


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

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 02/60] ui/vnc-jobs: clear source tag
  2026-03-17  8:50 ` [PATCH 02/60] ui/vnc-jobs: clear source tag Marc-André Lureau
@ 2026-03-24 13:44   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 13:44 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:16PM +0400, Marc-André Lureau wrote:
> Avoid potentially removing a dangling source & simplify code.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc-jobs.c |  4 +---
>  ui/vnc.c      | 31 +++++++------------------------
>  2 files changed, 8 insertions(+), 27 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 03/60] ui/vnc-jobs: remove needless buffer_reset() before end
  2026-03-17  8:50 ` [PATCH 03/60] ui/vnc-jobs: remove needless buffer_reset() before end Marc-André Lureau
@ 2026-03-24 13:45   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 13:45 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:17PM +0400, Marc-André Lureau wrote:
> vnc_async_encoding_end() does buffer_free() next.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc-jobs.c | 1 -
>  1 file changed, 1 deletion(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 04/60] ui/vnc: clarify intent using buffer_empty() function
  2026-03-17  8:50 ` [PATCH 04/60] ui/vnc: clarify intent using buffer_empty() function Marc-André Lureau
@ 2026-03-24 13:45   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 13:45 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:18PM +0400, Marc-André Lureau wrote:
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc-jobs.c | 2 +-
>  ui/vnc.c      | 2 +-
>  2 files changed, 2 insertions(+), 2 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 05/60] ui/vnc-jobs: vnc_has_job_locked() argument cannot be NULL
  2026-03-17  8:50 ` [PATCH 05/60] ui/vnc-jobs: vnc_has_job_locked() argument cannot be NULL Marc-André Lureau
@ 2026-03-24 13:46   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 13:46 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:19PM +0400, Marc-André Lureau wrote:
> The only caller, vnc_jobs_join() cannot take vs == NULL argument, or it
> would later crash.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc-jobs.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 06/60] ui/vnc-jobs: remove dead VncJobQueue.exit
  2026-03-17  8:50 ` [PATCH 06/60] ui/vnc-jobs: remove dead VncJobQueue.exit Marc-André Lureau
@ 2026-03-24 13:49   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 13:49 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:20PM +0400, Marc-André Lureau wrote:
> Since commit 09526058d0a5 ("ui/vnc: Remove vnc_stop_worker_thread()"),
> it's not used anymore. It seems stopping worker thread hasn't been
> supported ever.

Yeah seems vnc_stop_worker_thread was dead code since the first
commit in bd023f953e5e51237726af04300e4637fe203774.

Not a problem, as long as we do NOT have the ability to dynamically
add/remove VNC  display backends via QMP. If we get get that then
I wouldn't trust 16 year old code for stopping the worker that has
never been exercised !

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc-jobs.c | 10 ++--------
>  1 file changed, 2 insertions(+), 8 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 07/60] ui/vnc-jobs: remove vnc_queue_clear()
  2026-03-17  8:50 ` [PATCH 07/60] ui/vnc-jobs: remove vnc_queue_clear() Marc-André Lureau
@ 2026-03-24 13:51   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 13:51 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:21PM +0400, Marc-André Lureau wrote:
> The function was never called, since the worker thread was never
> exiting. Also it was incomplete (not clearing pending job list) and
> mixing global queue and argument. Let's remove it.
> 
> Note: maybe the worker thread could be torn down when vnc_jobs_join()
> realizes there is no job left.

Dynamically starting and stopping the worker thread would mean
having to think about possible race conditions. Simpler to ignore
that possibility IMHO.

> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc-jobs.c | 10 +---------
>  1 file changed, 1 insertion(+), 9 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 08/60] ui/vnc-jobs: narrow taking the lock when pushing empty jobs
  2026-03-17  8:50 ` [PATCH 08/60] ui/vnc-jobs: narrow taking the lock when pushing empty jobs Marc-André Lureau
@ 2026-03-24 13:53   ` Daniel P. Berrangé
  2026-03-24 14:04     ` Marc-André Lureau
  0 siblings, 1 reply; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 13:53 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:22PM +0400, Marc-André Lureau wrote:
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc-jobs.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
> index 28a4738f1ec..ae925171fae 100644
> --- a/ui/vnc-jobs.c
> +++ b/ui/vnc-jobs.c
> @@ -122,14 +122,14 @@ static void vnc_job_free(VncJob *job)
>  
>  void vnc_job_push(VncJob *job)
>  {
> -    vnc_lock_queue(queue);
>      if (QLIST_EMPTY(&job->rectangles)) {

All the APIs which write to job->rectangles do that in a block
that is protected by vnc_lock_queue, so narrowing this to allow
lock-less access of job->rectangles feels dubious, without some
justification in the commit message to explain why it is safe
for job->rectangles to be unprotected, while 'queue->jobs' still
needs protection.

>          vnc_job_free(job);
>      } else {
> +        vnc_lock_queue(queue);
>          QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
>          qemu_cond_broadcast(&queue->cond);
> +        vnc_unlock_queue(queue);
>      }
> -    vnc_unlock_queue(queue);
>  }
>  
>  static bool vnc_has_job_locked(VncState *vs)
> 
> -- 
> 2.53.0
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 09/60] ui/vnc-jobs: drop redundant (and needless) qemu_thread_get_self()
  2026-03-17  8:50 ` [PATCH 09/60] ui/vnc-jobs: drop redundant (and needless) qemu_thread_get_self() Marc-André Lureau
@ 2026-03-24 14:00   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:00 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:23PM +0400, Marc-André Lureau wrote:
> The call is unnecessary, since "thread" is already set at creation time.
> Furthermore, the "thread" field is mostly useless as the thread is
> created DETACHED and isn't used for anything but perhaps debugging.

Doesn't even seem to be used for debugging - nothing ever reads it
afaict.

> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc-jobs.c | 2 --
>  1 file changed, 2 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 10/60] ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen)
  2026-03-17  8:50 ` [PATCH 10/60] ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen) Marc-André Lureau
@ 2026-03-24 14:03   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:03 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:24PM +0400, Marc-André Lureau wrote:
> The loop condition used `y <= s->height` instead of `y < s->height`,
> causing vc_clear_xy() to be called with y == s->height. This clears
> a row in the scrollback buffer beyond the visible screen.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/console-vc.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 08/60] ui/vnc-jobs: narrow taking the lock when pushing empty jobs
  2026-03-24 13:53   ` Daniel P. Berrangé
@ 2026-03-24 14:04     ` Marc-André Lureau
  0 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-24 14:04 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel

Hi

On Tue, Mar 24, 2026 at 5:54 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Tue, Mar 17, 2026 at 12:50:22PM +0400, Marc-André Lureau wrote:
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  ui/vnc-jobs.c | 4 ++--
> >  1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
> > index 28a4738f1ec..ae925171fae 100644
> > --- a/ui/vnc-jobs.c
> > +++ b/ui/vnc-jobs.c
> > @@ -122,14 +122,14 @@ static void vnc_job_free(VncJob *job)
> >
> >  void vnc_job_push(VncJob *job)
> >  {
> > -    vnc_lock_queue(queue);
> >      if (QLIST_EMPTY(&job->rectangles)) {
>
> All the APIs which write to job->rectangles do that in a block
> that is protected by vnc_lock_queue, so narrowing this to allow
> lock-less access of job->rectangles feels dubious, without some
> justification in the commit message to explain why it is safe
> for job->rectangles to be unprotected, while 'queue->jobs' still
> needs protection.

The caller is vnc_update_client(). It creates the job and push damaged
rect. It shouldn't take the lock either. Only if it modified a queued
job it would need the lock, but it doesn't. I'll update the patch.

>
> >          vnc_job_free(job);
> >      } else {
> > +        vnc_lock_queue(queue);
> >          QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
> >          qemu_cond_broadcast(&queue->cond);
> > +        vnc_unlock_queue(queue);
> >      }
> > -    vnc_unlock_queue(queue);
> >  }
> >
> >  static bool vnc_has_job_locked(VncState *vs)
> >
> > --
> > 2.53.0
> >
> >
>
> With regards,
> Daniel
> --
> |: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
> |: https://libvirt.org          ~~          https://entangle-photo.org :|
> |: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|
>
>


-- 
Marc-André Lureau


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

* Re: [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering
  2026-03-17  8:50 ` [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering Marc-André Lureau
@ 2026-03-24 14:07   ` Daniel P. Berrangé
  2026-03-24 14:17     ` Marc-André Lureau
  2026-03-25  5:35   ` Markus Armbruster
  1 sibling, 1 reply; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:07 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:25PM +0400, Marc-André Lureau wrote:
> The text console receives bytes that may be UTF-8 encoded (e.g. from
> a guest running a modern distro), but currently treats each byte as a
> raw character index into the VGA/CP437 font, producing garbled output
> for any multi-byte sequence.
> 
> Add a proper UTF-8 decoder using Bjoern Hoehrmann's DFA.
> The DFA inherently rejects overlong encodings, surrogates, and
> codepoints above U+10FFFF.  Completed codepoints are then mapped to
> CP437, unmappable characters are displayed as '?'.

I'm surprised we can't do a charset conversion using GLib APIs ?

Do the g_convert family of  APIs (which IIUC wrap the distro iconv)
not do what we would want ? If not, would direct use of iconv not
be an alternative ?

It feels pretty wrong to need to embed UTF8 decoding code in
QEMU

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/cp437.h      |  13 ++++
>  ui/console-vc.c |  62 +++++++++++++++++
>  ui/cp437.c      | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  ui/meson.build  |   2 +-
>  4 files changed, 281 insertions(+), 1 deletion(-)
> 
> diff --git a/ui/cp437.h b/ui/cp437.h
> new file mode 100644
> index 00000000000..81ace8317c7
> --- /dev/null
> +++ b/ui/cp437.h
> @@ -0,0 +1,13 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Copyright (c) QEMU contributors
> + */
> +#ifndef QEMU_CP437_H
> +#define QEMU_CP437_H
> +
> +#include <stdint.h>
> +
> +int unicode_to_cp437(uint32_t codepoint);
> +
> +#endif /* QEMU_CP437_H */
> diff --git a/ui/console-vc.c b/ui/console-vc.c
> index 8dee1f9bd01..7bbd65dea27 100644
> --- a/ui/console-vc.c
> +++ b/ui/console-vc.c
> @@ -9,6 +9,7 @@
>  #include "qemu/fifo8.h"
>  #include "qemu/option.h"
>  #include "ui/console.h"
> +#include "ui/cp437.h"
>  
>  #include "trace.h"
>  #include "console-priv.h"
> @@ -89,6 +90,8 @@ struct VCChardev {
>      enum TTYState state;
>      int esc_params[MAX_ESC_PARAMS];
>      int nb_esc_params;
> +    uint32_t utf8_state;     /* UTF-8 DFA decoder state */
> +    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
>      TextAttributes t_attrib; /* currently active text attributes */
>      TextAttributes t_attrib_saved;
>      int x_saved, y_saved;
> @@ -598,6 +601,47 @@ static void vc_clear_xy(VCChardev *vc, int x, int y)
>      vc_update_xy(vc, x, y);
>  }
>  
> +/*
> + * UTF-8 DFA decoder by Bjoern Hoehrmann.
> + * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
> + * See https://github.com/polijan/utf8_decode for details.
> + *
> + * SPDX-License-Identifier: MIT
> + */
> +#define UTF8_ACCEPT 0
> +#define UTF8_REJECT 12
> +
> +static const uint8_t utf8d[] = {
> +    /* character class lookup */
> +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> +    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
> +    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
> +    8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
> +   10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
> +
> +    /* state transition lookup */
> +     0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
> +    12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
> +    12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
> +    12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
> +    12,36,12,12,12,12,12,12,12,12,12,12,
> +};
> +
> +static uint32_t utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
> +{
> +    uint32_t type = utf8d[byte];
> +
> +    *codep = (*state != UTF8_ACCEPT) ?
> +        (byte & 0x3fu) | (*codep << 6) :
> +        (0xffu >> type) & (byte);
> +
> +    *state = utf8d[256 + *state + type];
> +    return *state;
> +}
> +
>  static void vc_put_one(VCChardev *vc, int ch)
>  {
>      QemuTextConsole *s = vc->console;
> @@ -761,6 +805,24 @@ static void vc_putchar(VCChardev *vc, int ch)
>  
>      switch(vc->state) {
>      case TTY_STATE_NORM:
> +        /* Feed byte through the UTF-8 DFA decoder */
> +        if (ch >= 0x80) {
> +            switch (utf8_decode(&vc->utf8_state, &vc->utf8_codepoint, ch)) {
> +            case UTF8_ACCEPT:
> +                vc_put_one(vc, unicode_to_cp437(vc->utf8_codepoint));
> +                break;
> +            case UTF8_REJECT:
> +                /* Reset state so the decoder can resync */
> +                vc->utf8_state = UTF8_ACCEPT;
> +                break;
> +            default:
> +                /* Need more bytes */
> +                break;
> +            }
> +            break;
> +        }
> +        /* ASCII byte: abort any pending UTF-8 sequence */
> +        vc->utf8_state = UTF8_ACCEPT;
>          switch(ch) {
>          case '\r':  /* carriage return */
>              s->x = 0;
> diff --git a/ui/cp437.c b/ui/cp437.c
> new file mode 100644
> index 00000000000..8ec38b73419
> --- /dev/null
> +++ b/ui/cp437.c
> @@ -0,0 +1,205 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Copyright (c) QEMU contributors
> + */
> +#include "qemu/osdep.h"
> +#include "cp437.h"
> +
> +/*
> + * Unicode to CP437 page tables.
> + *
> + * Borrowed from the Linux kernel (fs/nls/nls_cp437.c, "Dual BSD/GPL"),
> + * generated from the Unicode Organization tables (www.unicode.org).
> + */
> +static const unsigned char uni2cp437_page00[256] = {
> +    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
> +    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
> +    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
> +    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
> +    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
> +    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
> +    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
> +    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
> +    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
> +    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
> +    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
> +    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
> +    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
> +    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
> +    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
> +    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
> +
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
> +    0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, /* 0xa0-0xa7 */
> +    0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
> +    0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, /* 0xb0-0xb7 */
> +    0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8, /* 0xb8-0xbf */
> +    0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
> +    0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
> +    0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, /* 0xd0-0xd7 */
> +    0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
> +    0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
> +    0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, /* 0xe8-0xef */
> +    0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, /* 0xf0-0xf7 */
> +    0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98, /* 0xf8-0xff */
> +};
> +
> +static const unsigned char uni2cp437_page01[256] = {
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
> +
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> +    0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> +};
> +
> +static const unsigned char uni2cp437_page03[256] = {
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
> +
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> +    0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> +    0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
> +    0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
> +    0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
> +    0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
> +    0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
> +};
> +
> +static const unsigned char uni2cp437_page20[256] = {
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
> +
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xa0-0xa7 */
> +};
> +
> +static const unsigned char uni2cp437_page22[256] = {
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> +    0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> +    0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> +    0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
> +    0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
> +};
> +
> +static const unsigned char uni2cp437_page23[256] = {
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> +    0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> +    0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> +};
> +
> +static const unsigned char uni2cp437_page25[256] = {
> +    0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> +    0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> +    0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> +    0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> +    0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> +    0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> +    0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> +    0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> +    0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
> +    0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
> +    0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
> +    0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
> +
> +    0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> +    0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> +    0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
> +    0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
> +};
> +
> +static const unsigned char *const uni2cp437_page[256] = {
> +    [0x00] = uni2cp437_page00, [0x01] = uni2cp437_page01,
> +    [0x03] = uni2cp437_page03, [0x20] = uni2cp437_page20,
> +    [0x22] = uni2cp437_page22, [0x23] = uni2cp437_page23,
> +    [0x25] = uni2cp437_page25,
> +};
> +
> +/*
> + * Convert a Unicode code point to its CP437 equivalent for
> + * rendering with the VGA font.
> + * Returns '?' for characters that cannot be mapped.
> + */
> +int unicode_to_cp437(uint32_t codepoint)
> +{
> +    const unsigned char *page;
> +    unsigned char hi = (codepoint >> 8) & 0xff;
> +    unsigned char lo = codepoint & 0xff;
> +
> +    if (codepoint > 0xffff) {
> +        return '?';
> +    }
> +
> +    page = uni2cp437_page[hi];
> +    if (page && page[lo]) {
> +        return page[lo];
> +    }
> +
> +    return '?';
> +}
> diff --git a/ui/meson.build b/ui/meson.build
> index 69404bca71a..d4d9312b98c 100644
> --- a/ui/meson.build
> +++ b/ui/meson.build
> @@ -16,7 +16,7 @@ system_ss.add(files(
>    'ui-qmp-cmds.c',
>    'util.c',
>  ))
> -system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c'))
> +system_ss.add(when: pixman, if_true: files('console-vc.c', 'cp437.c'), if_false: files('console-vc-stubs.c'))
>  if dbus_display
>    system_ss.add(files('dbus-module.c'))
>  endif
> 
> -- 
> 2.53.0
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 13/60] ui/console-vc: fix comment shift-out/in comments
  2026-03-17  8:50 ` [PATCH 13/60] ui/console-vc: fix comment shift-out/in comments Marc-André Lureau
@ 2026-03-24 14:11   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:11 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:27PM +0400, Marc-André Lureau wrote:
> 14 is shift-out
> 15 is shift-in

Yep, checked at  https://www.ascii-code.com/

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

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

> 
> diff --git a/ui/console-vc.c b/ui/console-vc.c
> index 5eca9a5c004..fae8c8ce768 100644
> --- a/ui/console-vc.c
> +++ b/ui/console-vc.c
> @@ -847,10 +847,10 @@ static void vc_putchar(VCChardev *vc, int ch)
>              /* TODO: has to be implemented */
>              break;
>          case 14:
> -            /* SI (shift in), character set 0 (ignored) */
> +            /* SO (shift out), character set 1 (ignored) */
>              break;
>          case 15:
> -            /* SO (shift out), character set 1 (ignored) */
> +            /* SI (shift in), character set 0 (ignored) */
>              break;
>          case 27:    /* esc (introducing an escape sequence) */
>              vc->state = TTY_STATE_ESC;
> 
> -- 
> 2.53.0
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 14/60] ui/console: dispatch get_label() through QOM virtual method
  2026-03-17  8:50 ` [PATCH 14/60] ui/console: dispatch get_label() through QOM virtual method Marc-André Lureau
@ 2026-03-24 14:14   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:14 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:28PM +0400, Marc-André Lureau wrote:
> Replace the type-checking chain in qemu_console_get_label() (using
> QEMU_IS_GRAPHIC_CONSOLE/QEMU_IS_TEXT_CONSOLE) with a QemuConsoleClass
> virtual method, allowing each console subclass to provide its own
> get_label implementation.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/ui/console.h  |   2 +
>  ui/console-priv.h     |   1 -
>  ui/console-vc-stubs.c |   6 ---
>  ui/console-vc.c       |  12 ++++--
>  ui/console.c          | 101 +++++++++++++++++++++++++++-----------------------
>  5 files changed, 65 insertions(+), 57 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering
  2026-03-24 14:07   ` Daniel P. Berrangé
@ 2026-03-24 14:17     ` Marc-André Lureau
  2026-03-24 15:42       ` Daniel P. Berrangé
  0 siblings, 1 reply; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-24 14:17 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel

Hi

On Tue, Mar 24, 2026 at 6:08 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Tue, Mar 17, 2026 at 12:50:25PM +0400, Marc-André Lureau wrote:
> > The text console receives bytes that may be UTF-8 encoded (e.g. from
> > a guest running a modern distro), but currently treats each byte as a
> > raw character index into the VGA/CP437 font, producing garbled output
> > for any multi-byte sequence.
> >
> > Add a proper UTF-8 decoder using Bjoern Hoehrmann's DFA.
> > The DFA inherently rejects overlong encodings, surrogates, and
> > codepoints above U+10FFFF.  Completed codepoints are then mapped to
> > CP437, unmappable characters are displayed as '?'.
>
> I'm surprised we can't do a charset conversion using GLib APIs ?
>
> Do the g_convert family of  APIs (which IIUC wrap the distro iconv)
> not do what we would want ? If not, would direct use of iconv not
> be an alternative ?
>

I tried to use GIconv but ran into a number of issues, as it doesn't
operate on character level, but strings. And it uses allocation etc. I
didn't manage with iconv either.

> It feels pretty wrong to need to embed UTF8 decoding code in
> QEMU

Yes, but on a standalone qemu-vnc server, is it more acceptable?

>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  ui/cp437.h      |  13 ++++
> >  ui/console-vc.c |  62 +++++++++++++++++
> >  ui/cp437.c      | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  ui/meson.build  |   2 +-
> >  4 files changed, 281 insertions(+), 1 deletion(-)
> >
> > diff --git a/ui/cp437.h b/ui/cp437.h
> > new file mode 100644
> > index 00000000000..81ace8317c7
> > --- /dev/null
> > +++ b/ui/cp437.h
> > @@ -0,0 +1,13 @@
> > +/*
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * Copyright (c) QEMU contributors
> > + */
> > +#ifndef QEMU_CP437_H
> > +#define QEMU_CP437_H
> > +
> > +#include <stdint.h>
> > +
> > +int unicode_to_cp437(uint32_t codepoint);
> > +
> > +#endif /* QEMU_CP437_H */
> > diff --git a/ui/console-vc.c b/ui/console-vc.c
> > index 8dee1f9bd01..7bbd65dea27 100644
> > --- a/ui/console-vc.c
> > +++ b/ui/console-vc.c
> > @@ -9,6 +9,7 @@
> >  #include "qemu/fifo8.h"
> >  #include "qemu/option.h"
> >  #include "ui/console.h"
> > +#include "ui/cp437.h"
> >
> >  #include "trace.h"
> >  #include "console-priv.h"
> > @@ -89,6 +90,8 @@ struct VCChardev {
> >      enum TTYState state;
> >      int esc_params[MAX_ESC_PARAMS];
> >      int nb_esc_params;
> > +    uint32_t utf8_state;     /* UTF-8 DFA decoder state */
> > +    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
> >      TextAttributes t_attrib; /* currently active text attributes */
> >      TextAttributes t_attrib_saved;
> >      int x_saved, y_saved;
> > @@ -598,6 +601,47 @@ static void vc_clear_xy(VCChardev *vc, int x, int y)
> >      vc_update_xy(vc, x, y);
> >  }
> >
> > +/*
> > + * UTF-8 DFA decoder by Bjoern Hoehrmann.
> > + * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
> > + * See https://github.com/polijan/utf8_decode for details.
> > + *
> > + * SPDX-License-Identifier: MIT
> > + */
> > +#define UTF8_ACCEPT 0
> > +#define UTF8_REJECT 12
> > +
> > +static const uint8_t utf8d[] = {
> > +    /* character class lookup */
> > +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> > +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> > +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> > +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> > +    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
> > +    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
> > +    8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
> > +   10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
> > +
> > +    /* state transition lookup */
> > +     0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
> > +    12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
> > +    12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
> > +    12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
> > +    12,36,12,12,12,12,12,12,12,12,12,12,
> > +};
> > +
> > +static uint32_t utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
> > +{
> > +    uint32_t type = utf8d[byte];
> > +
> > +    *codep = (*state != UTF8_ACCEPT) ?
> > +        (byte & 0x3fu) | (*codep << 6) :
> > +        (0xffu >> type) & (byte);
> > +
> > +    *state = utf8d[256 + *state + type];
> > +    return *state;
> > +}
> > +
> >  static void vc_put_one(VCChardev *vc, int ch)
> >  {
> >      QemuTextConsole *s = vc->console;
> > @@ -761,6 +805,24 @@ static void vc_putchar(VCChardev *vc, int ch)
> >
> >      switch(vc->state) {
> >      case TTY_STATE_NORM:
> > +        /* Feed byte through the UTF-8 DFA decoder */
> > +        if (ch >= 0x80) {
> > +            switch (utf8_decode(&vc->utf8_state, &vc->utf8_codepoint, ch)) {
> > +            case UTF8_ACCEPT:
> > +                vc_put_one(vc, unicode_to_cp437(vc->utf8_codepoint));
> > +                break;
> > +            case UTF8_REJECT:
> > +                /* Reset state so the decoder can resync */
> > +                vc->utf8_state = UTF8_ACCEPT;
> > +                break;
> > +            default:
> > +                /* Need more bytes */
> > +                break;
> > +            }
> > +            break;
> > +        }
> > +        /* ASCII byte: abort any pending UTF-8 sequence */
> > +        vc->utf8_state = UTF8_ACCEPT;
> >          switch(ch) {
> >          case '\r':  /* carriage return */
> >              s->x = 0;
> > diff --git a/ui/cp437.c b/ui/cp437.c
> > new file mode 100644
> > index 00000000000..8ec38b73419
> > --- /dev/null
> > +++ b/ui/cp437.c
> > @@ -0,0 +1,205 @@
> > +/*
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * Copyright (c) QEMU contributors
> > + */
> > +#include "qemu/osdep.h"
> > +#include "cp437.h"
> > +
> > +/*
> > + * Unicode to CP437 page tables.
> > + *
> > + * Borrowed from the Linux kernel (fs/nls/nls_cp437.c, "Dual BSD/GPL"),
> > + * generated from the Unicode Organization tables (www.unicode.org).
> > + */
> > +static const unsigned char uni2cp437_page00[256] = {
> > +    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
> > +    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
> > +    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
> > +    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
> > +    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
> > +    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
> > +    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
> > +    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
> > +    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
> > +    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
> > +    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
> > +    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
> > +    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
> > +    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
> > +    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
> > +    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
> > +
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
> > +    0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, /* 0xa0-0xa7 */
> > +    0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
> > +    0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, /* 0xb0-0xb7 */
> > +    0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8, /* 0xb8-0xbf */
> > +    0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
> > +    0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
> > +    0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, /* 0xd0-0xd7 */
> > +    0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
> > +    0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
> > +    0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, /* 0xe8-0xef */
> > +    0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, /* 0xf0-0xf7 */
> > +    0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98, /* 0xf8-0xff */
> > +};
> > +
> > +static const unsigned char uni2cp437_page01[256] = {
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
> > +
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> > +    0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> > +};
> > +
> > +static const unsigned char uni2cp437_page03[256] = {
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
> > +
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> > +    0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> > +    0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
> > +    0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
> > +    0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
> > +    0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
> > +    0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
> > +};
> > +
> > +static const unsigned char uni2cp437_page20[256] = {
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
> > +
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xa0-0xa7 */
> > +};
> > +
> > +static const unsigned char uni2cp437_page22[256] = {
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> > +    0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> > +    0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> > +    0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
> > +    0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
> > +};
> > +
> > +static const unsigned char uni2cp437_page23[256] = {
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> > +    0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> > +    0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> > +};
> > +
> > +static const unsigned char uni2cp437_page25[256] = {
> > +    0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
> > +    0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
> > +    0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
> > +    0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
> > +    0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
> > +    0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
> > +    0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
> > +    0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
> > +    0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
> > +    0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
> > +    0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
> > +    0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
> > +
> > +    0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
> > +    0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
> > +    0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
> > +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
> > +    0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
> > +};
> > +
> > +static const unsigned char *const uni2cp437_page[256] = {
> > +    [0x00] = uni2cp437_page00, [0x01] = uni2cp437_page01,
> > +    [0x03] = uni2cp437_page03, [0x20] = uni2cp437_page20,
> > +    [0x22] = uni2cp437_page22, [0x23] = uni2cp437_page23,
> > +    [0x25] = uni2cp437_page25,
> > +};
> > +
> > +/*
> > + * Convert a Unicode code point to its CP437 equivalent for
> > + * rendering with the VGA font.
> > + * Returns '?' for characters that cannot be mapped.
> > + */
> > +int unicode_to_cp437(uint32_t codepoint)
> > +{
> > +    const unsigned char *page;
> > +    unsigned char hi = (codepoint >> 8) & 0xff;
> > +    unsigned char lo = codepoint & 0xff;
> > +
> > +    if (codepoint > 0xffff) {
> > +        return '?';
> > +    }
> > +
> > +    page = uni2cp437_page[hi];
> > +    if (page && page[lo]) {
> > +        return page[lo];
> > +    }
> > +
> > +    return '?';
> > +}
> > diff --git a/ui/meson.build b/ui/meson.build
> > index 69404bca71a..d4d9312b98c 100644
> > --- a/ui/meson.build
> > +++ b/ui/meson.build
> > @@ -16,7 +16,7 @@ system_ss.add(files(
> >    'ui-qmp-cmds.c',
> >    'util.c',
> >  ))
> > -system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c'))
> > +system_ss.add(when: pixman, if_true: files('console-vc.c', 'cp437.c'), if_false: files('console-vc-stubs.c'))
> >  if dbus_display
> >    system_ss.add(files('dbus-module.c'))
> >  endif
> >
> > --
> > 2.53.0
> >
> >
>
> With regards,
> Daniel
> --
> |: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
> |: https://libvirt.org          ~~          https://entangle-photo.org :|
> |: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|
>
>


-- 
Marc-André Lureau


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

* Re: [PATCH 32/60] ui: avoid duplicating vgafont16 in each translation unit
  2026-03-17  8:50 ` [PATCH 32/60] ui: avoid duplicating vgafont16 in each translation unit Marc-André Lureau
@ 2026-03-24 14:22   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:22 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:46PM +0400, Marc-André Lureau wrote:
> vgafont.h defined vgafont16 as a static const array, so every .c file
> that included it (via console-priv.h) got its own 4 KiB copy, that
> the linker may or not deduplicate?
> 
> Move the array definition into a new vgafont.c compilation unit and
> turn the header into a proper extern declaration with an include guard.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vgafont.h   | 4613 +-------------------------------------------------------
>  ui/vgafont.c   | 4613 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  ui/meson.build |    1 +
>  3 files changed, 4619 insertions(+), 4608 deletions(-)

> 
> diff --git a/ui/vgafont.h b/ui/vgafont.h
> index 7e1fc473f75..4498ac4e07b 100644
> --- a/ui/vgafont.h
> +++ b/ui/vgafont.h
> @@ -1,4611 +1,8 @@
> -static const uint8_t vgafont16[256 * 16] = {
> +#ifndef VGAFONT_H
> +#define VGAFONT_H
>  
> -    /* 0 0x00 '^@' */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> -    0x00, /* 00000000 */
> +#include <stdint.h>

This is pulled in by  osdep.h, which every .c file should
be including. Assuming that's removed, then

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 33/60] ui/vgafont: add SPDX license header
  2026-03-17  8:50 ` [PATCH 33/60] ui/vgafont: add SPDX license header Marc-André Lureau
@ 2026-03-24 14:24   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:24 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:47PM +0400, Marc-André Lureau wrote:
> The vgafont was added without source origin, but it can be traced back
> to Linux kernel, which used
> GPL-2 (https://github.com/mpe/linux-fullhistory/blob/master/lib/fonts/font_8x16.c).
> 
> commit c6f37d0e4feeb264a699eda289d3cc69405100b0
> Author: Fabrice Bellard <fabrice@bellard.org>
> Date:   Wed Jul 14 17:39:50 2004 +0000
> 
>     virtual console
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vgafont.h | 3 +++
>  ui/vgafont.c | 3 +++
>  2 files changed, 6 insertions(+)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 34/60] ui: move FONT_WIDTH/HEIGHT to vgafont.h
  2026-03-17  8:50 ` [PATCH 34/60] ui: move FONT_WIDTH/HEIGHT to vgafont.h Marc-André Lureau
@ 2026-03-24 14:25   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:25 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:48PM +0400, Marc-André Lureau wrote:
> Since those values are related to the VGA font, it make sense to move
> them here.
> 
> 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(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 36/60] util: move datadir.c from system/
  2026-03-17  8:50 ` [PATCH 36/60] util: move datadir.c from system/ Marc-André Lureau
@ 2026-03-24 14:27   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:27 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:50PM +0400, Marc-André Lureau wrote:
> The datadir module provides general-purpose data file lookup
> utilities that are not specific to system emulation. Move it
> to util/ so it can be reused more broadly.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  {system => util}/datadir.c | 0
>  system/meson.build         | 1 -
>  system/trace-events        | 1 -
>  util/meson.build           | 1 +
>  util/trace-events          | 3 +++
>  5 files changed, 4 insertions(+), 2 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 38/60] ui: make qemu_default_pixelformat() static inline
  2026-03-17  8:50 ` [PATCH 38/60] ui: make qemu_default_pixelformat() static inline Marc-André Lureau
@ 2026-03-24 14:28   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:28 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:52PM +0400, Marc-André Lureau wrote:
> 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.
> 
> 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(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 39/60] ui: make unregister_displaychangelistener() skip unregistered
  2026-03-17  8:50 ` [PATCH 39/60] ui: make unregister_displaychangelistener() skip unregistered Marc-André Lureau
@ 2026-03-24 14:28   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:28 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:53PM +0400, Marc-André Lureau wrote:
> If the listener doesn't have associate ds / display state, it is already
> unregistered.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/console.c | 3 +++
>  1 file changed, 3 insertions(+)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 40/60] ui: minor code simplification
  2026-03-17  8:50 ` [PATCH 40/60] ui: minor code simplification Marc-André Lureau
@ 2026-03-24 14:30   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:30 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:54PM +0400, Marc-André Lureau wrote:
> Remove the extra "info" variable and its initialization.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/sdl2.c | 10 +++++-----
>  ui/vnc.c  |  7 ++-----
>  2 files changed, 7 insertions(+), 10 deletions(-)
> 
> diff --git a/ui/sdl2.c b/ui/sdl2.c
> index aaaede56e0e..3ffb8acaff8 100644
> --- a/ui/sdl2.c
> +++ b/ui/sdl2.c
> @@ -600,11 +600,11 @@ 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;
> -            dpy_set_ui_info(scon->dcl.con, &info, true);
> +            dpy_set_ui_info(scon->dcl.con,
> +                &(QemuUIInfo) {
> +                    .width = ev->window.data1,
> +                    .height = ev->window.data2,
> +                }, true);

I find this new code less readable than the original. The compiler
optimizer can remove uneccessary variables in this way.

I would, however, suggest removing the memset and doing

             QemuUIInfo info = { .width = ev->window.data1,
                                 .height = ev->window.data2 };
             dpy_set_ui_info(scon->dcl.con, &info, true);

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 41/60] system: make qemu_del_vm_change_state_handler accept NULL
  2026-03-17  8:50 ` [PATCH 41/60] system: make qemu_del_vm_change_state_handler accept NULL Marc-André Lureau
@ 2026-03-24 14:31   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:31 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:55PM +0400, Marc-André Lureau wrote:
> For convenience.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  system/runstate.c | 3 +++
>  1 file changed, 3 insertions(+)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 42/60] ui/vnc: assert preconditions instead of silently returning
  2026-03-17  8:50 ` [PATCH 42/60] ui/vnc: assert preconditions instead of silently returning Marc-André Lureau
@ 2026-03-24 14:31   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:31 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:56PM +0400, Marc-André Lureau wrote:
> Replace defensive NULL guards with assert() in vnc_display_close()
> and vnc_display_open(). These are internal functions whose callers
> guarantee non-NULL arguments, so a NULL value would indicate a
> programming error rather than a runtime condition.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc.c | 15 ++++-----------
>  1 file changed, 4 insertions(+), 11 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 43/60] ui/vnc: simplify vnc_init_func error handling
  2026-03-17  8:50 ` [PATCH 43/60] ui/vnc: simplify vnc_init_func error handling Marc-André Lureau
@ 2026-03-24 14:38   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:38 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:57PM +0400, Marc-André Lureau wrote:
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc.c | 12 +++++-------
>  1 file changed, 5 insertions(+), 7 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

> 
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 605e5117b7f..af5fb3c1551 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -4318,7 +4318,7 @@ void vnc_parse(const char *str)
>  
>  int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
>  {
> -    Error *local_err = NULL;
> +    ERRP_GUARD();
>      char *id = (char *)qemu_opts_id(opts);
>  
>      if (!id) {
> @@ -4326,14 +4326,12 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
>          id = vnc_auto_assign_id(opts);
>      }
>  
> -    vnc_display_init(id, &local_err);
> -    if (local_err) {
> -        error_propagate(errp, local_err);
> +    vnc_display_init(id, errp);
> +    if (*errp) {
>          return -1;
>      }
> -    vnc_display_open(id, &local_err);
> -    if (local_err != NULL) {
> -        error_propagate(errp, local_err);
> +    vnc_display_open(id, errp);
> +    if (*errp) {
>          return -1;
>      }
>      return 0;

Unrelated to this patch, I'm wondering about possible bugs in these
two init functions
:

  void vnc_display_init(const char *id, Error **errp)
  {
    VncDisplay *vd;

    if (vnc_display_find(id) != NULL) {
        return;
    }

Why isn't a vnc_display_find failure a reportable error ? With this
"return", the caller thinks we've successfully initialized the
'vc' struct but we haven't.

And


  void vnc_display_open(const char *id, Error **errp)
  {
    VncDisplay *vd = vnc_display_find(id);
    QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);

    ...snip....

    if (!opts) {
        return;
    }


Shouldn't the failure to find 'opts' be an error, rather than an
successful return ?

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 44/60] ui/vnc: VncDisplay.id is not const
  2026-03-17  8:50 ` [PATCH 44/60] ui/vnc: VncDisplay.id is not const Marc-André Lureau
@ 2026-03-24 14:39   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:39 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:58PM +0400, Marc-André Lureau wrote:
> s/strdup/g_strdup to highlight the issue and be consistent with other
> allocations.
> 
> The next patch is going to introduce vnc_display_free() to take care of
> deallocating it.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc.h | 2 +-
>  ui/vnc.c | 2 +-
>  2 files changed, 2 insertions(+), 2 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 45/60] ui/vnc: fix vnc_display_init() leak on failure
  2026-03-17  8:50 ` [PATCH 45/60] ui/vnc: fix vnc_display_init() leak on failure Marc-André Lureau
@ 2026-03-24 14:47   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:47 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:59PM +0400, Marc-André Lureau wrote:
> Do not add the display state to the vnc list, if the initialization
> failed. Add vnc_display_free(), to free the display state and associated
> data in such case. The function is meant to be public and reused in the
> following changes.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/keymaps.h |  1 +
>  ui/keymaps.c | 13 ++++++++++---
>  ui/vnc.c     | 30 ++++++++++++++++++++++++++----
>  3 files changed, 37 insertions(+), 7 deletions(-)
> 
> diff --git a/ui/keymaps.h b/ui/keymaps.h
> index 3d52c0882a1..e8917e56404 100644
> --- a/ui/keymaps.h
> +++ b/ui/keymaps.h
> @@ -54,6 +54,7 @@ typedef struct kbd_layout_t kbd_layout_t;
>  
>  kbd_layout_t *init_keyboard_layout(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);
>  int keycode_is_keypad(kbd_layout_t *k, int keycode);
> diff --git a/ui/keymaps.c b/ui/keymaps.c
> index 2359dbfe7e6..d1b3f43dc8a 100644
> --- a/ui/keymaps.c
> +++ b/ui/keymaps.c
> @@ -178,6 +178,14 @@ out:
>      return ret;
>  }
>  
> +void kbd_layout_free(kbd_layout_t *k)
> +{
> +    if (!k) {
> +        return;
> +    }
> +    g_hash_table_unref(k->hash);
> +    g_free(k);
> +}
>  
>  kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
>                                     const char *language, Error **errp)
> @@ -185,10 +193,9 @@ kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
>      kbd_layout_t *k;
>  
>      k = g_new0(kbd_layout_t, 1);
> -    k->hash = g_hash_table_new(NULL, NULL);
> +    k->hash = g_hash_table_new_full(NULL, NULL, NULL, g_free);
>      if (parse_keyboard_layout(k, table, language, errp) < 0) {
> -        g_hash_table_unref(k->hash);
> -        g_free(k);
> +        kbd_layout_free(k);
>          return NULL;
>      }
>      return k;

This is fixing a memory leak in init_keyboard_layout that's separate
from the VNC leak, so these ui/keymaps.c should be their own commit.


> diff --git a/ui/vnc.c b/ui/vnc.c
> index 763b13acbde..115ff8a988e 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -3421,6 +3421,8 @@ static void vmstate_change_handler(void *opaque, bool running, RunState state)
>      update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
>  }
>  
> +static void vnc_display_free(VncDisplay *vd);
> +
>  void vnc_display_init(const char *id, Error **errp)
>  {
>      VncDisplay *vd;
> @@ -3430,8 +3432,9 @@ void vnc_display_init(const char *id, Error **errp)
>      }
>      vd = g_malloc0(sizeof(*vd));
>  
> +    qemu_mutex_init(&vd->mutex);
>      vd->id = g_strdup(id);
> -    QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
> +    vd->dcl.ops = &dcl_ops;
>  
>      QTAILQ_INIT(&vd->clients);
>      vd->expires = TIME_MAX;
> @@ -3445,22 +3448,22 @@ void vnc_display_init(const char *id, Error **errp)
>      }
>  
>      if (!vd->kbd_layout) {
> +        vnc_display_free(vd);
>          return;
>      }
>  
>      vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
>      vd->connections_limit = 32;
>  
> -    qemu_mutex_init(&vd->mutex);
>      vnc_start_worker_thread();
>  
> -    vd->dcl.ops = &dcl_ops;
>      register_displaychangelistener(&vd->dcl);
>      vd->kbd = qkbd_state_init(vd->dcl.con);
>      vd->vmstate_handler_entry = qemu_add_vm_change_state_handler(
>          &vmstate_change_handler, vd);
> -}
>  
> +    QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
> +}
>  
>  static void vnc_display_close(VncDisplay *vd)
>  {
> @@ -3504,6 +3507,25 @@ static void vnc_display_close(VncDisplay *vd)
>  #endif
>  }
>  
> +static void vnc_display_free(VncDisplay *vd)
> +{
> +    if (!vd) {
> +        return;
> +    }
> +    vnc_display_close(vd);
> +    unregister_displaychangelistener(&vd->dcl);
> +    qkbd_state_free(vd->kbd);
> +    qemu_del_vm_change_state_handler(vd->vmstate_handler_entry);
> +    kbd_layout_free(vd->kbd_layout);
> +    qemu_mutex_destroy(&vd->mutex);
> +    if (QTAILQ_IN_USE(vd, next)) {
> +        QTAILQ_REMOVE(&vnc_displays, vd, next);
> +    }
> +    g_free(vd->id);
> +    g_free(vd);
> +}

If we're introducing this we need to answer the earlier questions
in this series about killing off the VNC worker thread, as IMHO,
we should not leave the thread running if we're claiming to be
able to free VncDisplay state.

> +
> +
>  int vnc_display_password(const char *id, const char *password, Error **errp)
>  {
>      VncDisplay *vd = vnc_display_find(id);
> 
> -- 
> 2.53.0
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 46/60] ui/vnc: merge vnc_display_init() and vnc_display_open()
  2026-03-17  8:51 ` [PATCH 46/60] ui/vnc: merge vnc_display_init() and vnc_display_open() Marc-André Lureau
@ 2026-03-24 14:51   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:51 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:51:00PM +0400, Marc-André Lureau wrote:
> Combine the two-step vnc_display_init()/vnc_display_open() sequence
> into a single vnc_display_new() function that returns VncDisplay*.
> This simplifies the API by making vnc_display_open() an
> internal detail and will allow further code simplification.
> 
> vnc_display_new() is moved to vnc.h, since it returns VncDisplay* now.
> Add vnc_display_free() for consistency, and it will be later used.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/ui/console.h |  2 --
>  ui/vnc.h             |  3 ++
>  ui/vnc.c             | 79 ++++++++++++++++++++++------------------------------
>  3 files changed, 37 insertions(+), 47 deletions(-)
> 
> diff --git a/include/ui/console.h b/include/ui/console.h
> index 152333d60fc..737ceba3890 100644
> --- a/include/ui/console.h
> +++ b/include/ui/console.h
> @@ -448,8 +448,6 @@ const char *qemu_display_get_vc(DisplayOptions *opts);
>  void qemu_display_help(void);
>  
>  /* vnc.c */
> -void vnc_display_init(const char *id, Error **errp);
> -void vnc_display_open(const char *id, Error **errp);
>  void vnc_display_add_client(const char *id, int csock, bool skipauth);
>  int vnc_display_password(const char *id, const char *password, Error **errp);
>  int vnc_display_pw_expire(const char *id, time_t expires);
> diff --git a/ui/vnc.h b/ui/vnc.h
> index c5d678ac31e..6afe0f16d12 100644
> --- a/ui/vnc.h
> +++ b/ui/vnc.h
> @@ -548,6 +548,9 @@ enum VncFeatures {
>  #define VNC_CLIPBOARD_NOTIFY   (1 << 27)
>  #define VNC_CLIPBOARD_PROVIDE  (1 << 28)
>  
> +VncDisplay *vnc_display_new(const char *id, Error **errp);
> +void vnc_display_free(VncDisplay *vd);

This re-inforces my comment from the previous patch, wrt stopping
the VNC worker thread. If we're exposing this as a "public" API,
then IMHO, we need to  keep around all the code for stopping the
worker thread, revert 09526058d0a501106dcac842a455e187f1413d98
and actually call vnc_stop_worker_thread in the right place(s).

> +
>  /*****************************************************************************
>   *
>   * Internal APIs
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 115ff8a988e..03b99c9e590 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -3421,14 +3421,14 @@ static void vmstate_change_handler(void *opaque, bool running, RunState state)
>      update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
>  }
>  
> -static void vnc_display_free(VncDisplay *vd);
> +static bool vnc_display_open(VncDisplay *vd, Error **errp);
>  
> -void vnc_display_init(const char *id, Error **errp)
> +VncDisplay *vnc_display_new(const char *id, Error **errp)
>  {
>      VncDisplay *vd;
>  
>      if (vnc_display_find(id) != NULL) {
> -        return;
> +        return NULL;
>      }
>      vd = g_malloc0(sizeof(*vd));
>  
> @@ -3449,7 +3449,7 @@ void vnc_display_init(const char *id, Error **errp)
>  
>      if (!vd->kbd_layout) {
>          vnc_display_free(vd);
> -        return;
> +        return NULL;
>      }
>  
>      vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
> @@ -3462,7 +3462,13 @@ void vnc_display_init(const char *id, Error **errp)
>      vd->vmstate_handler_entry = qemu_add_vm_change_state_handler(
>          &vmstate_change_handler, vd);
>  
> +    if (!vnc_display_open(vd, errp)) {
> +        vnc_display_free(vd);
> +        return NULL;
> +    }
> +
>      QTAILQ_INSERT_TAIL(&vnc_displays, vd, next);
> +    return vd;
>  }
>  
>  static void vnc_display_close(VncDisplay *vd)
> @@ -3507,7 +3513,7 @@ static void vnc_display_close(VncDisplay *vd)
>  #endif
>  }
>  
> -static void vnc_display_free(VncDisplay *vd)
> +void vnc_display_free(VncDisplay *vd)
>  {
>      if (!vd) {
>          return;
> @@ -3525,7 +3531,6 @@ static void vnc_display_free(VncDisplay *vd)
>      g_free(vd);
>  }
>  
> -
>  int vnc_display_password(const char *id, const char *password, Error **errp)
>  {
>      VncDisplay *vd = vnc_display_find(id);
> @@ -4068,10 +4073,9 @@ bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp)
>      return true;
>  }
>  
> -void vnc_display_open(const char *id, Error **errp)
> +static bool vnc_display_open(VncDisplay *vd, Error **errp)
>  {
> -    VncDisplay *vd = vnc_display_find(id);
> -    QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
> +    QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, vd->id);
>      g_autoptr(SocketAddressList) saddr_list = NULL;
>      g_autoptr(SocketAddressList) wsaddr_list = NULL;
>      const char *share, *device_id;
> @@ -4090,26 +4094,23 @@ void vnc_display_open(const char *id, Error **errp)
>      assert(vd);
>      assert(opts);
>  
> -    vnc_display_close(vd);
> -
>      reverse = qemu_opt_get_bool(opts, "reverse", false);
>      if (vnc_display_get_addresses(opts, reverse, &saddr_list, &wsaddr_list,
>                                    errp) < 0) {
> -        goto fail;
> +        return false;
>      }
>  
> -
>      passwordSecret = qemu_opt_get(opts, "password-secret");
>      if (passwordSecret) {
>          if (qemu_opt_get(opts, "password")) {
>              error_setg(errp,
>                         "'password' flag is redundant with 'password-secret'");
> -            goto fail;
> +            return false;
>          }
>          vd->password = qcrypto_secret_lookup_as_utf8(passwordSecret,
>                                                       errp);
>          if (!vd->password) {
> -            goto fail;
> +            return false;
>          }
>          password = true;
>      } else {
> @@ -4120,7 +4121,7 @@ void vnc_display_open(const char *id, Error **errp)
>                  QCRYPTO_CIPHER_ALGO_DES, QCRYPTO_CIPHER_MODE_ECB)) {
>              error_setg(errp,
>                         "Cipher backend does not support DES algorithm");
> -            goto fail;
> +            return false;
>          }
>      }
>  
> @@ -4130,7 +4131,7 @@ void vnc_display_open(const char *id, Error **errp)
>  #ifndef CONFIG_VNC_SASL
>      if (sasl) {
>          error_setg(errp, "VNC SASL auth requires cyrus-sasl support");
> -        goto fail;
> +        return false;
>      }
>  #endif /* CONFIG_VNC_SASL */
>      credid = qemu_opt_get(opts, "tls-creds");
> @@ -4141,7 +4142,7 @@ void vnc_display_open(const char *id, Error **errp)
>          if (!creds) {
>              error_setg(errp, "No TLS credentials with id '%s'",
>                         credid);
> -            goto fail;
> +            return false;
>          }
>          vd->tlscreds = (QCryptoTLSCreds *)
>              object_dynamic_cast(creds,
> @@ -4149,26 +4150,26 @@ void vnc_display_open(const char *id, Error **errp)
>          if (!vd->tlscreds) {
>              error_setg(errp, "Object with id '%s' is not TLS credentials",
>                         credid);
> -            goto fail;
> +            return false;
>          }
>          object_ref(OBJECT(vd->tlscreds));
>  
>          if (!qcrypto_tls_creds_check_endpoint(vd->tlscreds,
>                                                QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
>                                                errp)) {
> -            goto fail;
> +            return false;
>          }
>      }
>      tlsauthz = qemu_opt_get(opts, "tls-authz");
>      if (tlsauthz && !vd->tlscreds) {
>          error_setg(errp, "'tls-authz' provided but TLS is not enabled");
> -        goto fail;
> +        return false;
>      }
>  
>      saslauthz = qemu_opt_get(opts, "sasl-authz");
>      if (saslauthz && !sasl) {
>          error_setg(errp, "'sasl-authz' provided but SASL auth is not enabled");
> -        goto fail;
> +        return false;
>      }
>  
>      share = qemu_opt_get(opts, "share");
> @@ -4181,7 +4182,7 @@ void vnc_display_open(const char *id, Error **errp)
>              vd->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
>          } else {
>              error_setg(errp, "unknown vnc share= option");
> -            goto fail;
> +            return false;
>          }
>      } else {
>          vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
> @@ -4215,20 +4216,20 @@ void vnc_display_open(const char *id, Error **errp)
>      if (vnc_display_setup_auth(&vd->auth, &vd->subauth,
>                                 vd->tlscreds, password,
>                                 sasl, false, errp) < 0) {
> -        goto fail;
> +        return false;
>      }
>      trace_vnc_auth_init(vd, 0, vd->auth, vd->subauth);
>  
>      if (vnc_display_setup_auth(&vd->ws_auth, &vd->ws_subauth,
>                                 vd->tlscreds, password,
>                                 sasl, true, errp) < 0) {
> -        goto fail;
> +        return false;
>      }
>      trace_vnc_auth_init(vd, 1, vd->ws_auth, vd->ws_subauth);
>  
>  #ifdef CONFIG_VNC_SASL
>      if (sasl && !vnc_sasl_server_init(errp)) {
> -        goto fail;
> +        return false;
>      }
>  #endif
>      vd->lock_key_sync = lock_key_sync;
> @@ -4241,7 +4242,7 @@ void vnc_display_open(const char *id, Error **errp)
>      if (audiodev) {
>          vd->audio_be = audio_be_by_name(audiodev, errp);
>          if (!vd->audio_be) {
> -            goto fail;
> +            return false;
>          }
>      } else {
>          vd->audio_be = audio_get_default_audio_be(NULL);
> @@ -4255,7 +4256,7 @@ void vnc_display_open(const char *id, Error **errp)
>          con = qemu_console_lookup_by_device_name(device_id, head, &err);
>          if (err) {
>              error_propagate(errp, err);
> -            goto fail;
> +            return false;
>          }
>      } else {
>          con = qemu_console_lookup_default();
> @@ -4271,16 +4272,16 @@ void vnc_display_open(const char *id, Error **errp)
>      qkbd_state_set_delay(vd->kbd, key_delay_ms);
>  
>      if (saddr_list == NULL) {
> -        return;
> +        return true;
>      }
>  
>      if (reverse) {
>          if (vnc_display_connect(vd, saddr_list, wsaddr_list, errp) < 0) {
> -            goto fail;
> +            return false;
>          }
>      } else {
>          if (vnc_display_listen(vd, saddr_list, wsaddr_list, errp) < 0) {
> -            goto fail;
> +            return false;
>          }
>      }
>  
> @@ -4288,11 +4289,7 @@ void vnc_display_open(const char *id, Error **errp)
>          vnc_display_print_local_addr(vd);
>      }
>  
> -    /* Success */
> -    return;
> -
> -fail:
> -    vnc_display_close(vd);
> +    return true;
>  }

I think it would be helpful for clarity if we changed vnc_display_init
and vnc_display_open to have a 'bool' return value in an earlier patch,
then let this patch just focus on the merge without the large
refactoring of return vaules.


>  
>  void vnc_display_add_client(const char *id, int csock, bool skipauth)
> @@ -4348,15 +4345,7 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
>          id = vnc_auto_assign_id(opts);
>      }
>  
> -    vnc_display_init(id, errp);
> -    if (*errp) {
> -        return -1;
> -    }
> -    vnc_display_open(id, errp);
> -    if (*errp) {
> -        return -1;
> -    }
> -    return 0;
> +    return vnc_display_new(id, errp) != NULL ? 0 : -1;
>  }
>  
>  static void vnc_register_config(void)
> 
> -- 
> 2.53.0
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 47/60] ui/vnc: report an error for duplicate display id
  2026-03-17  8:51 ` [PATCH 47/60] ui/vnc: report an error for duplicate display id Marc-André Lureau
@ 2026-03-24 14:52   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:52 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:51:01PM +0400, Marc-André Lureau wrote:
> Returning NULL without setting an error is odd.
> 
> Note that we could make this a programming error too, as the duplicate
> check is also done during QemuOpt parsing.

IMHO we shouldn't rely on QemuOpts given our general intent to get
off QemuOpts to QMP for everything.

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

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


> 
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 03b99c9e590..2c53c92914b 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -3428,6 +3428,7 @@ VncDisplay *vnc_display_new(const char *id, Error **errp)
>      VncDisplay *vd;
>  
>      if (vnc_display_find(id) != NULL) {
> +        error_setg(errp, "Display '%s' already exists", id);
>          return NULL;
>      }
>      vd = g_malloc0(sizeof(*vd));
> 
> -- 
> 2.53.0
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 48/60] ui/vnc: defer listener registration until the console is known
  2026-03-17  8:51 ` [PATCH 48/60] ui/vnc: defer listener registration until the console is known Marc-André Lureau
@ 2026-03-24 14:53   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:53 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:51:02PM +0400, Marc-André Lureau wrote:
> Previously, the display change listener was registered early in
> vnc_display_new() without a console, requiring vnc_display_open() to
> conditionally unregister and re-register it when the actual console was
> resolved. Since vnc_display_new() and vnc_display_open() were merged in
> the previous commit, simply delay the registration and keyboard state
> initialization to vnc_display_open(), after the console has been looked
> up. This removes the conditional re-registration and simplifies the code.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/vnc.c | 12 +++---------
>  1 file changed, 3 insertions(+), 9 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 49/60] ui/vnc: explicitly link with png
  2026-03-17  8:51 ` [PATCH 49/60] ui/vnc: explicitly link with png Marc-André Lureau
@ 2026-03-24 14:56   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:56 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:51:03PM +0400, Marc-André Lureau wrote:
> The VNC code uses PNG directly.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  ui/meson.build | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 51/60] ui/console: remove qemu_console_is_visible()
  2026-03-17  8:51 ` [PATCH 51/60] ui/console: remove qemu_console_is_visible() Marc-André Lureau
@ 2026-03-24 14:57   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 14:57 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:51:05PM +0400, Marc-André Lureau wrote:
> 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.
> 
> 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(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 57/60] tests: rename the dbus-daemon helper script
  2026-03-17  8:51 ` [PATCH 57/60] tests: rename the dbus-daemon helper script Marc-André Lureau
@ 2026-03-24 15:05   ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 15:05 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:51:11PM +0400, Marc-André Lureau wrote:
> The following patches are going to use it for qemu-vnc.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  tests/{dbus-vmstate-daemon.sh => dbus-daemon.sh} | 0
>  tests/qtest/meson.build                          | 2 +-
>  2 files changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 60/60] contrib/qemu-vnc: add standalone VNC server over D-Bus
  2026-03-17  8:51 ` [PATCH 60/60] contrib/qemu-vnc: add standalone VNC server over D-Bus Marc-André Lureau
@ 2026-03-24 15:24   ` Daniel P. Berrangé
  2026-03-24 15:44     ` Peter Maydell
  2026-03-25  8:32     ` Marc-André Lureau
  0 siblings, 2 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 15:24 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:51:14PM +0400, Marc-André Lureau wrote:
> Add a standalone VNC server binary that connects to a running QEMU
> instance via the D-Bus display interface (org.qemu.Display1, via the bus
> or directly p2p). This allows serving a VNC display without compiling
> VNC support directly into the QEMU system emulator, and enables running
> the VNC server as a separate process with independent lifecycle and
> privilege domain.

IIUC, this new binary is linking to all the existnig ui/vnc.c
code, so there's no actual new impl of the RFB protocol, just
a new glue layer for launching the existing impl with comms
over DBus instead of from direct callback functions ?

> Built only when both VNC and D-Bus display support are enabled.
> If we wanted to have qemu -vnc disabled, and qemu-vnc built, we would
> need to split CONFIG_VNC. This is left as a future exercise.
> 
> I left out for now:
>  - sasl & tls authz

IMHO these are must have features, as the default VNC protocol
security is a very bad joke.

>  - some runtime functionalities (better done by restarting)
>  - a few legacy options
>  - Windows support
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  MAINTAINERS                    |   5 +
>  docs/conf.py                   |   3 +
>  docs/interop/dbus-display.rst  |   2 +
>  docs/interop/dbus-vnc.rst      |  26 ++
>  docs/interop/index.rst         |   1 +
>  docs/meson.build               |   1 +
>  docs/tools/index.rst           |   1 +
>  docs/tools/qemu-vnc.rst        | 199 +++++++++++
>  meson.build                    |  17 +
>  contrib/qemu-vnc/qemu-vnc.h    |  46 +++
>  contrib/qemu-vnc/trace.h       |   4 +
>  contrib/qemu-vnc/audio.c       | 307 +++++++++++++++++
>  contrib/qemu-vnc/chardev.c     | 127 +++++++
>  contrib/qemu-vnc/clipboard.c   | 378 +++++++++++++++++++++
>  contrib/qemu-vnc/console.c     | 168 ++++++++++
>  contrib/qemu-vnc/dbus.c        | 439 ++++++++++++++++++++++++
>  contrib/qemu-vnc/display.c     | 456 +++++++++++++++++++++++++
>  contrib/qemu-vnc/input.c       | 239 ++++++++++++++
>  contrib/qemu-vnc/qemu-vnc.c    | 450 +++++++++++++++++++++++++
>  contrib/qemu-vnc/stubs.c       |  66 ++++
>  contrib/qemu-vnc/utils.c       |  59 ++++

IMHO we should not really be adding more things to the contrib
directory, and rather wish most of the existing stuff was
not there. When I see "contrib" directories in projects, the
message is generally "unmaintained, abandoned junk".

If this is a serious maintained feature we expect people to use,
it should be formally under the ui/ directory, possibly a
'ui/dbus-vnc' subdir ?

>  tests/qtest/dbus-vnc-test.c    | 733 +++++++++++++++++++++++++++++++++++++++++
>  contrib/qemu-vnc/meson.build   |  26 ++
>  contrib/qemu-vnc/qemu-vnc1.xml | 174 ++++++++++
>  contrib/qemu-vnc/trace-events  |  20 ++
>  meson_options.txt              |   2 +
>  scripts/meson-buildoptions.sh  |   3 +
>  tests/dbus-daemon.sh           |  14 +-
>  tests/qtest/meson.build        |   8 +
>  29 files changed, 3971 insertions(+), 3 deletions(-)
> 

> diff --git a/meson_options.txt b/meson_options.txt
> index 31d5916cfce..ef938e74793 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -119,6 +119,8 @@ option('vfio_user_server', type: 'feature', value: 'disabled',
>         description: 'vfio-user server support')
>  option('dbus_display', type: 'feature', value: 'auto',
>         description: '-display dbus support')
> +option('qemu_vnc', type: 'feature', value: 'auto',
> +       description: 'standalone VNC server over D-Bus')

Every option is about QEMU, so 'qemu_' on a name feels redundant,
and QEMU alrady has a VNC server too.

I suggest 'dbus_vnc' is possibly a better name


>  option('tpm', type : 'feature', value : 'auto',
>         description: 'TPM support')
>  option('valgrind', type : 'feature', value: 'auto',

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering
  2026-03-24 14:17     ` Marc-André Lureau
@ 2026-03-24 15:42       ` Daniel P. Berrangé
  0 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 15:42 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 24, 2026 at 06:17:37PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Mar 24, 2026 at 6:08 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
> >
> > On Tue, Mar 17, 2026 at 12:50:25PM +0400, Marc-André Lureau wrote:
> > > The text console receives bytes that may be UTF-8 encoded (e.g. from
> > > a guest running a modern distro), but currently treats each byte as a
> > > raw character index into the VGA/CP437 font, producing garbled output
> > > for any multi-byte sequence.
> > >
> > > Add a proper UTF-8 decoder using Bjoern Hoehrmann's DFA.
> > > The DFA inherently rejects overlong encodings, surrogates, and
> > > codepoints above U+10FFFF.  Completed codepoints are then mapped to
> > > CP437, unmappable characters are displayed as '?'.
> >
> > I'm surprised we can't do a charset conversion using GLib APIs ?
> >
> > Do the g_convert family of  APIs (which IIUC wrap the distro iconv)
> > not do what we would want ? If not, would direct use of iconv not
> > be an alternative ?
> >
> 
> I tried to use GIconv but ran into a number of issues, as it doesn't
> operate on character level, but strings. And it uses allocation etc. I
> didn't manage with iconv either.

Looking again, the g_utf8_validate function is /almost/ what we
want, but its API design collapses both "invalid utf8" and
"incomplete character" into the same error return value, so we
can't distinguish them to decide whether to wait for more bytes
or reset the state :-(

So yeah, I can see why this is needed now.

> 
> > It feels pretty wrong to need to embed UTF8 decoding code in
> > QEMU
> 
> Yes, but on a standalone qemu-vnc server, is it more acceptable?

IIUC, this will be linked into regular QEMU too, right ?

> > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > > ---
> > >  ui/cp437.h      |  13 ++++
> > >  ui/console-vc.c |  62 +++++++++++++++++
> > >  ui/cp437.c      | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > >  ui/meson.build  |   2 +-
> > >  4 files changed, 281 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/ui/cp437.h b/ui/cp437.h
> > > new file mode 100644
> > > index 00000000000..81ace8317c7
> > > --- /dev/null
> > > +++ b/ui/cp437.h
> > > @@ -0,0 +1,13 @@
> > > +/*
> > > + * SPDX-License-Identifier: GPL-2.0-or-later
> > > + *
> > > + * Copyright (c) QEMU contributors
> > > + */
> > > +#ifndef QEMU_CP437_H
> > > +#define QEMU_CP437_H
> > > +
> > > +#include <stdint.h>

Shouldn't be required, since it is pulled in by osdep.h

> > > +
> > > +int unicode_to_cp437(uint32_t codepoint);

Perhaps better as qemu_unicode_to_cp437 

> > > +
> > > +#endif /* QEMU_CP437_H */


> > > diff --git a/ui/console-vc.c b/ui/console-vc.c
> > > index 8dee1f9bd01..7bbd65dea27 100644
> > > --- a/ui/console-vc.c
> > > +++ b/ui/console-vc.c
> > > @@ -9,6 +9,7 @@
> > >  #include "qemu/fifo8.h"
> > >  #include "qemu/option.h"
> > >  #include "ui/console.h"
> > > +#include "ui/cp437.h"
> > >
> > >  #include "trace.h"
> > >  #include "console-priv.h"
> > > @@ -89,6 +90,8 @@ struct VCChardev {
> > >      enum TTYState state;
> > >      int esc_params[MAX_ESC_PARAMS];
> > >      int nb_esc_params;
> > > +    uint32_t utf8_state;     /* UTF-8 DFA decoder state */
> > > +    uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
> > >      TextAttributes t_attrib; /* currently active text attributes */
> > >      TextAttributes t_attrib_saved;
> > >      int x_saved, y_saved;
> > > @@ -598,6 +601,47 @@ static void vc_clear_xy(VCChardev *vc, int x, int y)
> > >      vc_update_xy(vc, x, y);
> > >  }
> > >
> > > +/*
> > > + * UTF-8 DFA decoder by Bjoern Hoehrmann.
> > > + * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
> > > + * See https://github.com/polijan/utf8_decode for details.
> > > + *
> > > + * SPDX-License-Identifier: MIT
> > > + */
> > > +#define UTF8_ACCEPT 0
> > > +#define UTF8_REJECT 12

This is an awfully generic define name, could we use something with
QEMU_ as a prefix to avoid risk of clashes with any external headers
we import

> > > +
> > > +static const uint8_t utf8d[] = {
> > > +    /* character class lookup */
> > > +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> > > +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> > > +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> > > +    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
> > > +    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
> > > +    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
> > > +    8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
> > > +   10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
> > > +
> > > +    /* state transition lookup */
> > > +     0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
> > > +    12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
> > > +    12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
> > > +    12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
> > > +    12,36,12,12,12,12,12,12,12,12,12,12,
> > > +};
> > > +
> > > +static uint32_t utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
> > > +{
> > > +    uint32_t type = utf8d[byte];
> > > +
> > > +    *codep = (*state != UTF8_ACCEPT) ?
> > > +        (byte & 0x3fu) | (*codep << 6) :
> > > +        (0xffu >> type) & (byte);
> > > +
> > > +    *state = utf8d[256 + *state + type];
> > > +    return *state;
> > > +}
> > > +
> > >  static void vc_put_one(VCChardev *vc, int ch)
> > >  {
> > >      QemuTextConsole *s = vc->console;
> > > @@ -761,6 +805,24 @@ static void vc_putchar(VCChardev *vc, int ch)
> > >
> > >      switch(vc->state) {
> > >      case TTY_STATE_NORM:
> > > +        /* Feed byte through the UTF-8 DFA decoder */
> > > +        if (ch >= 0x80) {
> > > +            switch (utf8_decode(&vc->utf8_state, &vc->utf8_codepoint, ch)) {
> > > +            case UTF8_ACCEPT:
> > > +                vc_put_one(vc, unicode_to_cp437(vc->utf8_codepoint));
> > > +                break;
> > > +            case UTF8_REJECT:
> > > +                /* Reset state so the decoder can resync */
> > > +                vc->utf8_state = UTF8_ACCEPT;
> > > +                break;
> > > +            default:
> > > +                /* Need more bytes */
> > > +                break;
> > > +            }
> > > +            break;
> > > +        }
> > > +        /* ASCII byte: abort any pending UTF-8 sequence */
> > > +        vc->utf8_state = UTF8_ACCEPT;
> > >          switch(ch) {
> > >          case '\r':  /* carriage return */
> > >              s->x = 0;

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 60/60] contrib/qemu-vnc: add standalone VNC server over D-Bus
  2026-03-24 15:24   ` Daniel P. Berrangé
@ 2026-03-24 15:44     ` Peter Maydell
  2026-03-25  8:32     ` Marc-André Lureau
  1 sibling, 0 replies; 105+ messages in thread
From: Peter Maydell @ 2026-03-24 15:44 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: Marc-André Lureau, qemu-devel

On Tue, 24 Mar 2026 at 15:24, Daniel P. Berrangé <berrange@redhat.com> wrote:
> IMHO we should not really be adding more things to the contrib
> directory, and rather wish most of the existing stuff was
> not there. When I see "contrib" directories in projects, the
> message is generally "unmaintained, abandoned junk".

Definitely agreed: either we care about something, and it should
have a proper place and somebody who maintains it; or else
we don't care about it and should kick it out of the tree.
Having a contrib/ directory is an attractive nuisance for
accumulating more stuff in the awkward halfway-house.

We should really clean up the existing /contrib/ and move
stuff into where it belongs (quite often tools/, which in
turn mostly contains things that should *not* be in tools/...)

-- PMM


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

* Re: [PATCH 00/60] ui: add standalone VNC server over D-Bus
  2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
                   ` (59 preceding siblings ...)
  2026-03-17  8:51 ` [PATCH 60/60] contrib/qemu-vnc: add standalone VNC server over D-Bus Marc-André Lureau
@ 2026-03-24 17:36 ` Daniel P. Berrangé
  60 siblings, 0 replies; 105+ messages in thread
From: Daniel P. Berrangé @ 2026-03-24 17:36 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Tue, Mar 17, 2026 at 12:50:14PM +0400, Marc-André Lureau wrote:
> This series adds qemu-vnc, a standalone VNC server that connects to a
> running QEMU instance via the D-Bus display interface (org.qemu.Display1).
> This allows serving a VNC display as a separate process with an independent
> lifecycle and privilege domain, without requiring VNC support compiled into
> the QEMU system emulator itself.
> 
> The bulk of the series is preparatory refactoring:

A few of the patches in this series are addressing bugs such as the
leaks. Where practical those should probably be ordered at the very
start of the series such that they have minimal deps and could be
cherry-picked to stable.

> 
> - Clean up VNC code: merge init/open, fix leaks, simplify error handling
> - Extract and clean up VT100 emulation from console-vc into a reusable unit
> - Reorganize ui/ code: move DisplaySurface functions, vgafont, datadir
> and other pieces into their own files
> - Refactor console APIs: rename methods, simplify listener registration,
> return completion status from gfx_update
> - Extract common ui sources into a static library that can be linked by
> both the system emulator and the new standalone binary
> 
> The final patch adds contrib/qemu-vnc, built when both VNC and D-Bus
> display support are enabled.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> Marc-André Lureau (60):
>       ui/vnc-jobs: fix VncRectEntry leak on job cleanup
>       ui/vnc-jobs: clear source tag
>       ui/vnc-jobs: remove needless buffer_reset() before end
>       ui/vnc: clarify intent using buffer_empty() function
>       ui/vnc-jobs: vnc_has_job_locked() argument cannot be NULL
>       ui/vnc-jobs: remove dead VncJobQueue.exit
>       ui/vnc-jobs: remove vnc_queue_clear()
>       ui/vnc-jobs: narrow taking the lock when pushing empty jobs
>       ui/vnc-jobs: drop redundant (and needless) qemu_thread_get_self()
>       ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen)
>       ui/console-vc: add UTF-8 input decoding with CP437 rendering
>       ui/console-vc: ignore string-type escape sequences
>       ui/console-vc: fix comment shift-out/in comments
>       ui/console: dispatch get_label() through QOM virtual method
>       ui/console-vc: introduce QemuVT100
>       ui/console-vc: set vt100 associated pixman image
>       ui/console-vc: vga_putcharxy()->vt100_putcharxy()
>       ui/console-vc: make invalidate_xy() take vt100
>       ui/console-vc: make show_cursor() take vt100
>       ui/console-vc: decouple VT100 display updates via function pointer
>       ui/console-vc: console_refresh() -> vt100_refresh()
>       ui/console-vc: move cursor blinking logic into VT100 layer
>       ui/console-vc: console_scroll() -> vt100_scroll()
>       ui/console-vc: refactor text_console_resize() into vt100_set_image()
>       ui/console-vc: move vc_put_lf() to VT100 layer as vt100_put_lf()
>       ui/console-vc: unify the write path
>       ui/console-vc: move VT100 state machine and output FIFO into QemuVT100
>       ui/console-vc: extract vt100_input() from vc_chr_write()
>       ui/console-vc: extract vt100_keysym() from qemu_text_console_handle_keysym()
>       ui/console-vc: extract vt100_init() and vt100_fini()
>       ui/console: remove console_ch_t typedef and console_write_ch()
>       ui: avoid duplicating vgafont16 in each translation unit
>       ui/vgafont: add SPDX license header
>       ui: move FONT_WIDTH/HEIGHT to vgafont.h
>       ui/console-vc: move VT100 emulation into separate unit
>       util: move datadir.c from system/
>       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
>       system: make qemu_del_vm_change_state_handler accept NULL
>       ui/vnc: assert preconditions instead of silently returning
>       ui/vnc: simplify vnc_init_func error handling
>       ui/vnc: VncDisplay.id is not const
>       ui/vnc: fix vnc_display_init() leak on failure
>       ui/vnc: merge vnc_display_init() and vnc_display_open()
>       ui/vnc: report an error for duplicate display id
>       ui/vnc: defer listener registration until the console is known
>       ui/vnc: explicitly link with png
>       ui/vnc: add vnc-system unit, to allow different implementations
>       ui/console: remove qemu_console_is_visible()
>       ui/console: simplify registering display/console change listener
>       ui/console: return completion status from gfx_update callback
>       ui/console: rename public API to use consistent qemu_console_ prefix
>       ui/console: move console_handle_touch_event() to input
>       ui: extract common sources into a static library
>       tests: rename the dbus-daemon helper script
>       tests/qtest: fix dbus-vmstate-test compilation
>       tests/qtest: drop DBUS_VMSTATE_TEST_TMPDIR
>       contrib/qemu-vnc: add standalone VNC server over D-Bus
> 
>  MAINTAINERS                                      |    5 +
>  docs/conf.py                                     |    3 +
>  docs/interop/dbus-display.rst                    |    2 +
>  docs/interop/dbus-vnc.rst                        |   26 +
>  docs/interop/index.rst                           |    1 +
>  docs/meson.build                                 |    1 +
>  docs/tools/index.rst                             |    1 +
>  docs/tools/qemu-vnc.rst                          |  199 +
>  meson.build                                      |   17 +
>  contrib/qemu-vnc/qemu-vnc.h                      |   46 +
>  contrib/qemu-vnc/trace.h                         |    4 +
>  hw/display/qxl.h                                 |    2 +-
>  include/ui/console.h                             |  146 +-
>  include/ui/input.h                               |   15 +
>  include/ui/qemu-pixman.h                         |    8 +
>  include/ui/surface.h                             |    2 -
>  ui/console-priv.h                                |    8 -
>  ui/cp437.h                                       |   13 +
>  ui/keymaps.h                                     |    1 +
>  ui/vgafont.h                                     | 4618 +---------------------
>  ui/vnc.h                                         |    9 +-
>  ui/vt100.h                                       |   92 +
>  contrib/qemu-vnc/audio.c                         |  307 ++
>  contrib/qemu-vnc/chardev.c                       |  127 +
>  contrib/qemu-vnc/clipboard.c                     |  378 ++
>  contrib/qemu-vnc/console.c                       |  168 +
>  contrib/qemu-vnc/dbus.c                          |  439 ++
>  contrib/qemu-vnc/display.c                       |  456 +++
>  contrib/qemu-vnc/input.c                         |  239 ++
>  contrib/qemu-vnc/qemu-vnc.c                      |  450 +++
>  contrib/qemu-vnc/stubs.c                         |   66 +
>  contrib/qemu-vnc/utils.c                         |   59 +
>  hw/arm/musicpal.c                                |    7 +-
>  hw/display/artist.c                              |    8 +-
>  hw/display/ati.c                                 |   16 +-
>  hw/display/bcm2835_fb.c                          |   12 +-
>  hw/display/bochs-display.c                       |   20 +-
>  hw/display/cg3.c                                 |   11 +-
>  hw/display/cirrus_vga.c                          |    8 +-
>  hw/display/cirrus_vga_isa.c                      |    2 +-
>  hw/display/dm163.c                               |   10 +-
>  hw/display/exynos4210_fimd.c                     |   10 +-
>  hw/display/g364fb.c                              |   19 +-
>  hw/display/jazz_led.c                            |   24 +-
>  hw/display/macfb.c                               |   12 +-
>  hw/display/next-fb.c                             |    8 +-
>  hw/display/omap_lcdc.c                           |   18 +-
>  hw/display/pl110.c                               |    9 +-
>  hw/display/qxl-render.c                          |   18 +-
>  hw/display/qxl.c                                 |   25 +-
>  hw/display/ramfb-standalone.c                    |    6 +-
>  hw/display/ramfb.c                               |    4 +-
>  hw/display/sm501.c                               |   14 +-
>  hw/display/ssd0303.c                             |   14 +-
>  hw/display/ssd0323.c                             |   16 +-
>  hw/display/tcx.c                                 |   22 +-
>  hw/display/vga-isa.c                             |    2 +-
>  hw/display/vga-mmio.c                            |    2 +-
>  hw/display/vga-pci.c                             |    6 +-
>  hw/display/vga.c                                 |   60 +-
>  hw/display/vhost-user-gpu.c                      |   22 +-
>  hw/display/virtio-gpu-base.c                     |    7 +-
>  hw/display/virtio-gpu-rutabaga.c                 |   10 +-
>  hw/display/virtio-gpu-udmabuf.c                  |    4 +-
>  hw/display/virtio-gpu-virgl.c                    |   20 +-
>  hw/display/virtio-gpu.c                          |   26 +-
>  hw/display/virtio-vga.c                          |   10 +-
>  hw/display/vmware_vga.c                          |   21 +-
>  hw/display/xenfb.c                               |   12 +-
>  hw/display/xlnx_dp.c                             |   20 +-
>  hw/vfio/display.c                                |   49 +-
>  system/runstate.c                                |    3 +
>  tests/qtest/dbus-vmstate-test.c                  |   13 +-
>  tests/qtest/dbus-vnc-test.c                      |  733 ++++
>  ui/console-vc-stubs.c                            |    9 +-
>  ui/console-vc.c                                  | 1021 +----
>  ui/console.c                                     |  471 +--
>  ui/cp437.c                                       |  205 +
>  ui/curses.c                                      |   23 +-
>  ui/dbus-console.c                                |   16 +-
>  ui/dbus-listener.c                               |   37 +-
>  ui/display-surface.c                             |  107 +
>  ui/egl-headless.c                                |    8 +-
>  ui/gtk-egl.c                                     |    6 +-
>  ui/gtk-gl-area.c                                 |    6 +-
>  ui/gtk.c                                         |   36 +-
>  ui/input.c                                       |   65 +
>  ui/keymaps.c                                     |   13 +-
>  ui/sdl2-2d.c                                     |    2 +-
>  ui/sdl2-gl.c                                     |    2 +-
>  ui/sdl2.c                                        |   24 +-
>  ui/spice-display.c                               |   24 +-
>  ui/vgafont.c                                     | 4616 +++++++++++++++++++++
>  ui/vnc-jobs.c                                    |   54 +-
>  ui/vnc-system.c                                  |   19 +
>  ui/vnc.c                                         |  202 +-
>  ui/vt100.c                                       |  987 +++++
>  {system => util}/datadir.c                       |    0
>  contrib/qemu-vnc/meson.build                     |   26 +
>  contrib/qemu-vnc/qemu-vnc1.xml                   |  174 +
>  contrib/qemu-vnc/trace-events                    |   20 +
>  hw/display/apple-gfx.m                           |   26 +-
>  meson_options.txt                                |    2 +
>  scripts/meson-buildoptions.sh                    |    3 +
>  system/meson.build                               |    1 -
>  system/trace-events                              |    1 -
>  tests/{dbus-vmstate-daemon.sh => dbus-daemon.sh} |   16 +-
>  tests/qtest/meson.build                          |   13 +-
>  ui/cocoa.m                                       |   23 +-
>  ui/meson.build                                   |  103 +-
>  util/meson.build                                 |    1 +
>  util/trace-events                                |    3 +
>  112 files changed, 10982 insertions(+), 6624 deletions(-)
> ---
> base-commit: 559919ce54927d59b215a4665eda7ab6118a48aa
> change-id: 20260312-qemu-vnc-9662fc572262
> 
> Best regards,
> -- 
> Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering
  2026-03-17  8:50 ` [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering Marc-André Lureau
  2026-03-24 14:07   ` Daniel P. Berrangé
@ 2026-03-25  5:35   ` Markus Armbruster
  2026-03-25  6:48     ` Marc-André Lureau
  1 sibling, 1 reply; 105+ messages in thread
From: Markus Armbruster @ 2026-03-25  5:35 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Daniel P. Berrangé

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

> The text console receives bytes that may be UTF-8 encoded (e.g. from
> a guest running a modern distro), but currently treats each byte as a
> raw character index into the VGA/CP437 font, producing garbled output
> for any multi-byte sequence.
>
> Add a proper UTF-8 decoder using Bjoern Hoehrmann's DFA.
> The DFA inherently rejects overlong encodings, surrogates, and
> codepoints above U+10FFFF.  Completed codepoints are then mapped to
> CP437, unmappable characters are displayed as '?'.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Have you considered the decoder in util/unicode.c?  Do we need two
decoders, or could we replace one by the other?

There's a mad UTF-8 test suite buried in tests/unit/check-qjson.c
derived from Markus Kuhn's UTF-8 decoder capability and stress test at
<http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt>.  How does
this decoder do on these tests?



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

* Re: [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering
  2026-03-25  5:35   ` Markus Armbruster
@ 2026-03-25  6:48     ` Marc-André Lureau
  0 siblings, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-25  6:48 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Daniel P. Berrangé

HI

On Wed, Mar 25, 2026 at 9:35 AM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > The text console receives bytes that may be UTF-8 encoded (e.g. from
> > a guest running a modern distro), but currently treats each byte as a
> > raw character index into the VGA/CP437 font, producing garbled output
> > for any multi-byte sequence.
> >
> > Add a proper UTF-8 decoder using Bjoern Hoehrmann's DFA.
> > The DFA inherently rejects overlong encodings, surrogates, and
> > codepoints above U+10FFFF.  Completed codepoints are then mapped to
> > CP437, unmappable characters are displayed as '?'.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> Have you considered the decoder in util/unicode.c?  Do we need two
> decoders, or could we replace one by the other?
>

Oh! I missed it, I should definitely try to use it.

> There's a mad UTF-8 test suite buried in tests/unit/check-qjson.c
> derived from Markus Kuhn's UTF-8 decoder capability and stress test at
> <http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt>.  How does
> this decoder do on these tests?
>

According to the author original article, it should pass
https://bjoern.hoehrmann.de/utf-8/decoder/dfa/



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

* Re: [PATCH 60/60] contrib/qemu-vnc: add standalone VNC server over D-Bus
  2026-03-24 15:24   ` Daniel P. Berrangé
  2026-03-24 15:44     ` Peter Maydell
@ 2026-03-25  8:32     ` Marc-André Lureau
  1 sibling, 0 replies; 105+ messages in thread
From: Marc-André Lureau @ 2026-03-25  8:32 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel

Hi

On Tue, Mar 24, 2026 at 7:24 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Tue, Mar 17, 2026 at 12:51:14PM +0400, Marc-André Lureau wrote:
> > Add a standalone VNC server binary that connects to a running QEMU
> > instance via the D-Bus display interface (org.qemu.Display1, via the bus
> > or directly p2p). This allows serving a VNC display without compiling
> > VNC support directly into the QEMU system emulator, and enables running
> > the VNC server as a separate process with independent lifecycle and
> > privilege domain.
>
> IIUC, this new binary is linking to all the existnig ui/vnc.c
> code, so there's no actual new impl of the RFB protocol, just
> a new glue layer for launching the existing impl with comms
> over DBus instead of from direct callback functions ?
>
> > Built only when both VNC and D-Bus display support are enabled.
> > If we wanted to have qemu -vnc disabled, and qemu-vnc built, we would
> > need to split CONFIG_VNC. This is left as a future exercise.
> >
> > I left out for now:
> >  - sasl & tls authz
>
> IMHO these are must have features, as the default VNC protocol
> security is a very bad joke.

Ok that should be doable. One thing that bothers me a bit is mapping
the -object options (authz) to a more regular CLI. Perhaps I'll just
add -object args and wire it with the other options..

>
> >  - some runtime functionalities (better done by restarting)
> >  - a few legacy options
> >  - Windows support
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  MAINTAINERS                    |   5 +
> >  docs/conf.py                   |   3 +
> >  docs/interop/dbus-display.rst  |   2 +
> >  docs/interop/dbus-vnc.rst      |  26 ++
> >  docs/interop/index.rst         |   1 +
> >  docs/meson.build               |   1 +
> >  docs/tools/index.rst           |   1 +
> >  docs/tools/qemu-vnc.rst        | 199 +++++++++++
> >  meson.build                    |  17 +
> >  contrib/qemu-vnc/qemu-vnc.h    |  46 +++
> >  contrib/qemu-vnc/trace.h       |   4 +
> >  contrib/qemu-vnc/audio.c       | 307 +++++++++++++++++
> >  contrib/qemu-vnc/chardev.c     | 127 +++++++
> >  contrib/qemu-vnc/clipboard.c   | 378 +++++++++++++++++++++
> >  contrib/qemu-vnc/console.c     | 168 ++++++++++
> >  contrib/qemu-vnc/dbus.c        | 439 ++++++++++++++++++++++++
> >  contrib/qemu-vnc/display.c     | 456 +++++++++++++++++++++++++
> >  contrib/qemu-vnc/input.c       | 239 ++++++++++++++
> >  contrib/qemu-vnc/qemu-vnc.c    | 450 +++++++++++++++++++++++++
> >  contrib/qemu-vnc/stubs.c       |  66 ++++
> >  contrib/qemu-vnc/utils.c       |  59 ++++
>
> IMHO we should not really be adding more things to the contrib
> directory, and rather wish most of the existing stuff was
> not there. When I see "contrib" directories in projects, the
> message is generally "unmaintained, abandoned junk".
>
> If this is a serious maintained feature we expect people to use,
> it should be formally under the ui/ directory, possibly a
> 'ui/dbus-vnc' subdir ?

It is intended to be maintained. (although I hope we can deprecate it
in favour of qemu-rdp in the long term). I'll follow the "Cleaning up
contrib/ and tools/" thread and see where it could land.
.

>
> >  tests/qtest/dbus-vnc-test.c    | 733 +++++++++++++++++++++++++++++++++++++++++
> >  contrib/qemu-vnc/meson.build   |  26 ++
> >  contrib/qemu-vnc/qemu-vnc1.xml | 174 ++++++++++
> >  contrib/qemu-vnc/trace-events  |  20 ++
> >  meson_options.txt              |   2 +
> >  scripts/meson-buildoptions.sh  |   3 +
> >  tests/dbus-daemon.sh           |  14 +-
> >  tests/qtest/meson.build        |   8 +
> >  29 files changed, 3971 insertions(+), 3 deletions(-)
> >
>
> > diff --git a/meson_options.txt b/meson_options.txt
> > index 31d5916cfce..ef938e74793 100644
> > --- a/meson_options.txt
> > +++ b/meson_options.txt
> > @@ -119,6 +119,8 @@ option('vfio_user_server', type: 'feature', value: 'disabled',
> >         description: 'vfio-user server support')
> >  option('dbus_display', type: 'feature', value: 'auto',
> >         description: '-display dbus support')
> > +option('qemu_vnc', type: 'feature', value: 'auto',
> > +       description: 'standalone VNC server over D-Bus')
>
> Every option is about QEMU, so 'qemu_' on a name feels redundant,
> and QEMU alrady has a VNC server too.
>
> I suggest 'dbus_vnc' is possibly a better name
>

Well, I just named the option after the binary's name. I think that's easier.

>
> >  option('tpm', type : 'feature', value : 'auto',
> >         description: 'TPM support')
> >  option('valgrind', type : 'feature', value: 'auto',
>
> With regards,
> Daniel
> --
> |: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
> |: https://libvirt.org          ~~          https://entangle-photo.org :|
> |: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|
>



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

end of thread, other threads:[~2026-03-25  8:33 UTC | newest]

Thread overview: 105+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-17  8:50 [PATCH 00/60] ui: add standalone VNC server over D-Bus Marc-André Lureau
2026-03-17  8:50 ` [PATCH 01/60] ui/vnc-jobs: fix VncRectEntry leak on job cleanup Marc-André Lureau
2026-03-24 13:43   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 02/60] ui/vnc-jobs: clear source tag Marc-André Lureau
2026-03-24 13:44   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 03/60] ui/vnc-jobs: remove needless buffer_reset() before end Marc-André Lureau
2026-03-24 13:45   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 04/60] ui/vnc: clarify intent using buffer_empty() function Marc-André Lureau
2026-03-24 13:45   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 05/60] ui/vnc-jobs: vnc_has_job_locked() argument cannot be NULL Marc-André Lureau
2026-03-24 13:46   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 06/60] ui/vnc-jobs: remove dead VncJobQueue.exit Marc-André Lureau
2026-03-24 13:49   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 07/60] ui/vnc-jobs: remove vnc_queue_clear() Marc-André Lureau
2026-03-24 13:51   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 08/60] ui/vnc-jobs: narrow taking the lock when pushing empty jobs Marc-André Lureau
2026-03-24 13:53   ` Daniel P. Berrangé
2026-03-24 14:04     ` Marc-André Lureau
2026-03-17  8:50 ` [PATCH 09/60] ui/vnc-jobs: drop redundant (and needless) qemu_thread_get_self() Marc-André Lureau
2026-03-24 14:00   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 10/60] ui/console-vc: fix off-by-one in CSI J 2 (clear entire screen) Marc-André Lureau
2026-03-24 14:03   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 11/60] ui/console-vc: add UTF-8 input decoding with CP437 rendering Marc-André Lureau
2026-03-24 14:07   ` Daniel P. Berrangé
2026-03-24 14:17     ` Marc-André Lureau
2026-03-24 15:42       ` Daniel P. Berrangé
2026-03-25  5:35   ` Markus Armbruster
2026-03-25  6:48     ` Marc-André Lureau
2026-03-17  8:50 ` [PATCH 12/60] ui/console-vc: ignore string-type escape sequences Marc-André Lureau
2026-03-17  8:50 ` [PATCH 13/60] ui/console-vc: fix comment shift-out/in comments Marc-André Lureau
2026-03-24 14:11   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 14/60] ui/console: dispatch get_label() through QOM virtual method Marc-André Lureau
2026-03-24 14:14   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 15/60] ui/console-vc: introduce QemuVT100 Marc-André Lureau
2026-03-17  8:50 ` [PATCH 16/60] ui/console-vc: set vt100 associated pixman image Marc-André Lureau
2026-03-17  8:50 ` [PATCH 17/60] ui/console-vc: vga_putcharxy()->vt100_putcharxy() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 18/60] ui/console-vc: make invalidate_xy() take vt100 Marc-André Lureau
2026-03-17  8:50 ` [PATCH 19/60] ui/console-vc: make show_cursor() " Marc-André Lureau
2026-03-17  8:50 ` [PATCH 20/60] ui/console-vc: decouple VT100 display updates via function pointer Marc-André Lureau
2026-03-17  8:50 ` [PATCH 21/60] ui/console-vc: console_refresh() -> vt100_refresh() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 22/60] ui/console-vc: move cursor blinking logic into VT100 layer Marc-André Lureau
2026-03-17  8:50 ` [PATCH 23/60] ui/console-vc: console_scroll() -> vt100_scroll() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 24/60] ui/console-vc: refactor text_console_resize() into vt100_set_image() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 25/60] ui/console-vc: move vc_put_lf() to VT100 layer as vt100_put_lf() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 26/60] ui/console-vc: unify the write path Marc-André Lureau
2026-03-17  8:50 ` [PATCH 27/60] ui/console-vc: move VT100 state machine and output FIFO into QemuVT100 Marc-André Lureau
2026-03-17  8:50 ` [PATCH 28/60] ui/console-vc: extract vt100_input() from vc_chr_write() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 29/60] ui/console-vc: extract vt100_keysym() from qemu_text_console_handle_keysym() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 30/60] ui/console-vc: extract vt100_init() and vt100_fini() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 31/60] ui/console: remove console_ch_t typedef and console_write_ch() Marc-André Lureau
2026-03-17  8:50 ` [PATCH 32/60] ui: avoid duplicating vgafont16 in each translation unit Marc-André Lureau
2026-03-24 14:22   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 33/60] ui/vgafont: add SPDX license header Marc-André Lureau
2026-03-24 14:24   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 34/60] ui: move FONT_WIDTH/HEIGHT to vgafont.h Marc-André Lureau
2026-03-24 14:25   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 35/60] ui/console-vc: move VT100 emulation into separate unit Marc-André Lureau
2026-03-17  8:50 ` [PATCH 36/60] util: move datadir.c from system/ Marc-André Lureau
2026-03-24 14:27   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 37/60] ui: move DisplaySurface functions to display-surface.c Marc-André Lureau
2026-03-17  8:50 ` [PATCH 38/60] ui: make qemu_default_pixelformat() static inline Marc-André Lureau
2026-03-24 14:28   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 39/60] ui: make unregister_displaychangelistener() skip unregistered Marc-André Lureau
2026-03-24 14:28   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 40/60] ui: minor code simplification Marc-André Lureau
2026-03-24 14:30   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 41/60] system: make qemu_del_vm_change_state_handler accept NULL Marc-André Lureau
2026-03-24 14:31   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 42/60] ui/vnc: assert preconditions instead of silently returning Marc-André Lureau
2026-03-24 14:31   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 43/60] ui/vnc: simplify vnc_init_func error handling Marc-André Lureau
2026-03-24 14:38   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 44/60] ui/vnc: VncDisplay.id is not const Marc-André Lureau
2026-03-24 14:39   ` Daniel P. Berrangé
2026-03-17  8:50 ` [PATCH 45/60] ui/vnc: fix vnc_display_init() leak on failure Marc-André Lureau
2026-03-24 14:47   ` Daniel P. Berrangé
2026-03-17  8:51 ` [PATCH 46/60] ui/vnc: merge vnc_display_init() and vnc_display_open() Marc-André Lureau
2026-03-24 14:51   ` Daniel P. Berrangé
2026-03-17  8:51 ` [PATCH 47/60] ui/vnc: report an error for duplicate display id Marc-André Lureau
2026-03-24 14:52   ` Daniel P. Berrangé
2026-03-17  8:51 ` [PATCH 48/60] ui/vnc: defer listener registration until the console is known Marc-André Lureau
2026-03-24 14:53   ` Daniel P. Berrangé
2026-03-17  8:51 ` [PATCH 49/60] ui/vnc: explicitly link with png Marc-André Lureau
2026-03-24 14:56   ` Daniel P. Berrangé
2026-03-17  8:51 ` [PATCH 50/60] ui/vnc: add vnc-system unit, to allow different implementations Marc-André Lureau
2026-03-17  8:51 ` [PATCH 51/60] ui/console: remove qemu_console_is_visible() Marc-André Lureau
2026-03-24 14:57   ` Daniel P. Berrangé
2026-03-17  8:51 ` [PATCH 52/60] ui/console: simplify registering display/console change listener Marc-André Lureau
2026-03-17  8:51 ` [PATCH 53/60] ui/console: return completion status from gfx_update callback Marc-André Lureau
2026-03-17 11:43   ` BALATON Zoltan
2026-03-17  8:51 ` [PATCH 54/60] ui/console: rename public API to use consistent qemu_console_ prefix Marc-André Lureau
2026-03-17 11:46   ` BALATON Zoltan
2026-03-17  8:51 ` [PATCH 55/60] ui/console: move console_handle_touch_event() to input Marc-André Lureau
2026-03-17  8:51 ` [PATCH 56/60] ui: extract common sources into a static library Marc-André Lureau
2026-03-17  8:51 ` [PATCH 57/60] tests: rename the dbus-daemon helper script Marc-André Lureau
2026-03-24 15:05   ` Daniel P. Berrangé
2026-03-17  8:51 ` [PATCH 58/60] tests/qtest: fix dbus-vmstate-test compilation Marc-André Lureau
2026-03-17 12:28   ` Fabiano Rosas
2026-03-17 12:39     ` Marc-André Lureau
2026-03-17  8:51 ` [PATCH 59/60] tests/qtest: drop DBUS_VMSTATE_TEST_TMPDIR Marc-André Lureau
2026-03-17  8:51 ` [PATCH 60/60] contrib/qemu-vnc: add standalone VNC server over D-Bus Marc-André Lureau
2026-03-24 15:24   ` Daniel P. Berrangé
2026-03-24 15:44     ` Peter Maydell
2026-03-25  8:32     ` Marc-André Lureau
2026-03-24 17:36 ` [PATCH 00/60] ui: " Daniel P. Berrangé

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox