From 349b1881deeb28999639444df4d318787673aed9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 11 Dec 2023 17:51:20 +0900 Subject: ui/vnc: Respect bound console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ui/vnc may have a bound console so pass it to qemu_console_is_graphic() and qemu_text_console_put_keysym(). Fixes: 1d0d59fe2919 ("vnc: allow binding servers to qemu consoles") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-Id: <20231211-vnc-v1-1-a3551d284809@daynix.com> --- ui/vnc.c | 59 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index af20d24534..fc12b343e2 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1931,7 +1931,8 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } qkbd_state_key_event(vs->vd->kbd, qcode, down); - if (!qemu_console_is_graphic(NULL)) { + if (!qemu_console_is_graphic(vs->vd->dcl.con)) { + QemuTextConsole *con = QEMU_TEXT_CONSOLE(vs->vd->dcl.con); bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK); bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL); /* QEMU console emulation */ @@ -1945,88 +1946,88 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) case 0xb8: /* Right ALT */ break; case 0xc8: - qemu_text_console_put_keysym(NULL, QEMU_KEY_UP); + qemu_text_console_put_keysym(con, QEMU_KEY_UP); break; case 0xd0: - qemu_text_console_put_keysym(NULL, QEMU_KEY_DOWN); + qemu_text_console_put_keysym(con, QEMU_KEY_DOWN); break; case 0xcb: - qemu_text_console_put_keysym(NULL, QEMU_KEY_LEFT); + qemu_text_console_put_keysym(con, QEMU_KEY_LEFT); break; case 0xcd: - qemu_text_console_put_keysym(NULL, QEMU_KEY_RIGHT); + qemu_text_console_put_keysym(con, QEMU_KEY_RIGHT); break; case 0xd3: - qemu_text_console_put_keysym(NULL, QEMU_KEY_DELETE); + qemu_text_console_put_keysym(con, QEMU_KEY_DELETE); break; case 0xc7: - qemu_text_console_put_keysym(NULL, QEMU_KEY_HOME); + qemu_text_console_put_keysym(con, QEMU_KEY_HOME); break; case 0xcf: - qemu_text_console_put_keysym(NULL, QEMU_KEY_END); + qemu_text_console_put_keysym(con, QEMU_KEY_END); break; case 0xc9: - qemu_text_console_put_keysym(NULL, QEMU_KEY_PAGEUP); + qemu_text_console_put_keysym(con, QEMU_KEY_PAGEUP); break; case 0xd1: - qemu_text_console_put_keysym(NULL, QEMU_KEY_PAGEDOWN); + qemu_text_console_put_keysym(con, QEMU_KEY_PAGEDOWN); break; case 0x47: - qemu_text_console_put_keysym(NULL, numlock ? '7' : QEMU_KEY_HOME); + qemu_text_console_put_keysym(con, numlock ? '7' : QEMU_KEY_HOME); break; case 0x48: - qemu_text_console_put_keysym(NULL, numlock ? '8' : QEMU_KEY_UP); + qemu_text_console_put_keysym(con, numlock ? '8' : QEMU_KEY_UP); break; case 0x49: - qemu_text_console_put_keysym(NULL, numlock ? '9' : QEMU_KEY_PAGEUP); + qemu_text_console_put_keysym(con, numlock ? '9' : QEMU_KEY_PAGEUP); break; case 0x4b: - qemu_text_console_put_keysym(NULL, numlock ? '4' : QEMU_KEY_LEFT); + qemu_text_console_put_keysym(con, numlock ? '4' : QEMU_KEY_LEFT); break; case 0x4c: - qemu_text_console_put_keysym(NULL, '5'); + qemu_text_console_put_keysym(con, '5'); break; case 0x4d: - qemu_text_console_put_keysym(NULL, numlock ? '6' : QEMU_KEY_RIGHT); + qemu_text_console_put_keysym(con, numlock ? '6' : QEMU_KEY_RIGHT); break; case 0x4f: - qemu_text_console_put_keysym(NULL, numlock ? '1' : QEMU_KEY_END); + qemu_text_console_put_keysym(con, numlock ? '1' : QEMU_KEY_END); break; case 0x50: - qemu_text_console_put_keysym(NULL, numlock ? '2' : QEMU_KEY_DOWN); + qemu_text_console_put_keysym(con, numlock ? '2' : QEMU_KEY_DOWN); break; case 0x51: - qemu_text_console_put_keysym(NULL, numlock ? '3' : QEMU_KEY_PAGEDOWN); + qemu_text_console_put_keysym(con, numlock ? '3' : QEMU_KEY_PAGEDOWN); break; case 0x52: - qemu_text_console_put_keysym(NULL, '0'); + qemu_text_console_put_keysym(con, '0'); break; case 0x53: - qemu_text_console_put_keysym(NULL, numlock ? '.' : QEMU_KEY_DELETE); + qemu_text_console_put_keysym(con, numlock ? '.' : QEMU_KEY_DELETE); break; case 0xb5: - qemu_text_console_put_keysym(NULL, '/'); + qemu_text_console_put_keysym(con, '/'); break; case 0x37: - qemu_text_console_put_keysym(NULL, '*'); + qemu_text_console_put_keysym(con, '*'); break; case 0x4a: - qemu_text_console_put_keysym(NULL, '-'); + qemu_text_console_put_keysym(con, '-'); break; case 0x4e: - qemu_text_console_put_keysym(NULL, '+'); + qemu_text_console_put_keysym(con, '+'); break; case 0x9c: - qemu_text_console_put_keysym(NULL, '\n'); + qemu_text_console_put_keysym(con, '\n'); break; default: if (control) { - qemu_text_console_put_keysym(NULL, sym & 0x1f); + qemu_text_console_put_keysym(con, sym & 0x1f); } else { - qemu_text_console_put_keysym(NULL, sym); + qemu_text_console_put_keysym(con, sym); } break; } @@ -2044,7 +2045,7 @@ static void key_event(VncState *vs, int down, uint32_t sym) int keycode; int lsym = sym; - if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(NULL)) { + if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(vs->vd->dcl.con)) { lsym = lsym - 'A' + 'a'; } -- cgit v1.2.3 From 7945576cf298efb90a03b3e74912e3fe5952db56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 7 Mar 2024 18:04:31 +0400 Subject: ui/dbus: factor out sending a scanout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- ui/dbus-listener.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 18f556aa73..3f4529dbbd 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -630,11 +630,26 @@ static void dbus_gfx_update_sub(DBusDisplayListener *ddl, DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); } +static void ddl_scanout(DBusDisplayListener *ddl) +{ + GVariant *v_data; + + v_data = g_variant_new_from_data( + G_VARIANT_TYPE("ay"), surface_data(ddl->ds), + surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE, + (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image)); + + qemu_dbus_display1_listener_call_scanout( + ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds), + surface_stride(ddl->ds), surface_format(ddl->ds), v_data, + G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL, + g_object_ref(ddl)); +} + static void dbus_gfx_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - GVariant *v_data; assert(ddl->ds); @@ -652,23 +667,7 @@ static void dbus_gfx_update(DisplayChangeListener *dcl, #endif if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { - v_data = g_variant_new_from_data( - G_VARIANT_TYPE("ay"), - surface_data(ddl->ds), - surface_stride(ddl->ds) * surface_height(ddl->ds), - TRUE, - (GDestroyNotify)pixman_image_unref, - pixman_image_ref(ddl->ds->image)); - qemu_dbus_display1_listener_call_scanout( - ddl->proxy, - surface_width(ddl->ds), - surface_height(ddl->ds), - surface_stride(ddl->ds), - surface_format(ddl->ds), - v_data, - G_DBUS_CALL_FLAGS_NONE, - DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); - return; + return ddl_scanout(ddl); } dbus_gfx_update_sub(ddl, x, y, w, h); -- cgit v1.2.3 From fa88b85dea96ebad8d4380a3dee2a9e6e40c218c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 7 Mar 2024 18:21:13 +0400 Subject: ui/dbus: filter out pending messages when scanout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Listener" connection, being private and under the control of the qemu display, allows for the optimization of discarding pending intermediary messages when queuing a new scanout. This ensures that the client receives only the latest scanout update, improving communication efficiency. While the current implementation does not provide a mechanism for clients who may wish to receive all updates, making this behavior optional could be considered in the future. For now, adopting this new default behavior accelerates the communication process without a guarantee of delivering all updates. The filter is removed when the connection is dropped. Signed-off-by: Marc-André Lureau --- ui/dbus-listener.c | 40 ++++++++++++++++++++++++++++++++++++++++ ui/trace-events | 1 + 2 files changed, 41 insertions(+) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 3f4529dbbd..4a0a5d78f9 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -83,6 +83,9 @@ struct _DBusDisplayListener { egl_fb fb; #endif #endif + + guint dbus_filter; + guint32 out_serial_to_discard; }; G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) @@ -90,6 +93,12 @@ G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) static void dbus_gfx_update(DisplayChangeListener *dcl, int x, int y, int w, int h); +static void ddl_discard_pending_messages(DBusDisplayListener *ddl) +{ + ddl->out_serial_to_discard = g_dbus_connection_get_last_serial( + g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); +} + #ifdef CONFIG_OPENGL static void dbus_scanout_disable(DisplayChangeListener *dcl) { @@ -276,6 +285,8 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, return; } + ddl_discard_pending_messages(ddl); + /* FIXME: add missing x/y/w/h support */ qemu_dbus_display1_listener_call_scanout_dmabuf( ddl->proxy, @@ -323,6 +334,8 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl) return false; } + ddl_discard_pending_messages(ddl); + if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync( ddl->map_proxy, GPOINTER_TO_UINT(target_handle), @@ -384,6 +397,8 @@ dbus_scanout_share_d3d_texture( return false; } + ddl_discard_pending_messages(ddl); + qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d( ddl->d3d11_proxy, GPOINTER_TO_INT(target_handle), @@ -639,6 +654,8 @@ static void ddl_scanout(DBusDisplayListener *ddl) surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE, (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image)); + ddl_discard_pending_messages(ddl); + qemu_dbus_display1_listener_call_scanout( ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds), surface_stride(ddl->ds), surface_format(ddl->ds), v_data, @@ -963,6 +980,28 @@ dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) #endif } +static GDBusMessage * +dbus_filter(GDBusConnection *connection, + GDBusMessage *message, + gboolean incoming, + gpointer user_data) +{ + DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data); + guint32 serial; + + if (incoming) { + return message; + } + + serial = g_dbus_message_get_serial(message); + if (serial <= ddl->out_serial_to_discard) { + trace_dbus_filter(serial, ddl->out_serial_to_discard); + return NULL; + } + + return message; +} + DBusDisplayListener * dbus_display_listener_new(const char *bus_name, GDBusConnection *conn, @@ -987,6 +1026,7 @@ dbus_display_listener_new(const char *bus_name, return NULL; } + ddl->dbus_filter = g_dbus_connection_add_filter(conn, dbus_filter, g_object_ref(ddl), g_object_unref); ddl->bus_name = g_strdup(bus_name); ddl->conn = conn; ddl->console = console; diff --git a/ui/trace-events b/ui/trace-events index 16c35c9fd6..e6a2894303 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -161,6 +161,7 @@ dbus_clipboard_register(const char *bus_name) "peer %s" dbus_clipboard_unregister(const char *bus_name) "peer %s" dbus_scanout_texture(uint32_t tex_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "tex_id:%u y0top:%d back:%ux%u %u+%u-%ux%u" dbus_gl_gfx_switch(void *p) "surf: %p" +dbus_filter(unsigned int serial, unsigned int filter) "serial=%u (<= %u)" # egl-helpers.c egl_init_d3d11_device(void *p) "d3d device: %p" -- cgit v1.2.3 From cab47b210598c11b76053a01316df9835b94dc09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 15 Jan 2024 19:10:56 +0400 Subject: virtio-gpu: remove needless condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_create_displaysurface_pixman() never returns NULL. Signed-off-by: Marc-André Lureau --- hw/display/virtio-gpu.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 1c1ee230b3..ccbe31d759 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -684,10 +684,6 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, /* realloc the surface ptr */ scanout->ds = qemu_create_displaysurface_pixman(rect); - if (!scanout->ds) { - *error = VIRTIO_GPU_RESP_ERR_UNSPEC; - return; - } #ifdef WIN32 qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset); #endif @@ -1423,9 +1419,6 @@ static int virtio_gpu_post_load(void *opaque, int version_id) return -EINVAL; } scanout->ds = qemu_create_displaysurface_pixman(res->image); - if (!scanout->ds) { - return -EINVAL; - } #ifdef WIN32 qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0); #endif -- cgit v1.2.3 From dfcf74fa68c88233209aafc5f82728d0b9a1af5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 15 Jan 2024 16:34:33 +0400 Subject: virtio-gpu: fix scanout migration post-load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current post-loading code for scanout has a FIXME: it doesn't take the resource region/rect into account. But there is more, when adding blob migration support in commit f66767f75c9, I didn't realize that blob resources could be used for scanouts. This situationn leads to a crash during post-load, as they don't have an associated res->image. virtio_gpu_do_set_scanout() handle all cases, but requires the associated virtio_gpu_framebuffer, which is currently not saved during migration. Add a v2 of "virtio-gpu-one-scanout" with the framebuffer fields, so we can restore blob scanouts, as well as fixing the existing FIXME. Signed-off-by: Marc-André Lureau Reviewed-by: Sebastian Ott --- hw/display/virtio-gpu.c | 51 +++++++++++++++++++++++++++++++++--------- include/hw/virtio/virtio-gpu.h | 1 + 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index ccbe31d759..78d5a4f164 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -600,6 +600,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data) static void virtio_gpu_update_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_simple_resource *res, + struct virtio_gpu_framebuffer *fb, struct virtio_gpu_rect *r) { struct virtio_gpu_simple_resource *ores; @@ -617,9 +618,10 @@ static void virtio_gpu_update_scanout(VirtIOGPU *g, scanout->y = r->y; scanout->width = r->width; scanout->height = r->height; + scanout->fb = *fb; } -static void virtio_gpu_do_set_scanout(VirtIOGPU *g, +static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_framebuffer *fb, struct virtio_gpu_simple_resource *res, @@ -645,7 +647,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, r->x, r->y, r->width, r->height, fb->width, fb->height); *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; - return; + return false; } g->parent_obj.enable = 1; @@ -653,11 +655,12 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, if (res->blob) { if (console_has_gl(scanout->con)) { if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) { - virtio_gpu_update_scanout(g, scanout_id, res, r); + virtio_gpu_update_scanout(g, scanout_id, res, fb, r); } else { *error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return false; } - return; + return true; } data = res->blob; @@ -693,7 +696,8 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, scanout->ds); } - virtio_gpu_update_scanout(g, scanout_id, res, r); + virtio_gpu_update_scanout(g, scanout_id, res, fb, r); + return true; } static void virtio_gpu_set_scanout(VirtIOGPU *g, @@ -1164,7 +1168,8 @@ static void virtio_gpu_cursor_bh(void *opaque) static const VMStateDescription vmstate_virtio_gpu_scanout = { .name = "virtio-gpu-one-scanout", - .version_id = 1, + .version_id = 2, + .minimum_version_id = 1, .fields = (const VMStateField[]) { VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout), VMSTATE_UINT32(width, struct virtio_gpu_scanout), @@ -1176,6 +1181,12 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = { VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout), VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout), VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), + VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2), VMSTATE_END_OF_LIST() }, }; @@ -1347,6 +1358,7 @@ static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size, if (!res->blob_size) { continue; } + assert(!res->image); qemu_put_be32(f, res->resource_id); qemu_put_be32(f, res->blob_size); qemu_put_be32(f, res->iov_cnt); @@ -1409,21 +1421,40 @@ static int virtio_gpu_post_load(void *opaque, int version_id) int i; for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { - /* FIXME: should take scanout.r.{x,y} into account */ scanout = &g->parent_obj.scanout[i]; if (!scanout->resource_id) { continue; } + res = virtio_gpu_find_resource(g, scanout->resource_id); if (!res) { return -EINVAL; } - scanout->ds = qemu_create_displaysurface_pixman(res->image); + + if (scanout->fb.format != 0) { + uint32_t error = 0; + struct virtio_gpu_rect r = { + .x = scanout->x, + .y = scanout->y, + .width = scanout->width, + .height = scanout->height + }; + + if (!virtio_gpu_do_set_scanout(g, i, &scanout->fb, res, &r, &error)) { + return -EINVAL; + } + } else { + /* legacy v1 migration support */ + if (!res->image) { + return -EINVAL; + } + scanout->ds = qemu_create_displaysurface_pixman(res->image); #ifdef WIN32 - qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0); + qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0); #endif + dpy_gfx_replace_surface(scanout->con, scanout->ds); + } - dpy_gfx_replace_surface(scanout->con, scanout->ds); dpy_gfx_update_full(scanout->con); if (scanout->cursor.resource_id) { update_cursor(g, &scanout->cursor); diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index b28e7ef0d2..ed44cdad6b 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -81,6 +81,7 @@ struct virtio_gpu_scanout { uint32_t resource_id; struct virtio_gpu_update_cursor cursor; QEMUCursor *current_cursor; + struct virtio_gpu_framebuffer fb; }; struct virtio_gpu_requested_state { -- cgit v1.2.3