:p
atchew
Login
From: Marc-André Lureau <marcandre.lureau@redhat.com> The following changes since commit 9a4e273ddec3927920c5958d2226c6b38b543336: Merge tag 'pull-tcg-20250711' of https://gitlab.com/rth7680/qemu into staging (2025-07-13 01:46:04 -0400) are available in the Git repository at: https://gitlab.com/marcandre.lureau/qemu.git tags/ui-pull-request for you to fetch changes up to c99b7e6d4aa8bcc12d47483ebe81072168de56fb: tpm: "qemu -tpmdev help" should return success (2025-07-14 15:02:00 +0400) ---------------------------------------------------------------- UI-related for 10.1 - [PATCH v3 0/2] ui/vnc: Do not copy z_stream - [PATCH v6 0/7] ui/spice: Enable gl=on option for non-local or remote clients - [PATCH v6 0/1] Allow injection of virtio-gpu EDID name - [PATCH 0/2] ui/gtk: Add keep-aspect-ratio and scale option ---------------------------------------------------------------- Akihiko Odaki (2): ui/vnc: Do not copy z_stream ui/vnc: Introduce the VncWorker type Andrew Keesler (1): hw/display: Allow injection of virtio-gpu EDID name Marc-André Lureau (1): tpm: "qemu -tpmdev help" should return success Vivek Kasireddy (7): ui/egl-helpers: Error check the fds in egl_dmabuf_export_texture() ui/spice: Enable gl=on option for non-local or remote clients ui/spice: Add an option for users to provide a preferred video codec ui/spice: Add an option to submit gl_draw requests at fixed rate ui/console-gl: Add a helper to create a texture with linear memory layout ui/spice: Create a new texture with linear layout when gl=on is specified ui/spice: Blit the scanout texture if its memory layout is not linear Weifeng Liu (2): ui/gtk: Add keep-aspect-ratio option ui/gtk: Add scale option qapi/ui.json | 15 +- qapi/virtio.json | 18 +- include/hw/display/edid.h | 2 + include/hw/qdev-properties-system.h | 5 + include/hw/virtio/virtio-gpu.h | 3 + include/ui/console.h | 3 + include/ui/gtk.h | 2 + include/ui/spice-display.h | 5 + include/ui/surface.h | 1 + ui/vnc.h | 49 +-- hw/core/qdev-properties-system.c | 44 +++ hw/display/virtio-gpu-base.c | 27 ++ system/tpm.c | 5 +- ui/console-gl.c | 54 ++++ ui/egl-helpers.c | 6 + ui/gtk.c | 58 ++-- ui/spice-core.c | 31 ++ ui/spice-display.c | 226 +++++++++++++- ui/vnc-enc-tight.c | 456 +++++++++++++++------------- ui/vnc-enc-zlib.c | 47 +-- ui/vnc-enc-zrle.c | 122 ++++---- ui/vnc-jobs.c | 13 +- ui/vnc.c | 83 +++-- ui/vnc-enc-zrle.c.inc | 20 +- qemu-options.hx | 13 + 25 files changed, 882 insertions(+), 426 deletions(-) -- 2.50.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> vnc_worker_thread_loop() copies z_stream stored in its local VncState to the persistent VncState, and the copied one is freed with deflateEnd() later. However, deflateEnd() refuses to operate with a copied z_stream and returns Z_STREAM_ERROR, leaking the allocated memory. Avoid copying the zlib state to fix the memory leak. Fixes: bd023f953e5e ("vnc: threaded VNC server") Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20250603-zlib-v3-1-20b857bd8d05@rsg.ci.i.u-tokyo.ac.jp> --- ui/vnc.h | 2 +- ui/vnc-enc-zlib.c | 30 +++++++++++++++--------------- ui/vnc.c | 13 ++++++++++--- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/ui/vnc.h b/ui/vnc.h index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -XXX,XX +XXX,XX @@ struct VncState * update vnc_async_encoding_start() */ VncTight *tight; - VncZlib zlib; + VncZlib *zlib; VncHextile hextile; VncZrle *zrle; VncZywrle zywrle; diff --git a/ui/vnc-enc-zlib.c b/ui/vnc-enc-zlib.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc-enc-zlib.c +++ b/ui/vnc-enc-zlib.c @@ -XXX,XX +XXX,XX @@ void vnc_zlib_zfree(void *x, void *addr) static void vnc_zlib_start(VncState *vs) { - buffer_reset(&vs->zlib.zlib); + buffer_reset(&vs->zlib->zlib); // make the output buffer be the zlib buffer, so we can compress it later - vs->zlib.tmp = vs->output; - vs->output = vs->zlib.zlib; + vs->zlib->tmp = vs->output; + vs->output = vs->zlib->zlib; } static int vnc_zlib_stop(VncState *vs) { - z_streamp zstream = &vs->zlib.stream; + z_streamp zstream = &vs->zlib->stream; int previous_out; // switch back to normal output/zlib buffers - vs->zlib.zlib = vs->output; - vs->output = vs->zlib.tmp; + vs->zlib->zlib = vs->output; + vs->output = vs->zlib->tmp; // compress the zlib buffer @@ -XXX,XX +XXX,XX @@ static int vnc_zlib_stop(VncState *vs) return -1; } - vs->zlib.level = vs->tight->compression; + vs->zlib->level = vs->tight->compression; zstream->opaque = vs; } - if (vs->tight->compression != vs->zlib.level) { + if (vs->tight->compression != vs->zlib->level) { if (deflateParams(zstream, vs->tight->compression, Z_DEFAULT_STRATEGY) != Z_OK) { return -1; } - vs->zlib.level = vs->tight->compression; + vs->zlib->level = vs->tight->compression; } // reserve memory in output buffer - buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64); + buffer_reserve(&vs->output, vs->zlib->zlib.offset + 64); // set pointers - zstream->next_in = vs->zlib.zlib.buffer; - zstream->avail_in = vs->zlib.zlib.offset; + zstream->next_in = vs->zlib->zlib.buffer; + zstream->avail_in = vs->zlib->zlib.offset; zstream->next_out = vs->output.buffer + vs->output.offset; zstream->avail_out = vs->output.capacity - vs->output.offset; previous_out = zstream->avail_out; @@ -XXX,XX +XXX,XX @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) void vnc_zlib_clear(VncState *vs) { - if (vs->zlib.stream.opaque) { - deflateEnd(&vs->zlib.stream); + if (vs->zlib->stream.opaque) { + deflateEnd(&vs->zlib->stream); } - buffer_free(&vs->zlib.zlib); + buffer_free(&vs->zlib->zlib); } diff --git a/ui/vnc.c b/ui/vnc.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -XXX,XX +XXX,XX @@ #include "io/dns-resolver.h" #include "monitor/monitor.h" +typedef struct VncConnection { + VncState vs; + VncZlib zlib; +} VncConnection; + #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 #define VNC_REFRESH_INTERVAL_MAX GUI_REFRESH_INTERVAL_IDLE @@ -XXX,XX +XXX,XX @@ void vnc_disconnect_finish(VncState *vs) vs->magic = 0; g_free(vs->zrle); g_free(vs->tight); - g_free(vs); + g_free(container_of(vs, VncConnection, vs)); } size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err) @@ -XXX,XX +XXX,XX @@ static void vnc_refresh(DisplayChangeListener *dcl) static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, bool skipauth, bool websocket) { - VncState *vs = g_new0(VncState, 1); + VncConnection *vc = g_new0(VncConnection, 1); + VncState *vs = &vc->vs; bool first_client = QTAILQ_EMPTY(&vd->clients); int i; trace_vnc_client_connect(vs, sioc); + vs->zlib = &vc->zlib; vs->zrle = g_new0(VncZrle, 1); vs->tight = g_new0(VncTight, 1); vs->magic = VNC_MAGIC; @@ -XXX,XX +XXX,XX @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, #ifdef CONFIG_PNG buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc); #endif - buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc); + buffer_init(&vc->zlib.zlib, "vnc-zlib/%p", sioc); buffer_init(&vs->zrle->zrle, "vnc-zrle/%p", sioc); buffer_init(&vs->zrle->fb, "vnc-zrle-fb/%p", sioc); buffer_init(&vs->zrle->zlib, "vnc-zrle-zlib/%p", sioc); -- 2.50.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> The worker thread copies data in VncState to avoid race, but some data are too big to copy. Such data are held with pointers to avoid the overhead to copy, but it requires tedious memory management and makes them vulnerable to race. Introduce the VncWorker type to contain all data shared without copying. It allows allocating and freeing all shared data at once and shows that the race with the worker thread needs to be taken care of when accessing them. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20250603-zlib-v3-2-20b857bd8d05@rsg.ci.i.u-tokyo.ac.jp> --- ui/vnc.h | 49 +++-- ui/vnc-enc-tight.c | 456 ++++++++++++++++++++++-------------------- ui/vnc-enc-zlib.c | 47 ++--- ui/vnc-enc-zrle.c | 122 +++++------ ui/vnc-jobs.c | 13 +- ui/vnc.c | 86 ++++---- ui/vnc-enc-zrle.c.inc | 20 +- 7 files changed, 405 insertions(+), 388 deletions(-) diff --git a/ui/vnc.h b/ui/vnc.h index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -XXX,XX +XXX,XX @@ struct VncState gboolean disconnecting; DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS); - uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in - * vnc-jobs-async.c */ VncDisplay *vd; VncStateUpdate update; /* Most recent pending request from client */ @@ -XXX,XX +XXX,XX @@ struct VncState /* Encoding specific, if you add something here, don't forget to * update vnc_async_encoding_start() */ - VncTight *tight; - VncZlib *zlib; VncHextile hextile; - VncZrle *zrle; VncZywrle zywrle; Notifier mouse_mode_notifier; @@ -XXX,XX +XXX,XX @@ struct VncState QTAILQ_ENTRY(VncState) next; }; +typedef struct VncWorker { + uint8_t lossy_rect[VNC_STAT_ROWS][VNC_STAT_COLS]; + + VncTight tight; + VncZlib zlib; + VncZrle zrle; +} VncWorker; + +typedef struct VncConnection { + VncState vs; + VncWorker worker; +} VncConnection; + /***************************************************************************** * @@ -XXX,XX +XXX,XX @@ int vnc_server_fb_stride(VncDisplay *vd); void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v); double vnc_update_freq(VncState *vs, int x, int y, int w, int h); -void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h); +void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h); /* Encodings */ -int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +int vnc_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); @@ -XXX,XX +XXX,XX @@ void vnc_hextile_set_pixel_conversion(VncState *vs, int generic); void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size); void vnc_zlib_zfree(void *x, void *addr); -int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); -void vnc_zlib_clear(VncState *vs); - -int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); -int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h); -void vnc_tight_clear(VncState *vs); - -int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); -int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); -void vnc_zrle_clear(VncState *vs); +int vnc_zlib_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +void vnc_zlib_clear(VncWorker *worker); + +int vnc_tight_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +int vnc_tight_png_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +void vnc_tight_clear(VncWorker *worker); + +int vnc_zrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +int vnc_zywrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +void vnc_zrle_clear(VncWorker *worker); /* vnc-clipboard.c */ void vnc_server_cut_text_caps(VncState *vs); diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -XXX,XX +XXX,XX @@ static const struct { }; -static int tight_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h); +static int tight_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); #ifdef CONFIG_VNC_JPEG static const struct { @@ -XXX,XX +XXX,XX @@ static const struct { { 9, PNG_ALL_FILTERS }, }; -static int send_png_rect(VncState *vs, int x, int y, int w, int h, - VncPalette *palette); +static int send_png_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, VncPalette *palette); -static bool tight_can_send_png_rect(VncState *vs, int w, int h) +static bool tight_can_send_png_rect(VncState *vs, VncTight *tight, int w, int h) { - if (vs->tight->type != VNC_ENCODING_TIGHT_PNG) { + if (tight->type != VNC_ENCODING_TIGHT_PNG) { return false; } @@ -XXX,XX +XXX,XX @@ static bool tight_can_send_png_rect(VncState *vs, int w, int h) */ static unsigned int -tight_detect_smooth_image24(VncState *vs, int w, int h) +tight_detect_smooth_image24(VncState *vs, VncTight *tight, int w, int h) { int off; int x, y, d, dx; @@ -XXX,XX +XXX,XX @@ tight_detect_smooth_image24(VncState *vs, int w, int h) int pixels = 0; int pix, left[3]; unsigned int errors; - unsigned char *buf = vs->tight->tight.buffer; + unsigned char *buf = tight->tight.buffer; /* * If client is big-endian, color samples begin from the second @@ -XXX,XX +XXX,XX @@ tight_detect_smooth_image24(VncState *vs, int w, int h) #define DEFINE_DETECT_FUNCTION(bpp) \ \ static unsigned int \ - tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \ + tight_detect_smooth_image##bpp(VncState *vs, VncTight *tight, \ + int w, int h) { \ bool endian; \ uint##bpp##_t pix; \ int max[3], shift[3]; \ @@ -XXX,XX +XXX,XX @@ tight_detect_smooth_image24(VncState *vs, int w, int h) int pixels = 0; \ int sample, sum, left[3]; \ unsigned int errors; \ - unsigned char *buf = vs->tight->tight.buffer; \ + unsigned char *buf = tight->tight.buffer; \ \ endian = 0; /* FIXME */ \ \ @@ -XXX,XX +XXX,XX @@ DEFINE_DETECT_FUNCTION(16) DEFINE_DETECT_FUNCTION(32) static int -tight_detect_smooth_image(VncState *vs, int w, int h) +tight_detect_smooth_image(VncState *vs, VncTight *tight, int w, int h) { unsigned int errors; - int compression = vs->tight->compression; - int quality = vs->tight->quality; + int compression = tight->compression; + int quality = tight->quality; if (!vs->vd->lossy) { return 0; @@ -XXX,XX +XXX,XX @@ tight_detect_smooth_image(VncState *vs, int w, int h) return 0; } - if (vs->tight->quality != (uint8_t)-1) { + if (tight->quality != (uint8_t)-1) { if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) { return 0; } @@ -XXX,XX +XXX,XX @@ tight_detect_smooth_image(VncState *vs, int w, int h) } if (vs->client_pf.bytes_per_pixel == 4) { - if (vs->tight->pixel24) { - errors = tight_detect_smooth_image24(vs, w, h); - if (vs->tight->quality != (uint8_t)-1) { + if (tight->pixel24) { + errors = tight_detect_smooth_image24(vs, tight, w, h); + if (tight->quality != (uint8_t)-1) { return (errors < tight_conf[quality].jpeg_threshold24); } return (errors < tight_conf[compression].gradient_threshold24); } else { - errors = tight_detect_smooth_image32(vs, w, h); + errors = tight_detect_smooth_image32(vs, tight, w, h); } } else { - errors = tight_detect_smooth_image16(vs, w, h); + errors = tight_detect_smooth_image16(vs, tight, w, h); } if (quality != (uint8_t)-1) { return (errors < tight_conf[quality].jpeg_threshold); @@ -XXX,XX +XXX,XX @@ tight_detect_smooth_image(VncState *vs, int w, int h) #define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ \ static int \ - tight_fill_palette##bpp(VncState *vs, int x, int y, \ - int max, size_t count, \ + tight_fill_palette##bpp(VncState *vs, VncTight *tight, \ + int x, int y, int max, size_t count, \ uint32_t *bg, uint32_t *fg, \ VncPalette *palette) { \ uint##bpp##_t *data; \ uint##bpp##_t c0, c1, ci; \ int i, n0, n1; \ \ - data = (uint##bpp##_t *)vs->tight->tight.buffer; \ + data = (uint##bpp##_t *)tight->tight.buffer; \ \ c0 = data[0]; \ i = 1; \ @@ -XXX,XX +XXX,XX @@ DEFINE_FILL_PALETTE_FUNCTION(8) DEFINE_FILL_PALETTE_FUNCTION(16) DEFINE_FILL_PALETTE_FUNCTION(32) -static int tight_fill_palette(VncState *vs, int x, int y, +static int tight_fill_palette(VncState *vs, VncTight *tight, int x, int y, size_t count, uint32_t *bg, uint32_t *fg, VncPalette *palette) { int max; - max = count / tight_conf[vs->tight->compression].idx_max_colors_divisor; + max = count / tight_conf[tight->compression].idx_max_colors_divisor; if (max < 2 && - count >= tight_conf[vs->tight->compression].mono_min_rect_size) { + count >= tight_conf[tight->compression].mono_min_rect_size) { max = 2; } if (max >= 256) { @@ -XXX,XX +XXX,XX @@ static int tight_fill_palette(VncState *vs, int x, int y, switch (vs->client_pf.bytes_per_pixel) { case 4: - return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette); + return tight_fill_palette32(vs, tight, x, y, max, count, bg, fg, + palette); case 2: - return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette); + return tight_fill_palette16(vs, tight, x, y, max, count, bg, fg, + palette); default: max = 2; - return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette); + return tight_fill_palette8(vs, tight, x, y, max, count, bg, fg, + palette); } return 0; } @@ -XXX,XX +XXX,XX @@ DEFINE_MONO_ENCODE_FUNCTION(32) */ static void -tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) +tight_filter_gradient24(VncState *vs, VncTight *tight, uint8_t *buf, + int w, int h) { uint32_t *buf32; uint32_t pix32; @@ -XXX,XX +XXX,XX @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) int x, y, c; buf32 = (uint32_t *)buf; - memset(vs->tight->gradient.buffer, 0, w * 3 * sizeof(int)); + memset(tight->gradient.buffer, 0, w * 3 * sizeof(int)); if (1 /* FIXME */) { shift[0] = vs->client_pf.rshift; @@ -XXX,XX +XXX,XX @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) upper[c] = 0; here[c] = 0; } - prev = (int *)vs->tight->gradient.buffer; + prev = (int *)tight->gradient.buffer; for (x = 0; x < w; x++) { pix32 = *buf32++; for (c = 0; c < 3; c++) { @@ -XXX,XX +XXX,XX @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) #define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ \ static void \ - tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \ - int w, int h) { \ + tight_filter_gradient##bpp(VncState *vs, VncTight *tight, \ + uint##bpp##_t *buf, int w, int h) { \ uint##bpp##_t pix, diff; \ bool endian; \ int *prev; \ @@ -XXX,XX +XXX,XX @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) int prediction; \ int x, y, c; \ \ - memset(vs->tight->gradient.buffer, 0, w * 3 * sizeof(int)); \ + memset(tight->gradient.buffer, 0, w * 3 * sizeof(int)); \ \ endian = 0; /* FIXME */ \ \ @@ -XXX,XX +XXX,XX @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) upper[c] = 0; \ here[c] = 0; \ } \ - prev = (int *)vs->tight->gradient.buffer; \ + prev = (int *)tight->gradient.buffer; \ for (x = 0; x < w; x++) { \ pix = *buf; \ if (endian) { \ @@ -XXX,XX +XXX,XX @@ static void extend_solid_area(VncState *vs, int x, int y, int w, int h, *w_ptr += cx - (*x_ptr + *w_ptr); } -static int tight_init_stream(VncState *vs, int stream_id, +static int tight_init_stream(VncState *vs, VncTight *tight, int stream_id, int level, int strategy) { - z_streamp zstream = &vs->tight->stream[stream_id]; + z_streamp zstream = &tight->stream[stream_id]; if (zstream->opaque == NULL) { int err; @@ -XXX,XX +XXX,XX @@ static int tight_init_stream(VncState *vs, int stream_id, return -1; } - vs->tight->levels[stream_id] = level; + tight->levels[stream_id] = level; zstream->opaque = vs; } - if (vs->tight->levels[stream_id] != level) { + if (tight->levels[stream_id] != level) { if (deflateParams(zstream, level, strategy) != Z_OK) { return -1; } - vs->tight->levels[stream_id] = level; + tight->levels[stream_id] = level; } return 0; } @@ -XXX,XX +XXX,XX @@ static void tight_send_compact_size(VncState *vs, size_t len) } } -static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, - int level, int strategy) +static int tight_compress_data(VncState *vs, VncTight *tight, int stream_id, + size_t bytes, int level, int strategy) { - z_streamp zstream = &vs->tight->stream[stream_id]; + z_streamp zstream = &tight->stream[stream_id]; int previous_out; if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) { - vnc_write(vs, vs->tight->tight.buffer, vs->tight->tight.offset); + vnc_write(vs, tight->tight.buffer, tight->tight.offset); return bytes; } - if (tight_init_stream(vs, stream_id, level, strategy)) { + if (tight_init_stream(vs, tight, stream_id, level, strategy)) { return -1; } /* reserve memory in output buffer */ - buffer_reserve(&vs->tight->zlib, bytes + 64); + buffer_reserve(&tight->zlib, bytes + 64); /* set pointers */ - zstream->next_in = vs->tight->tight.buffer; - zstream->avail_in = vs->tight->tight.offset; - zstream->next_out = vs->tight->zlib.buffer + vs->tight->zlib.offset; - zstream->avail_out = vs->tight->zlib.capacity - vs->tight->zlib.offset; + zstream->next_in = tight->tight.buffer; + zstream->avail_in = tight->tight.offset; + zstream->next_out = tight->zlib.buffer + tight->zlib.offset; + zstream->avail_out = tight->zlib.capacity - tight->zlib.offset; previous_out = zstream->avail_out; zstream->data_type = Z_BINARY; @@ -XXX,XX +XXX,XX @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, return -1; } - vs->tight->zlib.offset = vs->tight->zlib.capacity - zstream->avail_out; + tight->zlib.offset = tight->zlib.capacity - zstream->avail_out; /* ...how much data has actually been produced by deflate() */ bytes = previous_out - zstream->avail_out; tight_send_compact_size(vs, bytes); - vnc_write(vs, vs->tight->zlib.buffer, bytes); + vnc_write(vs, tight->zlib.buffer, bytes); - buffer_reset(&vs->tight->zlib); + buffer_reset(&tight->zlib); return bytes; } @@ -XXX,XX +XXX,XX @@ static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) } } -static int send_full_color_rect(VncState *vs, int x, int y, int w, int h) +static int send_full_color_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { + VncTight *tight = &worker->tight; + int level = tight_conf[tight->compression].raw_zlib_level; int stream = 0; ssize_t bytes; #ifdef CONFIG_PNG - if (tight_can_send_png_rect(vs, w, h)) { - return send_png_rect(vs, x, y, w, h, NULL); + if (tight_can_send_png_rect(vs, tight, w, h)) { + return send_png_rect(vs, worker, x, y, w, h, NULL); } #endif vnc_write_u8(vs, stream << 4); /* no flushing, no filter */ - if (vs->tight->pixel24) { - tight_pack24(vs, vs->tight->tight.buffer, w * h, - &vs->tight->tight.offset); + if (tight->pixel24) { + tight_pack24(vs, tight->tight.buffer, w * h, &tight->tight.offset); bytes = 3; } else { bytes = vs->client_pf.bytes_per_pixel; } - bytes = tight_compress_data(vs, stream, w * h * bytes, - tight_conf[vs->tight->compression].raw_zlib_level, + bytes = tight_compress_data(vs, tight, stream, w * h * bytes, level, Z_DEFAULT_STRATEGY); return (bytes >= 0); } -static int send_solid_rect(VncState *vs) +static int send_solid_rect(VncState *vs, VncWorker *worker) { + VncTight *tight = &worker->tight; size_t bytes; vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */ - if (vs->tight->pixel24) { - tight_pack24(vs, vs->tight->tight.buffer, 1, &vs->tight->tight.offset); + if (tight->pixel24) { + tight_pack24(vs, tight->tight.buffer, 1, &tight->tight.offset); bytes = 3; } else { bytes = vs->client_pf.bytes_per_pixel; } - vnc_write(vs, vs->tight->tight.buffer, bytes); + vnc_write(vs, tight->tight.buffer, bytes); return 1; } -static int send_mono_rect(VncState *vs, int x, int y, +static int send_mono_rect(VncState *vs, VncWorker *worker, int x, int y, int w, int h, uint32_t bg, uint32_t fg) { ssize_t bytes; int stream = 1; - int level = tight_conf[vs->tight->compression].mono_zlib_level; + int level = tight_conf[worker->tight.compression].mono_zlib_level; #ifdef CONFIG_PNG - if (tight_can_send_png_rect(vs, w, h)) { + if (tight_can_send_png_rect(vs, &worker->tight, w, h)) { int ret; int bpp = vs->client_pf.bytes_per_pixel * 8; VncPalette *palette = palette_new(2, bpp); palette_put(palette, bg); palette_put(palette, fg); - ret = send_png_rect(vs, x, y, w, h, palette); + ret = send_png_rect(vs, worker, x, y, w, h, palette); palette_destroy(palette); return ret; } @@ -XXX,XX +XXX,XX @@ static int send_mono_rect(VncState *vs, int x, int y, uint32_t buf[2] = {bg, fg}; size_t ret = sizeof (buf); - if (vs->tight->pixel24) { + if (worker->tight.pixel24) { tight_pack24(vs, (unsigned char*)buf, 2, &ret); } vnc_write(vs, buf, ret); - tight_encode_mono_rect32(vs->tight->tight.buffer, w, h, bg, fg); + tight_encode_mono_rect32(worker->tight.tight.buffer, w, h, bg, fg); break; } case 2: @@ -XXX,XX +XXX,XX @@ static int send_mono_rect(VncState *vs, int x, int y, uint16_t fg16 = fg; vnc_write(vs, &bg16, 2); vnc_write(vs, &fg16, 2); - tight_encode_mono_rect16(vs->tight->tight.buffer, w, h, bg, fg); + tight_encode_mono_rect16(worker->tight.tight.buffer, w, h, bg, fg); break; } default: @@ -XXX,XX +XXX,XX @@ static int send_mono_rect(VncState *vs, int x, int y, uint8_t fg8 = fg; vnc_write_u8(vs, bg8); vnc_write_u8(vs, fg8); - tight_encode_mono_rect8(vs->tight->tight.buffer, w, h, bg, fg); + tight_encode_mono_rect8(worker->tight.tight.buffer, w, h, bg, fg); break; } } - vs->tight->tight.offset = bytes; + worker->tight.tight.offset = bytes; - bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY); + bytes = tight_compress_data(vs, &worker->tight, stream, bytes, level, + Z_DEFAULT_STRATEGY); return (bytes >= 0); } struct palette_cb_priv { VncState *vs; + VncTight *tight; uint8_t *header; #ifdef CONFIG_PNG png_colorp png_palette; @@ -XXX,XX +XXX,XX @@ static void write_palette(int idx, uint32_t color, void *opaque) } } -static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) +static bool send_gradient_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { + VncTight *tight = &worker->tight; int stream = 3; - int level = tight_conf[vs->tight->compression].gradient_zlib_level; + int level = tight_conf[tight->compression].gradient_zlib_level; ssize_t bytes; if (vs->client_pf.bytes_per_pixel == 1) { - return send_full_color_rect(vs, x, y, w, h); + return send_full_color_rect(vs, worker, x, y, w, h); } vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT); - buffer_reserve(&vs->tight->gradient, w * 3 * sizeof(int)); + buffer_reserve(&tight->gradient, w * 3 * sizeof(int)); - if (vs->tight->pixel24) { - tight_filter_gradient24(vs, vs->tight->tight.buffer, w, h); + if (tight->pixel24) { + tight_filter_gradient24(vs, tight, tight->tight.buffer, w, h); bytes = 3; } else if (vs->client_pf.bytes_per_pixel == 4) { - tight_filter_gradient32(vs, (uint32_t *)vs->tight->tight.buffer, w, h); + tight_filter_gradient32(vs, tight, (uint32_t *)tight->tight.buffer, + w, h); bytes = 4; } else { - tight_filter_gradient16(vs, (uint16_t *)vs->tight->tight.buffer, w, h); + tight_filter_gradient16(vs, tight, (uint16_t *)tight->tight.buffer, + w, h); bytes = 2; } - buffer_reset(&vs->tight->gradient); + buffer_reset(&tight->gradient); bytes = w * h * bytes; - vs->tight->tight.offset = bytes; + tight->tight.offset = bytes; - bytes = tight_compress_data(vs, stream, bytes, + bytes = tight_compress_data(vs, tight, stream, bytes, level, Z_FILTERED); return (bytes >= 0); } -static int send_palette_rect(VncState *vs, int x, int y, +static int send_palette_rect(VncState *vs, VncWorker *worker, int x, int y, int w, int h, VncPalette *palette) { + VncTight *tight = &worker->tight; int stream = 2; - int level = tight_conf[vs->tight->compression].idx_zlib_level; + int level = tight_conf[tight->compression].idx_zlib_level; int colors; ssize_t bytes; #ifdef CONFIG_PNG - if (tight_can_send_png_rect(vs, w, h)) { - return send_png_rect(vs, x, y, w, h, palette); + if (tight_can_send_png_rect(vs, tight, w, h)) { + return send_png_rect(vs, worker, x, y, w, h, palette); } #endif @@ -XXX,XX +XXX,XX @@ static int send_palette_rect(VncState *vs, int x, int y, { size_t old_offset, offset, palette_sz = palette_size(palette); g_autofree uint32_t *header = g_new(uint32_t, palette_sz); - struct palette_cb_priv priv = { vs, (uint8_t *)header }; + struct palette_cb_priv priv = { vs, tight, (uint8_t *)header }; old_offset = vs->output.offset; palette_iter(palette, write_palette, &priv); vnc_write(vs, header, palette_sz * sizeof(uint32_t)); - if (vs->tight->pixel24) { + if (tight->pixel24) { tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset); vs->output.offset = old_offset + offset; } - tight_encode_indexed_rect32(vs->tight->tight.buffer, w * h, palette); + tight_encode_indexed_rect32(tight->tight.buffer, w * h, palette); break; } case 2: { size_t palette_sz = palette_size(palette); g_autofree uint16_t *header = g_new(uint16_t, palette_sz); - struct palette_cb_priv priv = { vs, (uint8_t *)header }; + struct palette_cb_priv priv = { vs, tight, (uint8_t *)header }; palette_iter(palette, write_palette, &priv); vnc_write(vs, header, palette_sz * sizeof(uint16_t)); - tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, palette); + tight_encode_indexed_rect16(tight->tight.buffer, w * h, palette); break; } default: return -1; /* No palette for 8bits colors */ } bytes = w * h; - vs->tight->tight.offset = bytes; + tight->tight.offset = bytes; - bytes = tight_compress_data(vs, stream, bytes, + bytes = tight_compress_data(vs, tight, stream, bytes, level, Z_DEFAULT_STRATEGY); return (bytes >= 0); } @@ -XXX,XX +XXX,XX @@ static int send_palette_rect(VncState *vs, int x, int y, /* This is called once per encoding */ static void jpeg_init_destination(j_compress_ptr cinfo) { - VncState *vs = cinfo->client_data; - Buffer *buffer = &vs->tight->jpeg; + VncTight *tight = cinfo->client_data; + Buffer *buffer = &tight->jpeg; cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset); @@ -XXX,XX +XXX,XX @@ static void jpeg_init_destination(j_compress_ptr cinfo) /* This is called when we ran out of buffer (shouldn't happen!) */ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) { - VncState *vs = cinfo->client_data; - Buffer *buffer = &vs->tight->jpeg; + VncTight *tight = cinfo->client_data; + Buffer *buffer = &tight->jpeg; buffer->offset = buffer->capacity; buffer_reserve(buffer, 2048); @@ -XXX,XX +XXX,XX @@ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) /* This is called when we are done processing data */ static void jpeg_term_destination(j_compress_ptr cinfo) { - VncState *vs = cinfo->client_data; - Buffer *buffer = &vs->tight->jpeg; + VncTight *tight = cinfo->client_data; + Buffer *buffer = &tight->jpeg; buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; } -static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) +static int send_jpeg_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, int quality) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; @@ -XXX,XX +XXX,XX @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) int dy; if (surface_bytes_per_pixel(vs->vd->ds) == 1) { - return send_full_color_rect(vs, x, y, w, h); + return send_full_color_rect(vs, worker, x, y, w, h); } - buffer_reserve(&vs->tight->jpeg, 2048); + buffer_reserve(&worker->tight.jpeg, 2048); cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); - cinfo.client_data = vs; + cinfo.client_data = &worker->tight; cinfo.image_width = w; cinfo.image_height = h; cinfo.input_components = 3; @@ -XXX,XX +XXX,XX @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) vnc_write_u8(vs, VNC_TIGHT_JPEG << 4); - tight_send_compact_size(vs, vs->tight->jpeg.offset); - vnc_write(vs, vs->tight->jpeg.buffer, vs->tight->jpeg.offset); - buffer_reset(&vs->tight->jpeg); + tight_send_compact_size(vs, worker->tight.jpeg.offset); + vnc_write(vs, worker->tight.jpeg.buffer, worker->tight.jpeg.offset); + buffer_reset(&worker->tight.jpeg); return 1; } @@ -XXX,XX +XXX,XX @@ static void write_png_palette(int idx, uint32_t pix, void *opaque) VncState *vs = priv->vs; png_colorp color = &priv->png_palette[idx]; - if (vs->tight->pixel24) + if (priv->tight->pixel24) { color->red = (pix >> vs->client_pf.rshift) & vs->client_pf.rmax; color->green = (pix >> vs->client_pf.gshift) & vs->client_pf.gmax; @@ -XXX,XX +XXX,XX @@ static void write_png_palette(int idx, uint32_t pix, void *opaque) static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { - VncState *vs = png_get_io_ptr(png_ptr); + VncWorker *worker = png_get_io_ptr(png_ptr); - buffer_reserve(&vs->tight->png, vs->tight->png.offset + length); - memcpy(vs->tight->png.buffer + vs->tight->png.offset, data, length); + buffer_reserve(&worker->tight.png, worker->tight.png.offset + length); + memcpy(worker->tight.png.buffer + worker->tight.png.offset, data, length); - vs->tight->png.offset += length; + worker->tight.png.offset += length; } static void png_flush_data(png_structp png_ptr) @@ -XXX,XX +XXX,XX @@ static void vnc_png_free(png_structp png_ptr, png_voidp ptr) g_free(ptr); } -static int send_png_rect(VncState *vs, int x, int y, int w, int h, - VncPalette *palette) +static int send_png_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, VncPalette *palette) { png_byte color_type; png_structp png_ptr; png_infop info_ptr; png_colorp png_palette = NULL; pixman_image_t *linebuf; - int level = tight_png_conf[vs->tight->compression].png_zlib_level; - int filters = tight_png_conf[vs->tight->compression].png_filters; + int level = tight_png_conf[worker->tight.compression].png_zlib_level; + int filters = tight_png_conf[worker->tight.compression].png_filters; uint8_t *buf; int dy; @@ -XXX,XX +XXX,XX @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, return -1; } - png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data); + png_set_write_fn(png_ptr, worker, png_write_data, png_flush_data); png_set_compression_level(png_ptr, level); png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters); @@ -XXX,XX +XXX,XX @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, palette_size(palette)); priv.vs = vs; + priv.tight = &worker->tight; priv.png_palette = png_palette; palette_iter(palette, write_png_palette, &priv); png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette)); if (vs->client_pf.bytes_per_pixel == 4) { - tight_encode_indexed_rect32(vs->tight->tight.buffer, w * h, + tight_encode_indexed_rect32(worker->tight.tight.buffer, w * h, palette); } else { - tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, + tight_encode_indexed_rect16(worker->tight.tight.buffer, w * h, palette); } } png_write_info(png_ptr, info_ptr); - buffer_reserve(&vs->tight->png, 2048); + buffer_reserve(&worker->tight.png, 2048); linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, w); buf = (uint8_t *)pixman_image_get_data(linebuf); for (dy = 0; dy < h; dy++) { if (color_type == PNG_COLOR_TYPE_PALETTE) { - memcpy(buf, vs->tight->tight.buffer + (dy * w), w); + memcpy(buf, worker->tight.tight.buffer + (dy * w), w); } else { qemu_pixman_linebuf_fill(linebuf, vs->vd->server, w, x, y + dy); } @@ -XXX,XX +XXX,XX @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, vnc_write_u8(vs, VNC_TIGHT_PNG << 4); - tight_send_compact_size(vs, vs->tight->png.offset); - vnc_write(vs, vs->tight->png.buffer, vs->tight->png.offset); - buffer_reset(&vs->tight->png); + tight_send_compact_size(vs, worker->tight.png.offset); + vnc_write(vs, worker->tight.png.buffer, worker->tight.png.offset); + buffer_reset(&worker->tight.png); return 1; } #endif /* CONFIG_PNG */ -static void vnc_tight_start(VncState *vs) +static void vnc_tight_start(VncState *vs, VncTight *tight) { - buffer_reset(&vs->tight->tight); + buffer_reset(&tight->tight); // make the output buffer be the zlib buffer, so we can compress it later - vs->tight->tmp = vs->output; - vs->output = vs->tight->tight; + tight->tmp = vs->output; + vs->output = tight->tight; } -static void vnc_tight_stop(VncState *vs) +static void vnc_tight_stop(VncState *vs, VncTight *tight) { // switch back to normal output/zlib buffers - vs->tight->tight = vs->output; - vs->output = vs->tight->tmp; + tight->tight = vs->output; + vs->output = tight->tmp; } -static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h, +static int send_sub_rect_nojpeg(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, int bg, int fg, int colors, VncPalette *palette) { int ret; if (colors == 0) { - if (tight_detect_smooth_image(vs, w, h)) { - ret = send_gradient_rect(vs, x, y, w, h); + if (tight_detect_smooth_image(vs, &worker->tight, w, h)) { + ret = send_gradient_rect(vs, worker, x, y, w, h); } else { - ret = send_full_color_rect(vs, x, y, w, h); + ret = send_full_color_rect(vs, worker, x, y, w, h); } } else if (colors == 1) { - ret = send_solid_rect(vs); + ret = send_solid_rect(vs, worker); } else if (colors == 2) { - ret = send_mono_rect(vs, x, y, w, h, bg, fg); + ret = send_mono_rect(vs, worker, x, y, w, h, bg, fg); } else if (colors <= 256) { - ret = send_palette_rect(vs, x, y, w, h, palette); + ret = send_palette_rect(vs, worker, x, y, w, h, palette); } else { ret = 0; } @@ -XXX,XX +XXX,XX @@ static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h, } #ifdef CONFIG_VNC_JPEG -static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h, +static int send_sub_rect_jpeg(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, int bg, int fg, int colors, VncPalette *palette, bool force) { int ret; if (colors == 0) { - if (force || (tight_jpeg_conf[vs->tight->quality].jpeg_full && - tight_detect_smooth_image(vs, w, h))) { - int quality = tight_conf[vs->tight->quality].jpeg_quality; + if (force || (tight_jpeg_conf[worker->tight.quality].jpeg_full && + tight_detect_smooth_image(vs, &worker->tight, w, h))) { + int quality = tight_conf[worker->tight.quality].jpeg_quality; - ret = send_jpeg_rect(vs, x, y, w, h, quality); + ret = send_jpeg_rect(vs, worker, x, y, w, h, quality); } else { - ret = send_full_color_rect(vs, x, y, w, h); + ret = send_full_color_rect(vs, worker, x, y, w, h); } } else if (colors == 1) { - ret = send_solid_rect(vs); + ret = send_solid_rect(vs, worker); } else if (colors == 2) { - ret = send_mono_rect(vs, x, y, w, h, bg, fg); + ret = send_mono_rect(vs, worker, x, y, w, h, bg, fg); } else if (colors <= 256) { if (force || (colors > 96 && - tight_jpeg_conf[vs->tight->quality].jpeg_idx && - tight_detect_smooth_image(vs, w, h))) { - int quality = tight_conf[vs->tight->quality].jpeg_quality; + tight_jpeg_conf[worker->tight.quality].jpeg_idx && + tight_detect_smooth_image(vs, &worker->tight, w, h))) { + int quality = tight_conf[worker->tight.quality].jpeg_quality; - ret = send_jpeg_rect(vs, x, y, w, h, quality); + ret = send_jpeg_rect(vs, worker, x, y, w, h, quality); } else { - ret = send_palette_rect(vs, x, y, w, h, palette); + ret = send_palette_rect(vs, worker, x, y, w, h, palette); } } else { ret = 0; @@ -XXX,XX +XXX,XX @@ static void vnc_tight_cleanup(Notifier *n, void *value) color_count_palette = NULL; } -static int send_sub_rect(VncState *vs, int x, int y, int w, int h) +static int send_sub_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { + VncTight *tight = &worker->tight; uint32_t bg = 0, fg = 0; int colors; int ret = 0; @@ -XXX,XX +XXX,XX @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) qemu_thread_atexit_add(&vnc_tight_cleanup_notifier); } - vnc_framebuffer_update(vs, x, y, w, h, vs->tight->type); + vnc_framebuffer_update(vs, x, y, w, h, tight->type); - vnc_tight_start(vs); + vnc_tight_start(vs, tight); vnc_raw_send_framebuffer_update(vs, x, y, w, h); - vnc_tight_stop(vs); + vnc_tight_stop(vs, tight); #ifdef CONFIG_VNC_JPEG - if (!vs->vd->non_adaptive && vs->tight->quality != (uint8_t)-1) { + if (!vs->vd->non_adaptive && tight->quality != (uint8_t)-1) { double freq = vnc_update_freq(vs, x, y, w, h); - if (freq < tight_jpeg_conf[vs->tight->quality].jpeg_freq_min) { + if (freq < tight_jpeg_conf[tight->quality].jpeg_freq_min) { allow_jpeg = false; } - if (freq >= tight_jpeg_conf[vs->tight->quality].jpeg_freq_threshold) { + if (freq >= tight_jpeg_conf[tight->quality].jpeg_freq_threshold) { force_jpeg = true; - vnc_sent_lossy_rect(vs, x, y, w, h); + vnc_sent_lossy_rect(worker, x, y, w, h); } } #endif - colors = tight_fill_palette(vs, x, y, w * h, &bg, &fg, color_count_palette); + colors = tight_fill_palette(vs, tight, x, y, w * h, &bg, &fg, + color_count_palette); #ifdef CONFIG_VNC_JPEG - if (allow_jpeg && vs->tight->quality != (uint8_t)-1) { - ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, + if (allow_jpeg && tight->quality != (uint8_t)-1) { + ret = send_sub_rect_jpeg(vs, worker, x, y, w, h, bg, fg, colors, color_count_palette, force_jpeg); } else { - ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, - color_count_palette); + ret = send_sub_rect_nojpeg(vs, worker, x, y, w, h, bg, fg, + colors, color_count_palette); } #else - ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, + ret = send_sub_rect_nojpeg(vs, worker, x, y, w, h, bg, fg, colors, color_count_palette); #endif return ret; } -static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h) +static int send_sub_rect_solid(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vnc_framebuffer_update(vs, x, y, w, h, vs->tight->type); + vnc_framebuffer_update(vs, x, y, w, h, worker->tight.type); - vnc_tight_start(vs); + vnc_tight_start(vs, &worker->tight); vnc_raw_send_framebuffer_update(vs, x, y, w, h); - vnc_tight_stop(vs); + vnc_tight_stop(vs, &worker->tight); - return send_solid_rect(vs); + return send_solid_rect(vs, worker); } -static int send_rect_simple(VncState *vs, int x, int y, int w, int h, - bool split) +static int send_rect_simple(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, bool split) { int max_size, max_width; int max_sub_width, max_sub_height; @@ -XXX,XX +XXX,XX @@ static int send_rect_simple(VncState *vs, int x, int y, int w, int h, int rw, rh; int n = 0; - max_size = tight_conf[vs->tight->compression].max_rect_size; - max_width = tight_conf[vs->tight->compression].max_rect_width; + max_size = tight_conf[worker->tight.compression].max_rect_size; + max_width = tight_conf[worker->tight.compression].max_rect_width; if (split && (w > max_width || w * h > max_size)) { max_sub_width = (w > max_width) ? max_width : w; @@ -XXX,XX +XXX,XX @@ static int send_rect_simple(VncState *vs, int x, int y, int w, int h, for (dx = 0; dx < w; dx += max_width) { rw = MIN(max_sub_width, w - dx); rh = MIN(max_sub_height, h - dy); - n += send_sub_rect(vs, x+dx, y+dy, rw, rh); + n += send_sub_rect(vs, worker, x + dx, y + dy, rw, rh); } } } else { - n += send_sub_rect(vs, x, y, w, h); + n += send_sub_rect(vs, worker, x, y, w, h); } return n; } -static int find_large_solid_color_rect(VncState *vs, int x, int y, - int w, int h, int max_rows) +static int find_large_solid_color_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, int max_rows) { int dx, dy, dw, dh; int n = 0; @@ -XXX,XX +XXX,XX @@ static int find_large_solid_color_rect(VncState *vs, int x, int y, /* If a rectangle becomes too large, send its upper part now. */ if (dy - y >= max_rows) { - n += send_rect_simple(vs, x, y, w, max_rows, true); + n += send_rect_simple(vs, worker, x, y, w, max_rows, true); y += max_rows; h -= max_rows; } @@ -XXX,XX +XXX,XX @@ static int find_large_solid_color_rect(VncState *vs, int x, int y, /* Send rectangles at top and left to solid-color area. */ if (y_best != y) { - n += send_rect_simple(vs, x, y, w, y_best-y, true); + n += send_rect_simple(vs, worker, x, y, w, y_best - y, true); } if (x_best != x) { - n += tight_send_framebuffer_update(vs, x, y_best, + n += tight_send_framebuffer_update(vs, worker, x, y_best, x_best-x, h_best); } /* Send solid-color rectangle. */ - n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best); + n += send_sub_rect_solid(vs, worker, + x_best, y_best, w_best, h_best); /* Send remaining rectangles (at right and bottom). */ if (x_best + w_best != x + w) { - n += tight_send_framebuffer_update(vs, x_best+w_best, + n += tight_send_framebuffer_update(vs, worker, x_best + w_best, y_best, w-(x_best-x)-w_best, h_best); } if (y_best + h_best != y + h) { - n += tight_send_framebuffer_update(vs, x, y_best+h_best, + n += tight_send_framebuffer_update(vs, worker, + x, y_best + h_best, w, h-(y_best-y)-h_best); } @@ -XXX,XX +XXX,XX @@ static int find_large_solid_color_rect(VncState *vs, int x, int y, return n; } } - return n + send_rect_simple(vs, x, y, w, h, true); + return n + send_rect_simple(vs, worker, x, y, w, h, true); } -static int tight_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) +static int tight_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { int max_rows; if (vs->client_pf.bytes_per_pixel == 4 && vs->client_pf.rmax == 0xFF && vs->client_pf.bmax == 0xFF && vs->client_pf.gmax == 0xFF) { - vs->tight->pixel24 = true; + worker->tight.pixel24 = true; } else { - vs->tight->pixel24 = false; + worker->tight.pixel24 = false; } #ifdef CONFIG_VNC_JPEG - if (vs->tight->quality != (uint8_t)-1) { + if (worker->tight.quality != (uint8_t)-1) { double freq = vnc_update_freq(vs, x, y, w, h); - if (freq > tight_jpeg_conf[vs->tight->quality].jpeg_freq_threshold) { - return send_rect_simple(vs, x, y, w, h, false); + if (freq > tight_jpeg_conf[worker->tight.quality].jpeg_freq_threshold) { + return send_rect_simple(vs, worker, x, y, w, h, false); } } #endif if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE) { - return send_rect_simple(vs, x, y, w, h, true); + return send_rect_simple(vs, worker, x, y, w, h, true); } /* Calculate maximum number of rows in one non-solid rectangle. */ - max_rows = tight_conf[vs->tight->compression].max_rect_size; - max_rows /= MIN(tight_conf[vs->tight->compression].max_rect_width, w); + max_rows = tight_conf[worker->tight.compression].max_rect_size; + max_rows /= MIN(tight_conf[worker->tight.compression].max_rect_width, w); - return find_large_solid_color_rect(vs, x, y, w, h, max_rows); + return find_large_solid_color_rect(vs, worker, x, y, w, h, max_rows); } -int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) +int vnc_tight_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vs->tight->type = VNC_ENCODING_TIGHT; - return tight_send_framebuffer_update(vs, x, y, w, h); + worker->tight.type = VNC_ENCODING_TIGHT; + return tight_send_framebuffer_update(vs, worker, x, y, w, h); } -int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) +int vnc_tight_png_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vs->tight->type = VNC_ENCODING_TIGHT_PNG; - return tight_send_framebuffer_update(vs, x, y, w, h); + worker->tight.type = VNC_ENCODING_TIGHT_PNG; + return tight_send_framebuffer_update(vs, worker, x, y, w, h); } -void vnc_tight_clear(VncState *vs) +void vnc_tight_clear(VncWorker *worker) { int i; - for (i = 0; i < ARRAY_SIZE(vs->tight->stream); i++) { - if (vs->tight->stream[i].opaque) { - deflateEnd(&vs->tight->stream[i]); + for (i = 0; i < ARRAY_SIZE(worker->tight.stream); i++) { + if (worker->tight.stream[i].opaque) { + deflateEnd(&worker->tight.stream[i]); } } - buffer_free(&vs->tight->tight); - buffer_free(&vs->tight->zlib); - buffer_free(&vs->tight->gradient); + buffer_free(&worker->tight.tight); + buffer_free(&worker->tight.zlib); + buffer_free(&worker->tight.gradient); #ifdef CONFIG_VNC_JPEG - buffer_free(&vs->tight->jpeg); + buffer_free(&worker->tight.jpeg); #endif #ifdef CONFIG_PNG - buffer_free(&vs->tight->png); + buffer_free(&worker->tight.png); #endif } diff --git a/ui/vnc-enc-zlib.c b/ui/vnc-enc-zlib.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc-enc-zlib.c +++ b/ui/vnc-enc-zlib.c @@ -XXX,XX +XXX,XX @@ void vnc_zlib_zfree(void *x, void *addr) g_free(addr); } -static void vnc_zlib_start(VncState *vs) +static void vnc_zlib_start(VncState *vs, VncWorker *worker) { - buffer_reset(&vs->zlib->zlib); + buffer_reset(&worker->zlib.zlib); // make the output buffer be the zlib buffer, so we can compress it later - vs->zlib->tmp = vs->output; - vs->output = vs->zlib->zlib; + worker->zlib.tmp = vs->output; + vs->output = worker->zlib.zlib; } -static int vnc_zlib_stop(VncState *vs) +static int vnc_zlib_stop(VncState *vs, VncWorker *worker) { - z_streamp zstream = &vs->zlib->stream; + z_streamp zstream = &worker->zlib.stream; int previous_out; // switch back to normal output/zlib buffers - vs->zlib->zlib = vs->output; - vs->output = vs->zlib->tmp; + worker->zlib.zlib = vs->output; + vs->output = worker->zlib.tmp; // compress the zlib buffer @@ -XXX,XX +XXX,XX @@ static int vnc_zlib_stop(VncState *vs) zstream->zalloc = vnc_zlib_zalloc; zstream->zfree = vnc_zlib_zfree; - err = deflateInit2(zstream, vs->tight->compression, Z_DEFLATED, + err = deflateInit2(zstream, worker->tight.compression, Z_DEFLATED, MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); @@ -XXX,XX +XXX,XX @@ static int vnc_zlib_stop(VncState *vs) return -1; } - vs->zlib->level = vs->tight->compression; + worker->zlib.level = worker->tight.compression; zstream->opaque = vs; } - if (vs->tight->compression != vs->zlib->level) { - if (deflateParams(zstream, vs->tight->compression, + if (worker->tight.compression != worker->zlib.level) { + if (deflateParams(zstream, worker->tight.compression, Z_DEFAULT_STRATEGY) != Z_OK) { return -1; } - vs->zlib->level = vs->tight->compression; + worker->zlib.level = worker->tight.compression; } // reserve memory in output buffer - buffer_reserve(&vs->output, vs->zlib->zlib.offset + 64); + buffer_reserve(&vs->output, worker->zlib.zlib.offset + 64); // set pointers - zstream->next_in = vs->zlib->zlib.buffer; - zstream->avail_in = vs->zlib->zlib.offset; + zstream->next_in = worker->zlib.zlib.buffer; + zstream->avail_in = worker->zlib.zlib.offset; zstream->next_out = vs->output.buffer + vs->output.offset; zstream->avail_out = vs->output.capacity - vs->output.offset; previous_out = zstream->avail_out; @@ -XXX,XX +XXX,XX @@ static int vnc_zlib_stop(VncState *vs) return previous_out - zstream->avail_out; } -int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_zlib_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { int old_offset, new_offset, bytes_written; @@ -XXX,XX +XXX,XX @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) vnc_write_s32(vs, 0); // compress the stream - vnc_zlib_start(vs); + vnc_zlib_start(vs, worker); vnc_raw_send_framebuffer_update(vs, x, y, w, h); - bytes_written = vnc_zlib_stop(vs); + bytes_written = vnc_zlib_stop(vs, worker); if (bytes_written == -1) return 0; @@ -XXX,XX +XXX,XX @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) return 1; } -void vnc_zlib_clear(VncState *vs) +void vnc_zlib_clear(VncWorker *worker) { - if (vs->zlib->stream.opaque) { - deflateEnd(&vs->zlib->stream); + if (worker->zlib.stream.opaque) { + deflateEnd(&worker->zlib.stream); } - buffer_free(&vs->zlib->zlib); + buffer_free(&worker->zlib.zlib); } diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc-enc-zrle.c +++ b/ui/vnc-enc-zrle.c @@ -XXX,XX +XXX,XX @@ static const int bits_per_packed_pixel[] = { }; -static void vnc_zrle_start(VncState *vs) +static void vnc_zrle_start(VncState *vs, VncZrle *zrle) { - buffer_reset(&vs->zrle->zrle); + buffer_reset(&zrle->zrle); /* make the output buffer be the zlib buffer, so we can compress it later */ - vs->zrle->tmp = vs->output; - vs->output = vs->zrle->zrle; + zrle->tmp = vs->output; + vs->output = zrle->zrle; } -static void vnc_zrle_stop(VncState *vs) +static void vnc_zrle_stop(VncState *vs, VncZrle *zrle) { /* switch back to normal output/zlib buffers */ - vs->zrle->zrle = vs->output; - vs->output = vs->zrle->tmp; + zrle->zrle = vs->output; + vs->output = zrle->tmp; } -static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h, - int bpp) +static void *zrle_convert_fb(VncState *vs, VncZrle *zrle, + int x, int y, int w, int h, int bpp) { Buffer tmp; - buffer_reset(&vs->zrle->fb); - buffer_reserve(&vs->zrle->fb, w * h * bpp + bpp); + buffer_reset(&zrle->fb); + buffer_reserve(&zrle->fb, w * h * bpp + bpp); tmp = vs->output; - vs->output = vs->zrle->fb; + vs->output = zrle->fb; vnc_raw_send_framebuffer_update(vs, x, y, w, h); - vs->zrle->fb = vs->output; + zrle->fb = vs->output; vs->output = tmp; - return vs->zrle->fb.buffer; + return zrle->fb.buffer; } -static int zrle_compress_data(VncState *vs, int level) +static int zrle_compress_data(VncState *vs, VncZrle *zrle, int level) { - z_streamp zstream = &vs->zrle->stream; + z_streamp zstream = &zrle->stream; - buffer_reset(&vs->zrle->zlib); + buffer_reset(&zrle->zlib); if (zstream->opaque != vs) { int err; @@ -XXX,XX +XXX,XX @@ static int zrle_compress_data(VncState *vs, int level) } /* reserve memory in output buffer */ - buffer_reserve(&vs->zrle->zlib, vs->zrle->zrle.offset + 64); + buffer_reserve(&zrle->zlib, zrle->zrle.offset + 64); /* set pointers */ - zstream->next_in = vs->zrle->zrle.buffer; - zstream->avail_in = vs->zrle->zrle.offset; - zstream->next_out = vs->zrle->zlib.buffer; - zstream->avail_out = vs->zrle->zlib.capacity; + zstream->next_in = zrle->zrle.buffer; + zstream->avail_in = zrle->zrle.offset; + zstream->next_out = zrle->zlib.buffer; + zstream->avail_out = zrle->zlib.capacity; zstream->data_type = Z_BINARY; /* start encoding */ @@ -XXX,XX +XXX,XX @@ static int zrle_compress_data(VncState *vs, int level) return -1; } - vs->zrle->zlib.offset = vs->zrle->zlib.capacity - zstream->avail_out; - return vs->zrle->zlib.offset; + zrle->zlib.offset = zrle->zlib.capacity - zstream->avail_out; + return zrle->zlib.offset; } /* Try to work out whether to use RLE and/or a palette. We do this by @@ -XXX,XX +XXX,XX @@ static void zrle_write_u8(VncState *vs, uint8_t value) #undef ZRLE_COMPACT_PIXEL #undef ZRLE_BPP -static int zrle_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) +static int zrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { bool be = vs->client_endian == G_BIG_ENDIAN; size_t bytes; int zywrle_level; - if (vs->zrle->type == VNC_ENCODING_ZYWRLE) { - if (!vs->vd->lossy || vs->tight->quality == (uint8_t)-1 - || vs->tight->quality == 9) { + if (worker->zrle.type == VNC_ENCODING_ZYWRLE) { + if (!vs->vd->lossy || worker->tight.quality == (uint8_t)-1 + || worker->tight.quality == 9) { zywrle_level = 0; - vs->zrle->type = VNC_ENCODING_ZRLE; - } else if (vs->tight->quality < 3) { + worker->zrle.type = VNC_ENCODING_ZRLE; + } else if (worker->tight.quality < 3) { zywrle_level = 3; - } else if (vs->tight->quality < 6) { + } else if (worker->tight.quality < 6) { zywrle_level = 2; } else { zywrle_level = 1; @@ -XXX,XX +XXX,XX @@ static int zrle_send_framebuffer_update(VncState *vs, int x, int y, zywrle_level = 0; } - vnc_zrle_start(vs); + vnc_zrle_start(vs, &worker->zrle); switch (vs->client_pf.bytes_per_pixel) { case 1: - zrle_encode_8ne(vs, x, y, w, h, zywrle_level); + zrle_encode_8ne(vs, &worker->zrle, x, y, w, h, zywrle_level); break; case 2: if (vs->client_pf.gmax > 0x1F) { if (be) { - zrle_encode_16be(vs, x, y, w, h, zywrle_level); + zrle_encode_16be(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_16le(vs, x, y, w, h, zywrle_level); + zrle_encode_16le(vs, &worker->zrle, x, y, w, h, zywrle_level); } } else { if (be) { - zrle_encode_15be(vs, x, y, w, h, zywrle_level); + zrle_encode_15be(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_15le(vs, x, y, w, h, zywrle_level); + zrle_encode_15le(vs, &worker->zrle, x, y, w, h, zywrle_level); } } break; @@ -XXX,XX +XXX,XX @@ static int zrle_send_framebuffer_update(VncState *vs, int x, int y, if ((fits_in_ls3bytes && !be) || (fits_in_ms3bytes && be)) { if (be) { - zrle_encode_24abe(vs, x, y, w, h, zywrle_level); + zrle_encode_24abe(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_24ale(vs, x, y, w, h, zywrle_level); + zrle_encode_24ale(vs, &worker->zrle, x, y, w, h, zywrle_level); } } else if ((fits_in_ls3bytes && be) || (fits_in_ms3bytes && !be)) { if (be) { - zrle_encode_24bbe(vs, x, y, w, h, zywrle_level); + zrle_encode_24bbe(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_24ble(vs, x, y, w, h, zywrle_level); + zrle_encode_24ble(vs, &worker->zrle, x, y, w, h, zywrle_level); } } else { if (be) { - zrle_encode_32be(vs, x, y, w, h, zywrle_level); + zrle_encode_32be(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_32le(vs, x, y, w, h, zywrle_level); + zrle_encode_32le(vs, &worker->zrle, x, y, w, h, zywrle_level); } } } break; } - vnc_zrle_stop(vs); - bytes = zrle_compress_data(vs, Z_DEFAULT_COMPRESSION); - vnc_framebuffer_update(vs, x, y, w, h, vs->zrle->type); + vnc_zrle_stop(vs, &worker->zrle); + bytes = zrle_compress_data(vs, &worker->zrle, Z_DEFAULT_COMPRESSION); + vnc_framebuffer_update(vs, x, y, w, h, worker->zrle.type); vnc_write_u32(vs, bytes); - vnc_write(vs, vs->zrle->zlib.buffer, vs->zrle->zlib.offset); + vnc_write(vs, worker->zrle.zlib.buffer, worker->zrle.zlib.offset); return 1; } -int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_zrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vs->zrle->type = VNC_ENCODING_ZRLE; - return zrle_send_framebuffer_update(vs, x, y, w, h); + worker->zrle.type = VNC_ENCODING_ZRLE; + return zrle_send_framebuffer_update(vs, worker, x, y, w, h); } -int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_zywrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vs->zrle->type = VNC_ENCODING_ZYWRLE; - return zrle_send_framebuffer_update(vs, x, y, w, h); + worker->zrle.type = VNC_ENCODING_ZYWRLE; + return zrle_send_framebuffer_update(vs, worker, x, y, w, h); } -void vnc_zrle_clear(VncState *vs) +void vnc_zrle_clear(VncWorker *worker) { - if (vs->zrle->stream.opaque) { - deflateEnd(&vs->zrle->stream); + if (worker->zrle.stream.opaque) { + deflateEnd(&worker->zrle.stream); } - buffer_free(&vs->zrle->zrle); - buffer_free(&vs->zrle->fb); - buffer_free(&vs->zrle->zlib); + buffer_free(&worker->zrle.zrle); + buffer_free(&worker->zrle.fb); + buffer_free(&worker->zrle.zlib); } diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -XXX,XX +XXX,XX @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->vnc_encoding = orig->vnc_encoding; local->features = orig->features; local->vd = orig->vd; - local->lossy_rect = orig->lossy_rect; local->write_pixels = orig->write_pixels; local->client_pf = orig->client_pf; local->client_endian = orig->client_endian; - local->tight = orig->tight; - local->zlib = orig->zlib; local->hextile = orig->hextile; - local->zrle = orig->zrle; local->client_width = orig->client_width; local->client_height = orig->client_height; } @@ -XXX,XX +XXX,XX @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) static void vnc_async_encoding_end(VncState *orig, VncState *local) { buffer_free(&local->output); - orig->tight = local->tight; - orig->zlib = local->zlib; orig->hextile = local->hextile; - orig->zrle = local->zrle; - orig->lossy_rect = local->lossy_rect; } static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect) @@ -XXX,XX +XXX,XX @@ static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect) static int vnc_worker_thread_loop(VncJobQueue *queue) { + VncConnection *vc; VncJob *job; VncRectEntry *entry, *tmp; VncState vs = {}; @@ -XXX,XX +XXX,XX @@ static int vnc_worker_thread_loop(VncJobQueue *queue) } assert(job->vs->magic == VNC_MAGIC); + vc = container_of(job->vs, VncConnection, vs); vnc_lock_output(job->vs); if (job->vs->ioc == NULL || job->vs->abort == true) { @@ -XXX,XX +XXX,XX @@ static int vnc_worker_thread_loop(VncJobQueue *queue) } if (vnc_worker_clamp_rect(&vs, job, &entry->rect)) { - n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y, + n = vnc_send_framebuffer_update(&vs, &vc->worker, + entry->rect.x, entry->rect.y, entry->rect.w, entry->rect.h); if (n >= 0) { diff --git a/ui/vnc.c b/ui/vnc.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -XXX,XX +XXX,XX @@ #include "io/dns-resolver.h" #include "monitor/monitor.h" -typedef struct VncConnection { - VncState vs; - VncZlib zlib; -} VncConnection; - #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 #define VNC_REFRESH_INTERVAL_MAX GUI_REFRESH_INTERVAL_IDLE @@ -XXX,XX +XXX,XX @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) return 1; } -int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { int n = 0; switch(vs->vnc_encoding) { case VNC_ENCODING_ZLIB: - n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h); + n = vnc_zlib_send_framebuffer_update(vs, worker, x, y, w, h); break; case VNC_ENCODING_HEXTILE: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE); n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h); break; case VNC_ENCODING_TIGHT: - n = vnc_tight_send_framebuffer_update(vs, x, y, w, h); + n = vnc_tight_send_framebuffer_update(vs, worker, x, y, w, h); break; case VNC_ENCODING_TIGHT_PNG: - n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h); + n = vnc_tight_png_send_framebuffer_update(vs, worker, x, y, w, h); break; case VNC_ENCODING_ZRLE: - n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h); + n = vnc_zrle_send_framebuffer_update(vs, worker, x, y, w, h); break; case VNC_ENCODING_ZYWRLE: - n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h); + n = vnc_zywrle_send_framebuffer_update(vs, worker, x, y, w, h); break; default: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); @@ -XXX,XX +XXX,XX @@ static void vnc_disconnect_start(VncState *vs) void vnc_disconnect_finish(VncState *vs) { - int i; + VncConnection *vc = container_of(vs, VncConnection, vs); trace_vnc_client_disconnect_finish(vs, vs->ioc); @@ -XXX,XX +XXX,XX @@ void vnc_disconnect_finish(VncState *vs) qapi_free_VncClientInfo(vs->info); - vnc_zlib_clear(vs); - vnc_tight_clear(vs); - vnc_zrle_clear(vs); + vnc_zlib_clear(&vc->worker); + vnc_tight_clear(&vc->worker); + vnc_zrle_clear(&vc->worker); #ifdef CONFIG_VNC_SASL vnc_sasl_client_cleanup(vs); @@ -XXX,XX +XXX,XX @@ void vnc_disconnect_finish(VncState *vs) } buffer_free(&vs->jobs_buffer); - for (i = 0; i < VNC_STAT_ROWS; ++i) { - g_free(vs->lossy_rect[i]); - } - g_free(vs->lossy_rect); - object_unref(OBJECT(vs->ioc)); vs->ioc = NULL; object_unref(OBJECT(vs->sioc)); vs->sioc = NULL; vs->magic = 0; - g_free(vs->zrle); - g_free(vs->tight); - g_free(container_of(vs, VncConnection, vs)); + g_free(vc); } size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err) @@ -XXX,XX +XXX,XX @@ static void send_xvp_message(VncState *vs, int code) static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) { + VncConnection *vc = container_of(vs, VncConnection, vs); int i; unsigned int enc = 0; vs->features = 0; vs->vnc_encoding = 0; - vs->tight->compression = 9; - vs->tight->quality = -1; /* Lossless by default */ + vc->worker.tight.compression = 9; + vc->worker.tight.quality = -1; /* Lossless by default */ vs->absolute = -1; /* @@ -XXX,XX +XXX,XX @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vnc_server_cut_text_caps(vs); break; case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9: - vs->tight->compression = (enc & 0x0F); + vc->worker.tight.compression = (enc & 0x0F); break; case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9: if (vs->vd->lossy) { - vs->tight->quality = (enc & 0x0F); + vc->worker.tight.quality = (enc & 0x0F); } break; default: @@ -XXX,XX +XXX,XX @@ static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y) return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT]; } -void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h) +void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h) { int i, j; @@ -XXX,XX +XXX,XX @@ void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h) for (j = y; j <= h; j++) { for (i = x; i <= w; i++) { - vs->lossy_rect[j][i] = 1; + worker->lossy_rect[j][i] = 1; } } } @@ -XXX,XX +XXX,XX @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); QTAILQ_FOREACH(vs, &vd->clients, next) { + VncConnection *vc = container_of(vs, VncConnection, vs); int j; /* kernel send buffers are full -> refresh later */ @@ -XXX,XX +XXX,XX @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) continue; } - if (!vs->lossy_rect[sty][stx]) { + if (!vc->worker.lossy_rect[sty][stx]) { continue; } - vs->lossy_rect[sty][stx] = 0; + vc->worker.lossy_rect[sty][stx] = 0; for (j = 0; j < VNC_STAT_RECT; ++j) { bitmap_set(vs->dirty[y + j], x / VNC_DIRTY_PIXELS_PER_BIT, @@ -XXX,XX +XXX,XX @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, VncConnection *vc = g_new0(VncConnection, 1); VncState *vs = &vc->vs; bool first_client = QTAILQ_EMPTY(&vd->clients); - int i; trace_vnc_client_connect(vs, sioc); - vs->zlib = &vc->zlib; - vs->zrle = g_new0(VncZrle, 1); - vs->tight = g_new0(VncTight, 1); vs->magic = VNC_MAGIC; vs->sioc = sioc; object_ref(OBJECT(vs->sioc)); @@ -XXX,XX +XXX,XX @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, object_ref(OBJECT(vs->ioc)); vs->vd = vd; - buffer_init(&vs->input, "vnc-input/%p", sioc); - buffer_init(&vs->output, "vnc-output/%p", sioc); - buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc); + buffer_init(&vs->input, "vnc-input/%p", sioc); + buffer_init(&vs->output, "vnc-output/%p", sioc); + buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc); - buffer_init(&vs->tight->tight, "vnc-tight/%p", sioc); - buffer_init(&vs->tight->zlib, "vnc-tight-zlib/%p", sioc); - buffer_init(&vs->tight->gradient, "vnc-tight-gradient/%p", sioc); + buffer_init(&vc->worker.tight.tight, "vnc-tight/%p", sioc); + buffer_init(&vc->worker.tight.zlib, "vnc-tight-zlib/%p", sioc); + buffer_init(&vc->worker.tight.gradient, "vnc-tight-gradient/%p", sioc); #ifdef CONFIG_VNC_JPEG - buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc); + buffer_init(&vc->worker.tight.jpeg, "vnc-tight-jpeg/%p", sioc); #endif #ifdef CONFIG_PNG - buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc); + buffer_init(&vc->worker.tight.png, "vnc-tight-png/%p", sioc); #endif - buffer_init(&vc->zlib.zlib, "vnc-zlib/%p", sioc); - buffer_init(&vs->zrle->zrle, "vnc-zrle/%p", sioc); - buffer_init(&vs->zrle->fb, "vnc-zrle-fb/%p", sioc); - buffer_init(&vs->zrle->zlib, "vnc-zrle-zlib/%p", sioc); + buffer_init(&vc->worker.zlib.zlib, "vnc-zlib/%p", sioc); + buffer_init(&vc->worker.zrle.zrle, "vnc-zrle/%p", sioc); + buffer_init(&vc->worker.zrle.fb, "vnc-zrle-fb/%p", sioc); + buffer_init(&vc->worker.zrle.zlib, "vnc-zrle-zlib/%p", sioc); if (skipauth) { vs->auth = VNC_AUTH_NONE; @@ -XXX,XX +XXX,XX @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n", sioc, websocket, vs->auth, vs->subauth); - vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); - for (i = 0; i < VNC_STAT_ROWS; ++i) { - vs->lossy_rect[i] = g_new0(uint8_t, VNC_STAT_COLS); - } - 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, NULL); diff --git a/ui/vnc-enc-zrle.c.inc b/ui/vnc-enc-zrle.c.inc index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc-enc-zrle.c.inc +++ b/ui/vnc-enc-zrle.c.inc @@ -XXX,XX +XXX,XX @@ #define ZRLE_ENCODE_TILE ZRLE_CONCAT2(zrle_encode_tile, ZRLE_ENCODE_SUFFIX) #define ZRLE_WRITE_PALETTE ZRLE_CONCAT2(zrle_write_palette,ZRLE_ENCODE_SUFFIX) -static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, - int zywrle_level); +static void ZRLE_ENCODE_TILE(VncState *vs, VncZrle *zrle, ZRLE_PIXEL *data, + int w, int h, int zywrle_level); #if ZRLE_BPP != 8 #include "vnc-enc-zywrle-template.c" #endif -static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h, - int zywrle_level) +static void ZRLE_ENCODE(VncState *vs, VncZrle *zrle, + int x, int y, int w, int h, int zywrle_level) { int ty; @@ -XXX,XX +XXX,XX @@ static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h, tw = MIN(VNC_ZRLE_TILE_WIDTH, x + w - tx); - buf = zrle_convert_fb(vs, tx, ty, tw, th, ZRLE_BPP); - ZRLE_ENCODE_TILE(vs, buf, tw, th, zywrle_level); + buf = zrle_convert_fb(vs, zrle, tx, ty, tw, th, ZRLE_BPP); + ZRLE_ENCODE_TILE(vs, zrle, buf, tw, th, zywrle_level); } } } -static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, - int zywrle_level) +static void ZRLE_ENCODE_TILE(VncState *vs, VncZrle *zrle, ZRLE_PIXEL *data, + int w, int h, int zywrle_level) { - VncPalette *palette = &vs->zrle->palette; + VncPalette *palette = &zrle->palette; int runs = 0; int single_pixels = 0; @@ -XXX,XX +XXX,XX @@ static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, #if ZRLE_BPP != 8 if (zywrle_level > 0 && !(zywrle_level & 0x80)) { ZYWRLE_ANALYZE(data, data, w, h, w, zywrle_level, vs->zywrle.buf); - ZRLE_ENCODE_TILE(vs, data, w, h, zywrle_level | 0x80); + ZRLE_ENCODE_TILE(vs, zrle, data, w, h, zywrle_level | 0x80); } else #endif -- 2.50.0
From: Vivek Kasireddy <vivek.kasireddy@intel.com> While trying to export and obtain fds associated with a texture, it is possible that the fds returned after eglExportDMABUFImageMESA() call have error values. Therefore, we need to evaluate the value of all fds and return false if any of them are negative. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com> Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Cc: Michael Scherle <michael.scherle@rz.uni-freiburg.de> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Message-Id: <20250617043546.1022779-2-vivek.kasireddy@intel.com> --- ui/egl-helpers.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index XXXXXXX..XXXXXXX 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -XXX,XX +XXX,XX @@ bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset, { EGLImageKHR image; EGLuint64KHR modifiers[DMABUF_MAX_PLANES]; + int i; image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, @@ -XXX,XX +XXX,XX @@ bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset, *modifier = modifiers[0]; } + for (i = 0; i < *num_planes; i++) { + if (fd[i] < 0) { + return false; + } + } return true; } -- 2.50.0
From: Vivek Kasireddy <vivek.kasireddy@intel.com> Newer versions of Spice server should be able to accept dmabuf fds from Qemu for clients that are connected via the network. In other words, when this option is enabled, Qemu would share a dmabuf fd with Spice which would encode and send the data associated with the fd to a client that could be located on a different machine. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com> Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Cc: Michael Scherle <michael.scherle@rz.uni-freiburg.de> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Message-Id: <20250617043546.1022779-3-vivek.kasireddy@intel.com> --- include/ui/spice-display.h | 1 + ui/spice-core.c | 4 ++++ ui/spice-display.c | 1 + 3 files changed, 6 insertions(+) diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -XXX,XX +XXX,XX @@ struct SimpleSpiceCursor { }; extern bool spice_opengl; +extern bool spice_remote_client; int qemu_spice_rect_is_empty(const QXLRect* r); void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); diff --git a/ui/spice-core.c b/ui/spice-core.c index XXXXXXX..XXXXXXX 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -XXX,XX +XXX,XX @@ static void qemu_spice_init(void) #ifdef HAVE_SPICE_GL if (qemu_opt_get_bool(opts, "gl", 0)) { if ((port != 0) || (tls_port != 0)) { +#if SPICE_SERVER_VERSION >= 0x000f03 /* release 0.15.3 */ + spice_remote_client = 1; +#else error_report("SPICE GL support is local-only for now and " "incompatible with -spice port/tls-port"); exit(1); +#endif } egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal); spice_opengl = 1; diff --git a/ui/spice-display.c b/ui/spice-display.c index XXXXXXX..XXXXXXX 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -XXX,XX +XXX,XX @@ #include "standard-headers/drm/drm_fourcc.h" bool spice_opengl; +bool spice_remote_client; int qemu_spice_rect_is_empty(const QXLRect* r) { -- 2.50.0
From: Vivek Kasireddy <vivek.kasireddy@intel.com> Giving users an option to choose a particular codec will enable them to make an appropriate decision based on their hardware and use-case. Note that, the Spice server would use this codec with Gstreamer encoder and only when gl=on is specified. If no codec is provided, then the codec gstreamer:h264 would be used as default. And, for the case where gl=off, the default codec to be used is determined by the Spice server. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com> Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Cc: Michael Scherle <michael.scherle@rz.uni-freiburg.de> Cc: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Message-Id: <20250617043546.1022779-4-vivek.kasireddy@intel.com> --- ui/spice-core.c | 15 +++++++++++++++ qemu-options.hx | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/ui/spice-core.c b/ui/spice-core.c index XXXXXXX..XXXXXXX 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -XXX,XX +XXX,XX @@ static QemuOptsList qemu_spice_opts = { },{ .name = "streaming-video", .type = QEMU_OPT_STRING, + },{ + .name = "video-codec", + .type = QEMU_OPT_STRING, },{ .name = "agent-mouse", .type = QEMU_OPT_BOOL, @@ -XXX,XX +XXX,XX @@ static void qemu_spice_init(void) char *x509_key_file = NULL, *x509_cert_file = NULL, *x509_cacert_file = NULL; + const char *video_codec = NULL; + g_autofree char *enc_codec = NULL; int port, tls_port, addr_flags; spice_image_compression_t compression; spice_wan_compression_t wan_compr; @@ -XXX,XX +XXX,XX @@ static void qemu_spice_init(void) if ((port != 0) || (tls_port != 0)) { #if SPICE_SERVER_VERSION >= 0x000f03 /* release 0.15.3 */ spice_remote_client = 1; + + video_codec = qemu_opt_get(opts, "video-codec"); + if (video_codec) { + enc_codec = g_strconcat("gstreamer:", video_codec, NULL); + } + if (spice_server_set_video_codecs(spice_server, + enc_codec ?: "gstreamer:h264")) { + error_report("invalid video codec"); + exit(1); + } #else error_report("SPICE GL support is local-only for now and " "incompatible with -spice port/tls-port"); diff --git a/qemu-options.hx b/qemu-options.hx index XXXXXXX..XXXXXXX 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -XXX,XX +XXX,XX @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,streaming-video=[off|all|filter]][,disable-copy-paste=on|off]\n" " [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n" " [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n" + " [,video-codec=<codec>\n" " [,gl=[on|off]][,rendernode=<file>]\n" " enable spice\n" " at least one of {port, tls-port} is mandatory\n", @@ -XXX,XX +XXX,XX @@ SRST ``seamless-migration=[on|off]`` Enable/disable spice seamless migration. Default is off. + ``video-codec=<codec>`` + Provide the preferred codec the Spice server should use with the + Gstreamer encoder. This option is only relevant when gl=on is + specified. If no codec is provided, then the codec gstreamer:h264 + would be used as default. And, for the case where gl=off, the + default codec to be used is determined by the Spice server. + ``gl=[on|off]`` Enable/disable OpenGL context. Default is off. -- 2.50.0
From: Vivek Kasireddy <vivek.kasireddy@intel.com> In the specific case where the display layer (virtio-gpu) is using dmabuf, and if remote clients are enabled (-spice gl=on,port=xxxx), it makes sense to limit the maximum (streaming) rate (refresh rate) to a fixed value using the GUI refresh timer. Otherwise, the updates or gl_draw requests would be sent as soon as the Guest submits a new frame which is not optimal as it would lead to increased network traffic and wastage of GPU cycles if the frames get dropped. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com> Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Cc: Michael Scherle <michael.scherle@rz.uni-freiburg.de> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Message-Id: <20250617043546.1022779-5-vivek.kasireddy@intel.com> --- include/ui/spice-display.h | 1 + ui/spice-core.c | 12 ++++++++ ui/spice-display.c | 62 ++++++++++++++++++++++++++++++++------ qemu-options.hx | 5 +++ 4 files changed, 70 insertions(+), 10 deletions(-) diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -XXX,XX +XXX,XX @@ struct SimpleSpiceCursor { extern bool spice_opengl; extern bool spice_remote_client; +extern int spice_max_refresh_rate; int qemu_spice_rect_is_empty(const QXLRect* r); void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); diff --git a/ui/spice-core.c b/ui/spice-core.c index XXXXXXX..XXXXXXX 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -XXX,XX +XXX,XX @@ struct SpiceTimer { QEMUTimer *timer; }; +#define DEFAULT_MAX_REFRESH_RATE 30 + static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque) { SpiceTimer *timer; @@ -XXX,XX +XXX,XX @@ static QemuOptsList qemu_spice_opts = { },{ .name = "video-codec", .type = QEMU_OPT_STRING, + },{ + .name = "max-refresh-rate", + .type = QEMU_OPT_NUMBER, },{ .name = "agent-mouse", .type = QEMU_OPT_BOOL, @@ -XXX,XX +XXX,XX @@ static void qemu_spice_init(void) spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF); } + spice_max_refresh_rate = qemu_opt_get_number(opts, "max-refresh-rate", + DEFAULT_MAX_REFRESH_RATE); + if (spice_max_refresh_rate <= 0) { + error_report("max refresh rate/fps is invalid"); + exit(1); + } + spice_server_set_agent_mouse (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1)); spice_server_set_playback_compression diff --git a/ui/spice-display.c b/ui/spice-display.c index XXXXXXX..XXXXXXX 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -XXX,XX +XXX,XX @@ bool spice_opengl; bool spice_remote_client; +int spice_max_refresh_rate; int qemu_spice_rect_is_empty(const QXLRect* r) { @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_block_timer(void *opaque) warn_report("spice: no gl-draw-done within one second"); } +static void spice_gl_draw(SimpleSpiceDisplay *ssd, + uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + uint64_t cookie; + + cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); + spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); +} + static void spice_gl_refresh(DisplayChangeListener *dcl) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - uint64_t cookie; - if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) { + if (!ssd->ds) { + return; + } + + if (qemu_console_is_gl_blocked(ssd->dcl.con)) { + if (spice_remote_client && ssd->gl_updates && ssd->have_scanout) { + glFlush(); + spice_gl_draw(ssd, 0, 0, + surface_width(ssd->ds), surface_height(ssd->ds)); + ssd->gl_updates = 0; + /* E.g, to achieve 60 FPS, update_interval needs to be ~16.66 ms */ + dcl->update_interval = 1000 / spice_max_refresh_rate; + } return; } @@ -XXX,XX +XXX,XX @@ static void spice_gl_refresh(DisplayChangeListener *dcl) if (ssd->gl_updates && ssd->have_surface) { qemu_spice_gl_block(ssd, true); glFlush(); - cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); - spice_qxl_gl_draw_async(&ssd->qxl, 0, 0, - surface_width(ssd->ds), - surface_height(ssd->ds), - cookie); + spice_gl_draw(ssd, 0, 0, + surface_width(ssd->ds), surface_height(ssd->ds)); ssd->gl_updates = 0; } } @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); trace_qemu_spice_gl_scanout_disable(ssd->qxl.id); + + /* + * We need to check for the case of "lost" updates, where a gl_draw + * was not submitted because the timer did not get a chance to run. + * One case where this happens is when the Guest VM is getting + * rebooted. If the console is blocked in this situation, we need + * to unblock it. Otherwise, newer updates would not take effect. + */ + if (qemu_console_is_gl_blocked(ssd->dcl.con)) { + if (spice_remote_client && ssd->gl_updates && ssd->have_scanout) { + ssd->gl_updates = 0; + qemu_spice_gl_block(ssd, false); + } + } spice_server_gl_scanout(&ssd->qxl, NULL, 0, 0, NULL, NULL, 0, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, false); qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0); @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, EGLint fourcc = 0; bool render_cursor = false; bool y_0_top = false; /* FIXME */ - uint64_t cookie; uint32_t width, height, texture; if (!ssd->have_scanout) { @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); qemu_spice_gl_block(ssd, true); glFlush(); - cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); - spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); + + /* + * In the case of remote clients, the submission of gl_draw request is + * deferred here, so that it can be submitted later (to spice server) + * from spice_gl_refresh() timer callback. This is done to ensure that + * Guest updates are submitted at a steady rate (e.g. 60 FPS) instead + * of submitting them arbitrarily. + */ + if (spice_remote_client) { + ssd->gl_updates++; + } else { + spice_gl_draw(ssd, x, y, w, h); + } } static const DisplayChangeListenerOps display_listener_gl_ops = { diff --git a/qemu-options.hx b/qemu-options.hx index XXXXXXX..XXXXXXX 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -XXX,XX +XXX,XX @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n" " [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n" " [,video-codec=<codec>\n" + " [,max-refresh-rate=rate\n" " [,gl=[on|off]][,rendernode=<file>]\n" " enable spice\n" " at least one of {port, tls-port} is mandatory\n", @@ -XXX,XX +XXX,XX @@ SRST would be used as default. And, for the case where gl=off, the default codec to be used is determined by the Spice server. + ``max-refresh-rate=rate`` + Provide the maximum refresh rate (or FPS) at which the encoding + requests should be sent to the Spice server. Default would be 30. + ``gl=[on|off]`` Enable/disable OpenGL context. Default is off. -- 2.50.0
From: Vivek Kasireddy <vivek.kasireddy@intel.com> There are cases where we do not want the memory layout of a texture to be tiled as the component processing the texture would not know how to de-tile either via software or hardware. Therefore, ensuring that the memory backing the texture has a linear layout is absolutely necessary in these situations. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com> Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Co-developed-by: Michael Scherle <michael.scherle@rz.uni-freiburg.de> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Message-Id: <20250617043546.1022779-6-vivek.kasireddy@intel.com> --- include/ui/console.h | 3 +++ ui/console-gl.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/include/ui/console.h b/include/ui/console.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -XXX,XX +XXX,XX @@ bool console_gl_check_format(DisplayChangeListener *dcl, pixman_format_code_t format); void surface_gl_create_texture(QemuGLShader *gls, DisplaySurface *surface); +bool surface_gl_create_texture_from_fd(DisplaySurface *surface, + int fd, GLuint *texture, + GLuint *mem_obj); void surface_gl_update_texture(QemuGLShader *gls, DisplaySurface *surface, int x, int y, int w, int h); diff --git a/ui/console-gl.c b/ui/console-gl.c index XXXXXXX..XXXXXXX 100644 --- a/ui/console-gl.c +++ b/ui/console-gl.c @@ -XXX,XX +XXX,XX @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "ui/console.h" #include "ui/shader.h" @@ -XXX,XX +XXX,XX @@ void surface_gl_create_texture(QemuGLShader *gls, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } +bool surface_gl_create_texture_from_fd(DisplaySurface *surface, + int fd, GLuint *texture, + GLuint *mem_obj) +{ + unsigned long size = surface_stride(surface) * surface_height(surface); + GLenum err = glGetError(); + *texture = 0; + *mem_obj = 0; + + if (!epoxy_has_gl_extension("GL_EXT_memory_object") || + !epoxy_has_gl_extension("GL_EXT_memory_object_fd")) { + error_report("spice: required OpenGL extensions not supported: " + "GL_EXT_memory_object and GL_EXT_memory_object_fd"); + return false; + } + +#ifdef GL_EXT_memory_object_fd + glCreateMemoryObjectsEXT(1, mem_obj); + glImportMemoryFdEXT(*mem_obj, size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd); + + err = glGetError(); + if (err != GL_NO_ERROR) { + error_report("spice: cannot import memory object from fd"); + goto cleanup_mem; + } + + glGenTextures(1, texture); + glBindTexture(GL_TEXTURE_2D, *texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_TILING_EXT, GL_LINEAR_TILING_EXT); + glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, surface_width(surface), + surface_height(surface), *mem_obj, 0); + err = glGetError(); + if (err != GL_NO_ERROR) { + error_report("spice: cannot create texture from memory object"); + goto cleanup_tex_and_mem; + } + return true; + +cleanup_tex_and_mem: + glDeleteTextures(1, texture); +cleanup_mem: + glDeleteMemoryObjectsEXT(1, mem_obj); + +#endif + return false; +} + void surface_gl_update_texture(QemuGLShader *gls, DisplaySurface *surface, int x, int y, int w, int h) -- 2.50.0
From: Vivek Kasireddy <vivek.kasireddy@intel.com> Since most encoders/decoders (invoked by Spice) may not work properly with tiled memory associated with a texture, we need to create another texture that has linear memory layout and use that instead. Note that, there does not seem to be a direct way to indicate to the GL implementation that a texture's backing memory needs to be linear. Instead, we have to do it in a roundabout way where we need to first create a tiled texture and import that as a memory object to create a new texture that has a linear memory layout. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com> Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Co-developed-by: Michael Scherle <michael.scherle@rz.uni-freiburg.de> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Message-Id: <20250617043546.1022779-7-vivek.kasireddy@intel.com> --- include/ui/surface.h | 1 + ui/console-gl.c | 6 ++++ ui/spice-display.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/include/ui/surface.h b/include/ui/surface.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/surface.h +++ b/include/ui/surface.h @@ -XXX,XX +XXX,XX @@ typedef struct DisplaySurface { GLenum glformat; GLenum gltype; GLuint texture; + GLuint mem_obj; #endif qemu_pixman_shareable share_handle; uint32_t share_handle_offset; diff --git a/ui/console-gl.c b/ui/console-gl.c index XXXXXXX..XXXXXXX 100644 --- a/ui/console-gl.c +++ b/ui/console-gl.c @@ -XXX,XX +XXX,XX @@ void surface_gl_destroy_texture(QemuGLShader *gls, } glDeleteTextures(1, &surface->texture); surface->texture = 0; +#ifdef GL_EXT_memory_object_fd + if (surface->mem_obj) { + glDeleteMemoryObjectsEXT(1, &surface->mem_obj); + surface->mem_obj = 0; + } +#endif } void surface_gl_setup_viewport(QemuGLShader *gls, diff --git a/ui/spice-display.c b/ui/spice-display.c index XXXXXXX..XXXXXXX 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -XXX,XX +XXX,XX @@ static void spice_gl_update(DisplayChangeListener *dcl, ssd->gl_updates++; } +static bool spice_gl_replace_fd_texture(SimpleSpiceDisplay *ssd, + int *fds, uint64_t *modifier, + int *num_planes) +{ + uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; + GLuint texture; + GLuint mem_obj; + int fourcc; + bool ret; + + if (!spice_remote_client) { + return true; + } + + if (*modifier == DRM_FORMAT_MOD_LINEAR) { + return true; + } + + if (*num_planes > 1) { + error_report("spice: cannot replace texture with multiple planes"); + return false; + } + + /* + * We really want to ensure that the memory layout of the texture + * is linear; otherwise, the encoder's output may show corruption. + */ + if (!surface_gl_create_texture_from_fd(ssd->ds, fds[0], &texture, + &mem_obj)) { + error_report("spice: cannot create new texture from fd"); + return false; + } + + /* + * A successful return after glImportMemoryFdEXT() means that + * the ownership of fd has been passed to GL. In other words, + * the fd we got above should not be used anymore. + */ + ret = egl_dmabuf_export_texture(texture, + fds, + (EGLint *)offsets, + (EGLint *)strides, + &fourcc, + num_planes, + modifier); + if (!ret) { + glDeleteTextures(1, &texture); +#ifdef GL_EXT_memory_object_fd + glDeleteMemoryObjectsEXT(1, &mem_obj); +#endif + + /* + * Since we couldn't export our newly create texture (or create, + * an fd associated with it) we need to backtrack and try to + * recreate the fd associated with the original texture. + */ + ret = egl_dmabuf_export_texture(ssd->ds->texture, + fds, + (EGLint *)offsets, + (EGLint *)strides, + &fourcc, + num_planes, + modifier); + if (!ret) { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + warn_report("spice: no texture available to display"); + } + } else { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + ssd->ds->texture = texture; + ssd->ds->mem_obj = mem_obj; + } + return ret; +} + static void spice_server_gl_scanout(QXLInstance *qxl, const int *fd, uint32_t width, uint32_t height, @@ -XXX,XX +XXX,XX @@ static void spice_gl_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + bool ret; if (ssd->ds) { surface_gl_destroy_texture(ssd->gls, ssd->ds); @@ -XXX,XX +XXX,XX @@ static void spice_gl_switch(DisplayChangeListener *dcl, return; } + ret = spice_gl_replace_fd_texture(ssd, fd, &modifier, &num_planes); + if (!ret) { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + return; + } + trace_qemu_spice_gl_surface(ssd->qxl.id, surface_width(ssd->ds), surface_height(ssd->ds), -- 2.50.0
From: Vivek Kasireddy <vivek.kasireddy@intel.com> In cases where the scanout buffer is provided as a texture (e.g. Virgl) we need to check to see if it has a linear memory layout or not. If it doesn't have a linear layout, then blitting it onto the texture associated with the display surface (which already has a linear layout) seems to ensure that there is no corruption seen regardless of which encoder or decoder is used. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Dmitry Osipenko <dmitry.osipenko@collabora.com> Cc: Frediano Ziglio <freddy77@gmail.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Cc: Michael Scherle <michael.scherle@rz.uni-freiburg.de> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> Message-Id: <20250617043546.1022779-8-vivek.kasireddy@intel.com> --- include/ui/spice-display.h | 3 ++ ui/spice-display.c | 81 +++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -XXX,XX +XXX,XX @@ struct SimpleSpiceDisplay { egl_fb guest_fb; egl_fb blit_fb; egl_fb cursor_fb; + bool backing_y_0_top; + bool blit_scanout_texture; + bool new_scanout_texture; bool have_hot; #endif }; diff --git a/ui/spice-display.c b/ui/spice-display.c index XXXXXXX..XXXXXXX 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0; - int fd[DMABUF_MAX_PLANES], num_planes; + int fd[DMABUF_MAX_PLANES], num_planes, i; uint64_t modifier; assert(tex_id); @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc); - /* note: spice server will close the fd */ - spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, - (uint32_t *)offset, (uint32_t *)stride, num_planes, - fourcc, modifier, y_0_top); - qemu_spice_gl_monitor_config(ssd, x, y, w, h); + if (spice_remote_client && modifier != DRM_FORMAT_MOD_LINEAR) { + egl_fb_destroy(&ssd->guest_fb); + egl_fb_setup_for_tex(&ssd->guest_fb, + backing_width, backing_height, + tex_id, false); + ssd->backing_y_0_top = y_0_top; + ssd->blit_scanout_texture = true; + ssd->new_scanout_texture = true; + + for (i = 0; i < num_planes; i++) { + close(fd[i]); + } + } else { + /* note: spice server will close the fd */ + spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, + (uint32_t *)offset, (uint32_t *)stride, + num_planes, fourcc, modifier, y_0_top); + qemu_spice_gl_monitor_config(ssd, x, y, w, h); + } + ssd->have_surface = false; ssd->have_scanout = true; } @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl, egl_dmabuf_release_texture(dmabuf); } +static bool spice_gl_blit_scanout_texture(SimpleSpiceDisplay *ssd, + egl_fb *scanout_tex_fb) +{ + uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; + int fds[DMABUF_MAX_PLANES], num_planes, fourcc; + uint64_t modifier; + bool ret; + + egl_fb_destroy(scanout_tex_fb); + egl_fb_setup_for_tex(scanout_tex_fb, + surface_width(ssd->ds), surface_height(ssd->ds), + ssd->ds->texture, false); + egl_fb_blit(scanout_tex_fb, &ssd->guest_fb, false); + glFlush(); + + if (!ssd->new_scanout_texture) { + return true; + } + + ret = egl_dmabuf_export_texture(ssd->ds->texture, + fds, + (EGLint *)offsets, + (EGLint *)strides, + &fourcc, + &num_planes, + &modifier); + if (!ret) { + error_report("spice: failed to get fd for texture"); + return false; + } + + spice_server_gl_scanout(&ssd->qxl, fds, + surface_width(ssd->ds), + surface_height(ssd->ds), + (uint32_t *)offsets, (uint32_t *)strides, + num_planes, fourcc, modifier, + ssd->backing_y_0_top); + qemu_spice_gl_monitor_config(ssd, 0, 0, + surface_width(ssd->ds), + surface_height(ssd->ds)); + ssd->new_scanout_texture = false; + return true; +} + static void qemu_spice_gl_update(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, EGLint fourcc = 0; bool render_cursor = false; bool y_0_top = false; /* FIXME */ + bool ret; uint32_t width, height, texture; if (!ssd->have_scanout) { @@ -XXX,XX +XXX,XX @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, glFlush(); } + if (spice_remote_client && ssd->blit_scanout_texture) { + egl_fb scanout_tex_fb; + + ret = spice_gl_blit_scanout_texture(ssd, &scanout_tex_fb); + if (!ret) { + return; + } + } + trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); qemu_spice_gl_block(ssd, true); glFlush(); -- 2.50.0
From: Andrew Keesler <ankeesler@google.com> Thanks to 72d277a7, 1ed2cb32, and others, EDID (Extended Display Identification Data) is propagated by QEMU such that a virtual display presents legitimate metadata (e.g., name, serial number, preferred resolutions, etc.) to its connected guest. This change adds the ability to specify the EDID name for a particular virtio-vga display. Previously, every virtual display would have the same name: "QEMU Monitor". Now, we can inject names of displays in order to test guest behavior that is specific to display names. We provide the ability to inject the display name from the frontend since this is guest visible data. Furthermore, this makes it clear where N potential display outputs would get their name from (which will be added in a future change). Note that we have elected to use a struct here for output data for extensibility - we intend to add per-output fields like resolution in a future change. It should be noted that EDID names longer than 12 bytes will be truncated per spec (I think?). Testing: verified that when I specified 2 outputs for a virtio-gpu with edid_name set, the names matched those that I configured with my vnc display. -display vnc=localhost:0,id=aaa,display=vga,head=0 \ -display vnc=localhost:1,id=bbb,display=vga,head=1 \ -device '{"driver":"virtio-vga", "max_outputs":2, "id":"vga", "outputs":[ { "name":"AAA" }, { "name":"BBB" } ]}' Signed-off-by: Andrew Keesler <ankeesler@google.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20250709121126.2946088-2-ankeesler@google.com> --- qapi/virtio.json | 18 ++++++++++-- include/hw/display/edid.h | 2 ++ include/hw/qdev-properties-system.h | 5 ++++ include/hw/virtio/virtio-gpu.h | 3 ++ hw/core/qdev-properties-system.c | 44 +++++++++++++++++++++++++++++ hw/display/virtio-gpu-base.c | 27 ++++++++++++++++++ 6 files changed, 97 insertions(+), 2 deletions(-) diff --git a/qapi/virtio.json b/qapi/virtio.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -XXX,XX +XXX,XX @@ { 'struct': 'IOThreadVirtQueueMapping', 'data': { 'iothread': 'str', '*vqs': ['uint16'] } } +## +# @VirtIOGPUOutput: +# +# Describes configuration of a VirtIO GPU output. +# +# @name: the name of the output +# +# Since: 10.1 +## + +{ 'struct': 'VirtIOGPUOutput', + 'data': { 'name': 'str' } } + ## # @DummyVirtioForceArrays: # # Not used by QMP; hack to let us use IOThreadVirtQueueMappingList -# internally +# and VirtIOGPUOutputList internally # # Since: 9.0 ## { 'struct': 'DummyVirtioForceArrays', - 'data': { 'unused-iothread-vq-mapping': ['IOThreadVirtQueueMapping'] } } + 'data': { 'unused-iothread-vq-mapping': ['IOThreadVirtQueueMapping'], + 'unused-virtio-gpu-output': ['VirtIOGPUOutput'] } } ## # @GranuleMode: diff --git a/include/hw/display/edid.h b/include/hw/display/edid.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/display/edid.h +++ b/include/hw/display/edid.h @@ -XXX,XX +XXX,XX @@ #ifndef EDID_H #define EDID_H +#define EDID_NAME_MAX_LENGTH 12 + typedef struct qemu_edid_info { const char *vendor; /* http://www.uefi.org/pnp_id_list */ const char *name; diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -XXX,XX +XXX,XX @@ extern const PropertyInfo qdev_prop_cpus390entitlement; extern const PropertyInfo qdev_prop_iothread_vq_mapping_list; extern const PropertyInfo qdev_prop_endian_mode; extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant; +extern const PropertyInfo qdev_prop_virtio_gpu_output_list; #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) @@ -XXX,XX +XXX,XX @@ extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant; qdev_prop_vmapple_virtio_blk_variant, \ VMAppleVirtioBlkVariant) +#define DEFINE_PROP_VIRTIO_GPU_OUTPUT_LIST(_name, _state, _field) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_virtio_gpu_output_list, \ + VirtIOGPUOutputList *) + #endif diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -XXX,XX +XXX,XX @@ #include "hw/virtio/virtio.h" #include "qemu/log.h" #include "system/vhost-user-backend.h" +#include "qapi/qapi-types-virtio.h" #include "standard-headers/linux/virtio_gpu.h" #include "standard-headers/linux/virtio_ids.h" @@ -XXX,XX +XXX,XX @@ struct virtio_gpu_base_conf { uint32_t xres; uint32_t yres; uint64_t hostmem; + VirtIOGPUOutputList *outputs; }; struct virtio_gpu_ctrl_command { @@ -XXX,XX +XXX,XX @@ struct VirtIOGPUBaseClass { #define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf) \ DEFINE_PROP_UINT32("max_outputs", _state, _conf.max_outputs, 1), \ + DEFINE_PROP_VIRTIO_GPU_OUTPUT_LIST("outputs", _state, _conf.outputs), \ DEFINE_PROP_BIT("edid", _state, _conf.flags, \ VIRTIO_GPU_FLAG_EDID_ENABLED, true), \ DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1280), \ diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index XXXXXXX..XXXXXXX 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -XXX,XX +XXX,XX @@ const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = { .set = qdev_propinfo_set_enum, .set_default_value = qdev_propinfo_set_default_value_enum, }; + +/* --- VirtIOGPUOutputList --- */ + +static void get_virtio_gpu_output_list(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + VirtIOGPUOutputList **prop_ptr = + object_field_prop_ptr(obj, opaque); + + visit_type_VirtIOGPUOutputList(v, name, prop_ptr, errp); +} + +static void set_virtio_gpu_output_list(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + VirtIOGPUOutputList **prop_ptr = + object_field_prop_ptr(obj, opaque); + VirtIOGPUOutputList *list; + + if (!visit_type_VirtIOGPUOutputList(v, name, &list, errp)) { + return; + } + + qapi_free_VirtIOGPUOutputList(*prop_ptr); + *prop_ptr = list; +} + +static void release_virtio_gpu_output_list(Object *obj, + const char *name, void *opaque) +{ + VirtIOGPUOutputList **prop_ptr = + object_field_prop_ptr(obj, opaque); + + qapi_free_VirtIOGPUOutputList(*prop_ptr); + *prop_ptr = NULL; +} + +const PropertyInfo qdev_prop_virtio_gpu_output_list = { + .type = "VirtIOGPUOutputList", + .description = "VirtIO GPU output list [{\"name\":\"<name>\"},...]", + .get = get_virtio_gpu_output_list, + .set = set_virtio_gpu_output_list, + .release = release_virtio_gpu_output_list, +}; diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -XXX,XX +XXX,XX @@ #include "qemu/error-report.h" #include "hw/display/edid.h" #include "trace.h" +#include "qapi/qapi-types-virtio.h" void virtio_gpu_base_reset(VirtIOGPUBase *g) @@ -XXX,XX +XXX,XX @@ void virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, struct virtio_gpu_resp_edid *edid) { + size_t output_idx; + VirtIOGPUOutputList *node; qemu_edid_info info = { .width_mm = g->req_state[scanout].width_mm, .height_mm = g->req_state[scanout].height_mm, @@ -XXX,XX +XXX,XX @@ virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, .refresh_rate = g->req_state[scanout].refresh_rate, }; + for (output_idx = 0, node = g->conf.outputs; + output_idx <= scanout && node; output_idx++, node = node->next) { + if (output_idx == scanout && node->value && node->value->name) { + info.name = node->value->name; + break; + } + } + edid->size = cpu_to_le32(sizeof(edid->edid)); qemu_edid_generate(edid->edid, sizeof(edid->edid), &info); } @@ -XXX,XX +XXX,XX @@ virtio_gpu_base_device_realize(DeviceState *qdev, VirtIOHandleOutput cursor_cb, Error **errp) { + size_t output_idx; + VirtIOGPUOutputList *node; VirtIODevice *vdev = VIRTIO_DEVICE(qdev); VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev); int i; @@ -XXX,XX +XXX,XX @@ virtio_gpu_base_device_realize(DeviceState *qdev, return false; } + for (output_idx = 0, node = g->conf.outputs; + node; output_idx++, node = node->next) { + if (output_idx == g->conf.max_outputs) { + error_setg(errp, "invalid outputs > %d", g->conf.max_outputs); + return false; + } + if (node->value && node->value->name && + strlen(node->value->name) > EDID_NAME_MAX_LENGTH) { + error_setg(errp, "invalid output name '%s' > %d", + node->value->name, EDID_NAME_MAX_LENGTH); + return false; + } + } + if (virtio_gpu_virgl_enabled(g->conf)) { error_setg(&g->migration_blocker, "virgl is not yet migratable"); if (migrate_add_blocker(&g->migration_blocker, errp) < 0) { -- 2.50.0
From: Weifeng Liu <weifeng.liu.z@gmail.com> When aspect ratio of host window and that of guest display are not aligned, we can either zoom the guest content to fill the whole host window or add padding to respect aspect ratio of the guest. Add an option keep-aspect-ratio to allow users to select their preferred behavior in this case. Suggested-by: BALATON Zoltan <balaton@eik.bme.hu> Suggested-by: Kim, Dongwon <dongwon.kim@intel.com> Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Tested-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20250601045245.36778-2-weifeng.liu.z@gmail.com> --- qapi/ui.json | 12 ++++++++---- include/ui/gtk.h | 1 + ui/gtk.c | 12 ++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -XXX,XX +XXX,XX @@ # @show-menubar: Display the main window menubar. Defaults to "on". # (Since 8.0) # +# @keep-aspect-ratio: Keep width/height aspect ratio of guest content when +# resizing host window. Defaults to "on". (Since 10.1) +# # Since: 2.12 ## { 'struct' : 'DisplayGTK', - 'data' : { '*grab-on-hover' : 'bool', - '*zoom-to-fit' : 'bool', - '*show-tabs' : 'bool', - '*show-menubar' : 'bool' } } + 'data' : { '*grab-on-hover' : 'bool', + '*zoom-to-fit' : 'bool', + '*show-tabs' : 'bool', + '*show-menubar' : 'bool', + '*keep-aspect-ratio' : 'bool' } } ## # @DisplayEGLHeadless: diff --git a/include/ui/gtk.h b/include/ui/gtk.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -XXX,XX +XXX,XX @@ struct GtkDisplayState { GdkCursor *null_cursor; Notifier mouse_mode_notifier; gboolean free_scale; + gboolean keep_aspect_ratio; bool external_pause_update; diff --git a/ui/gtk.c b/ui/gtk.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -XXX,XX +XXX,XX @@ void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh) sx = (double)ww / fbw; sy = (double)wh / fbh; - - vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); + if (vc->s->keep_aspect_ratio) { + vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); + } else { + vc->gfx.scale_x = sx; + vc->gfx.scale_y = sy; + } } } /** @@ -XXX,XX +XXX,XX @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, s->free_scale = true; } + s->keep_aspect_ratio = true; + if (s->opts->u.gtk.has_keep_aspect_ratio) + s->keep_aspect_ratio = s->opts->u.gtk.keep_aspect_ratio; + for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) { struct touch_slot *slot = &touch_slots[i]; slot->tracking_id = -1; -- 2.50.0
From: Weifeng Liu <weifeng.liu.z@gmail.com> Allow user to set a preferred scale (defaulting to 1) of the virtual display. Along with zoom-to-fix=false, this would be helpful for users running QEMU on hi-dpi host desktop to achieve pixel to pixel display -- e.g., if the scale factor of a user's host desktop is set to 200%, then they can set a 0.5 scale for the virtual display to avoid magnification that might cause blurriness. Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Tested-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20250601045245.36778-3-weifeng.liu.z@gmail.com> --- qapi/ui.json | 5 ++++- include/ui/gtk.h | 1 + ui/gtk.c | 46 +++++++++++++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -XXX,XX +XXX,XX @@ # @keep-aspect-ratio: Keep width/height aspect ratio of guest content when # resizing host window. Defaults to "on". (Since 10.1) # +# @scale: Set preferred scale of the display. Defaults to 1.0. (Since 10.1) +# # Since: 2.12 ## { 'struct' : 'DisplayGTK', @@ -XXX,XX +XXX,XX @@ '*zoom-to-fit' : 'bool', '*show-tabs' : 'bool', '*show-menubar' : 'bool', - '*keep-aspect-ratio' : 'bool' } } + '*keep-aspect-ratio' : 'bool', + '*scale' : 'number' } } ## # @DisplayEGLHeadless: diff --git a/include/ui/gtk.h b/include/ui/gtk.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -XXX,XX +XXX,XX @@ typedef struct VirtualGfxConsole { DisplaySurface *ds; pixman_image_t *convert; cairo_surface_t *surface; + double preferred_scale; double scale_x; double scale_y; #if defined(CONFIG_OPENGL) diff --git a/ui/gtk.c b/ui/gtk.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -XXX,XX +XXX,XX @@ #define VC_TERM_X_MIN 80 #define VC_TERM_Y_MIN 25 #define VC_SCALE_MIN 0.25 +#define VC_SCALE_MAX 4 #define VC_SCALE_STEP 0.25 #ifdef GDK_WINDOWING_X11 @@ -XXX,XX +XXX,XX @@ static void gd_update_geometry_hints(VirtualConsole *vc) if (!vc->gfx.ds) { return; } - if (s->free_scale) { - geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN; - geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN; - mask |= GDK_HINT_MIN_SIZE; - } else { - geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x; - geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - mask |= GDK_HINT_MIN_SIZE; - } + double scale_x = s->free_scale ? VC_SCALE_MIN : vc->gfx.scale_x; + double scale_y = s->free_scale ? VC_SCALE_MIN : vc->gfx.scale_y; + geo.min_width = surface_width(vc->gfx.ds) * scale_x; + geo.min_height = surface_height(vc->gfx.ds) * scale_y; + mask |= GDK_HINT_MIN_SIZE; geo_widget = vc->gfx.drawing_area; gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height); @@ -XXX,XX +XXX,XX @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) } s->full_screen = FALSE; if (vc->type == GD_VC_GFX) { - vc->gfx.scale_x = 1.0; - vc->gfx.scale_y = 1.0; + vc->gfx.scale_x = vc->gfx.preferred_scale; + vc->gfx.scale_y = vc->gfx.preferred_scale; gd_update_windowsize(vc); } } @@ -XXX,XX +XXX,XX @@ static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) GtkDisplayState *s = opaque; VirtualConsole *vc = gd_vc_find_current(s); - vc->gfx.scale_x = 1.0; - vc->gfx.scale_y = 1.0; + vc->gfx.scale_x = vc->gfx.preferred_scale; + vc->gfx.scale_y = vc->gfx.preferred_scale; gd_update_windowsize(vc); } @@ -XXX,XX +XXX,XX @@ static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) s->free_scale = TRUE; } else { s->free_scale = FALSE; - vc->gfx.scale_x = 1.0; - vc->gfx.scale_y = 1.0; + vc->gfx.scale_x = vc->gfx.preferred_scale; + vc->gfx.scale_y = vc->gfx.preferred_scale; } gd_update_windowsize(vc); @@ -XXX,XX +XXX,XX @@ static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc) } #endif +static bool gd_scale_valid(double scale) +{ + return scale >= VC_SCALE_MIN && scale <= VC_SCALE_MAX; +} + static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, QemuConsole *con, int idx, GSList *group, GtkWidget *view_menu) @@ -XXX,XX +XXX,XX @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, vc->label = qemu_console_get_label(con); vc->s = s; - vc->gfx.scale_x = 1.0; - vc->gfx.scale_y = 1.0; + vc->gfx.preferred_scale = 1.0; + if (s->opts->u.gtk.has_scale) { + if (gd_scale_valid(s->opts->u.gtk.scale)) { + vc->gfx.preferred_scale = s->opts->u.gtk.scale; + } else { + error_report("Invalid scale value %lf given, being ignored", + s->opts->u.gtk.scale); + s->opts->u.gtk.has_scale = false; + } + } + vc->gfx.scale_x = vc->gfx.preferred_scale; + vc->gfx.scale_y = vc->gfx.preferred_scale; #if defined(CONFIG_OPENGL) if (display_opengl) { -- 2.50.0
From: Marc-André Lureau <marcandre.lureau@redhat.com> Like other "-foo help" CLI, the qemu process should return 0 for "-tpmdev help". While touching this, switch to is_help_option() utility function as suggested by Peter Maydell. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-Id: <20250707101412.2055581-1-marcandre.lureau@redhat.com> --- system/tpm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/system/tpm.c b/system/tpm.c index XXXXXXX..XXXXXXX 100644 --- a/system/tpm.c +++ b/system/tpm.c @@ -XXX,XX +XXX,XX @@ #include "system/tpm.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/help_option.h" static QLIST_HEAD(, TPMBackend) tpm_backends = QLIST_HEAD_INITIALIZER(tpm_backends); @@ -XXX,XX +XXX,XX @@ int tpm_config_parse(QemuOptsList *opts_list, const char *optstr) { QemuOpts *opts; - if (!strcmp(optstr, "help")) { + if (is_help_option(optstr)) { tpm_display_backend_drivers(); - return -1; + exit(EXIT_SUCCESS); } opts = qemu_opts_parse_noisily(opts_list, optstr, true); if (!opts) { -- 2.50.0
From: Marc-André Lureau <marcandre.lureau@redhat.com> The following changes since commit 3f129ea545f16e82b5e43fcc3866b134b7cc4dfc: Merge tag 'pull-vfio-20260520' of https://github.com/legoater/qemu into staging (2026-05-20 16:53:28 -0400) are available in the Git repository at: https://gitlab.com/marcandre.lureau/qemu.git tags/ui-pull-request for you to fetch changes up to 64abff825b6fab9366b582f4d54eb2d12985c0b7: ui/gtk: Fix focus loss on re-attachment with single VC (2026-05-21 15:29:58 +0400) ---------------------------------------------------------------- UI pull request - ui/input: Decouple internal and QAPI input events - VNC OOB fixes - vt100 fixes - GTK focus fix ---------------------------------------------------------------- Akihiko Odaki (29): ui/input: Introduce QemuInputEvent typedef ui/input: Remove QAPI wrappers from QemuInputEvent ui/input: Store QKeyCode directly in QemuInputKeyEvent ui/input: Use Linux key codes for internal key events ui/input: Prohibit sending KEY_RESERVED ui/console: Add qemu_text_console_put_linux() ui/kbd-state: Use Linux key codes hw/arm/musicpal: Use Linux key codes hw/char/escc: Use Linux key codes hw/display/xenfb: Use Linux key codes hw/input/adb-kbd: Use Linux key codes hw/input/hid: Use Linux key codes hw/input/ps2: Use Linux key codes hw/input/virtio-input: Use Linux key codes hw/m68k/next-kbd: Use Linux key codes replay: Use Linux key codes ui/cocoa: Use Linux key codes ui/dbus: Use Linux key codes ui/gtk: Use Linux key codes ui/input-barrier: Use Linux key codes ui/input-legacy: Use Linux key codes ui/input-linux: Use Linux key codes ui/keymaps: Use Linux key codes ui/sdl2: Use Linux key codes ui/spice: Use Linux key codes ui/vnc: Use Linux key codes qemu-keymap: Use Linux key codes ui/console: Remove qemu_text_console_put_qcode() ui/input: Remove unused QKeyCode helpers and keymaps Daniel P. Berrangé (4): ui/vnc: fix OOB read access in VNC SASL mechname array ui/vnc: fix OOB write in VNC stats array ui/vnc: fix OOB write in lossy rect worker code ui/vnc: fix OOB read updating VNC update frequency stats Dongwon Kim (1): ui/gtk: Fix focus loss on re-attachment with single VC Heechan Kang (1): ui: fix validation of VNC extended clipboard data length Marc-André Lureau (1): ui/vt100: add vt100_fini() check Peter Maydell (2): ui/vt100: Standardize on uint8_t for "ch" byte variables ui/vt100: Take byte as uint8_t in bh_utf8_decode() include/qemu/typedefs.h | 1 + include/system/replay.h | 2 +- include/ui/console.h | 3 +- include/ui/input.h | 92 ++++++----- include/ui/kbd-state.h | 12 +- replay/replay-internal.h | 6 +- ui/vnc.h | 4 +- ui/x_keymap.h | 3 +- chardev/msmouse.c | 12 +- chardev/wctablet.c | 10 +- hw/arm/musicpal.c | 31 ++-- hw/char/escc.c | 45 +++--- hw/display/xenfb.c | 51 ++----- hw/input/adb-kbd.c | 246 +++++++++++++++-------------- hw/input/adb-mouse.c | 22 ++- hw/input/hid.c | 41 +++-- hw/input/ps2.c | 118 +++++++------- hw/input/stellaris_gamepad.c | 11 +- hw/input/virtio-input-hid.c | 89 ++++++----- hw/m68k/next-kbd.c | 128 ++++++++-------- qemu-keymap.c | 41 +++-- replay/replay-events.c | 6 +- replay/replay-input.c | 117 +++++--------- replay/replay.c | 2 +- replay/stubs-system.c | 2 +- tools/qemu-vnc/input.c | 9 +- ui/console.c | 62 +++++--- ui/dbus-console.c | 8 +- ui/gtk.c | 55 ++++--- ui/input-barrier.c | 26 ++-- ui/input-keymap.c | 74 +++++---- ui/input-legacy.c | 47 ++---- ui/input-linux.c | 3 +- ui/input.c | 214 ++++++++++++-------------- ui/kbd-state.c | 61 ++++---- ui/keymaps.c | 4 +- ui/sdl2-input.c | 17 ++- ui/spice-input.c | 3 +- ui/vdagent.c | 20 +-- ui/vnc-auth-sasl.c | 2 + ui/vnc-clipboard.c | 9 +- ui/vnc.c | 41 ++--- ui/vt100.c | 11 +- ui/x_keymap.c | 24 +-- hw/input/trace-events | 2 +- tools/qemu-vnc/trace-events | 2 +- ui/cocoa.m | 289 ++++++++++++++++++----------------- ui/meson.build | 29 ++-- ui/trace-events | 5 +- 49 files changed, 1014 insertions(+), 1098 deletions(-) -- 2.54.0
From: Peter Maydell <peter.maydell@linaro.org> The vt100 code is rather confused about how it handles bytes of data to be sent to the terminal: * vt100_input() takes a buffer of uint8_t * each byte is passed to vt100_putchar(), which takes "int ch" * that calls vt100_put_one(), which also takes "int ch" * vt100_put_one() sets TextCell::ch, which is uint8_t again * various places pass the TextCell:ch value to vt100_putcharxy(), which takes "int ch" again, but uses it unchecked as an index into a 256-entry array This confuses Coverity (e.g. CID 1659590) and the reader, who may be unsure whether the "int" variable really does hold only valid byte values 0..255 and whether we need to bounds-check before doing array dereferences. Standardize on keeping known-byte data in uint8_t all the way through. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260512104210.3330835-2-peter.maydell@linaro.org> --- ui/vt100.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/vt100.c b/ui/vt100.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vt100.c +++ b/ui/vt100.c @@ -XXX,XX +XXX,XX @@ static void image_bitblt(pixman_image_t *image, xs, ys, 0, 0, xd, yd, w, h); } -static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch, +static void vt100_putcharxy(QemuVT100 *vt, int x, int y, uint8_t ch, TextAttributes *t_attrib) { static pixman_image_t *glyphs[256]; @@ -XXX,XX +XXX,XX @@ static uint32_t bh_utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte) return *state; } -static void vt100_put_one(QemuVT100 *vt, int ch) +static void vt100_put_one(QemuVT100 *vt, uint8_t ch) { TextCell *c; int y1; @@ -XXX,XX +XXX,XX @@ static void vt100_restore_cursor(QemuVT100 *vt) vt->t_attrib = vt->t_attrib_saved; } -static void vt100_putchar(QemuVT100 *vt, int ch) +static void vt100_putchar(QemuVT100 *vt, uint8_t ch) { int i; int x, y; -- 2.54.0
From: Peter Maydell <peter.maydell@linaro.org> The bh_utf8_decode() UTF8 decoder takes its next byte as a "uint32_t byte" parameter, but it assumes it to be in bounds as it immediately indexes into its array with it. Use "uint8_t" as the argument type instead. This moves us away from the upstream implementation slightly, but it is the same type as we use in the one callsite, and it makes it clear that we can't be indexing off the end of the array with this guest-derived data. This probably helps make Coverity a bit happier (CID 1659590). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260512104210.3330835-3-peter.maydell@linaro.org> --- ui/vt100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/vt100.c b/ui/vt100.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vt100.c +++ b/ui/vt100.c @@ -XXX,XX +XXX,XX @@ static void vt100_clear_xy(QemuVT100 *vt, int x, int y) #define BH_UTF8_ACCEPT 0 #define BH_UTF8_REJECT 12 -static uint32_t bh_utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte) +static uint32_t bh_utf8_decode(uint32_t *state, uint32_t *codep, uint8_t byte) { static const uint8_t utf8d[] = { /* character class lookup */ -- 2.54.0
From: Marc-André Lureau <marcandre.lureau@redhat.com> vt100_fini() is called unconditonally from qemu_text_console_finalize(), but it may not have been vt100_init()/opened: fix the crash in that case. Fixes: 8fa294482eb ("ui/console-vc: move VT100 state machine ...") Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- ui/vt100.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/vt100.c b/ui/vt100.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vt100.c +++ b/ui/vt100.c @@ -XXX,XX +XXX,XX @@ void vt100_init(QemuVT100 *vt, void vt100_fini(QemuVT100 *vt) { + if (!QTAILQ_IN_USE(vt, list)) { + return; + } QTAILQ_REMOVE(&vt100s, vt, list); fifo8_destroy(&vt->out_fifo); g_free(vt->cells); -- 2.54.0
From: Daniel P. Berrangé <berrange@redhat.com> When reading the SASL mechname array off the VNC connection, if malicious, the received data may contain embedded NULs. If this happens the memory buffer returned by g_strndup may be shorter than the original data. Unfortunately the code continued to index into this buffer with an offset equal to the original length. This is a potential OOB read of the array. Fixes: 5847d9e1 (ui/vnc: simplify and avoid strncpy) Reported-by: boy juju <agx1657748706@gmail.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260521103353.1645561-2-berrange@redhat.com> --- ui/vnc-auth-sasl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -XXX,XX +XXX,XX @@ static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_ char *mechname = g_strndup((const char *) data, len); trace_vnc_auth_sasl_mech_choose(vs, mechname); + /* If 'data' had embedded NUL the dup'd string might now be shorter */ + len = strlen(mechname); if (strncmp(vs->sasl.mechlist, mechname, len) == 0) { if (vs->sasl.mechlist[len] != '\0' && vs->sasl.mechlist[len] != ',') { -- 2.54.0
From: Daniel P. Berrangé <berrange@redhat.com> The VncSurface struct maintains update statistics in an array: VncRectStat stats[VNC_STAT_ROWS][VNC_STAT_COLS]; where the dimensions are defined as: #define VNC_STAT_RECT 64 #define VNC_STAT_COLS (VNC_MAX_WIDTH / VNC_STAT_RECT) #define VNC_STAT_ROWS (VNC_MAX_HEIGHT / VNC_STAT_RECT) If VNC_MAX_WIDTH / VNC_MAX_HEIGHT are not an exact multiple of VNC_STAT_REC, the COLS/ROWS will be undersized by 1. Unfortunately: #define VNC_MAX_HEIGHT 2160 is not a multiple of 64, so there is potential for OOB reads and writes in the 'stats' array, if the guest surface is over 2112 pixels in height. An array overflow occurs when vnc_update_stats() records new statistics, either scribbling over data later in the VncDisplay struct that 'stats' is embedded in, or performing an OOB write on the allocated struct memory. Fixes: CVE-2026-48002 Reported-by: boy juju <agx1657748706@gmail.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260521103353.1645561-3-berrange@redhat.com> --- ui/vnc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/vnc.h b/ui/vnc.h index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -XXX,XX +XXX,XX @@ typedef void VncSendHextileTile(VncState *vs, #define VNC_DIRTY_BPL(x) (sizeof((x)->dirty) / VNC_MAX_HEIGHT * BITS_PER_BYTE) #define VNC_STAT_RECT 64 -#define VNC_STAT_COLS (VNC_MAX_WIDTH / VNC_STAT_RECT) -#define VNC_STAT_ROWS (VNC_MAX_HEIGHT / VNC_STAT_RECT) +#define VNC_STAT_COLS DIV_ROUND_UP(VNC_MAX_WIDTH, VNC_STAT_RECT) +#define VNC_STAT_ROWS DIV_ROUND_UP(VNC_MAX_HEIGHT, VNC_STAT_RECT) #define VNC_AUTH_CHALLENGE_SIZE 16 -- 2.54.0
From: Daniel P. Berrangé <berrange@redhat.com> Incorrect calculation of the boundary condition when tracking lossy rectangles in the worker thread will result in an OOB write which can corrupt further worker state, and/or trigger any guard pages that may lie beyond the VncWorker struct. This can be triggered through careful choice of the display resolution in the guest OS by an unprivileged user. Fixes: CVE-2026-48002 Reported-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260521103353.1645561-4-berrange@redhat.com> [Marc-André - added assert() suggest by philmd@linaro.org] Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- ui/vnc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -XXX,XX +XXX,XX @@ void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h) { int i, j; - w = (x + w) / VNC_STAT_RECT; - h = (y + h) / VNC_STAT_RECT; + w = DIV_ROUND_UP((x + w), VNC_STAT_RECT); + h = DIV_ROUND_UP((y + h), VNC_STAT_RECT); + assert(h <= VNC_STAT_ROWS) + assert(w <= VNC_STAT_COLS) x /= VNC_STAT_RECT; y /= VNC_STAT_RECT; - for (j = y; j <= h; j++) { - for (i = x; i <= w; i++) { + for (j = y; j < h; j++) { + for (i = x; i < w; i++) { worker->lossy_rect[j][i] = 1; } } -- 2.54.0
From: Daniel P. Berrangé <berrange@redhat.com> Incorrect loop bounds in vnc_update_freq result in iterating past the last row and past the last column in the VNC stats array. With suitably chosen dimensions this could be a OOB read that accesses memory beyond the VncDisplay struct that the stats array is embedded in. Should this hit a guard page, it could trigger a guest crash. If it does not, then the VNC frequency stats will be updated with garbage. Fixes: CVE-2026-48003 Reported-by: boy juju <agx1657748706@gmail.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260521103353.1645561-5-berrange@redhat.com> --- ui/vnc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -XXX,XX +XXX,XX @@ double vnc_update_freq(VncState *vs, int x, int y, int w, int h) int i, j; double total = 0; int num = 0; + int x_end = x + w; + int y_end = y + h; x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); - for (j = y; j <= y + h; j += VNC_STAT_RECT) { - for (i = x; i <= x + w; i += VNC_STAT_RECT) { + for (j = y; j < y_end; j += VNC_STAT_RECT) { + for (i = x; i < x_end; i += VNC_STAT_RECT) { total += vnc_stat_rect(vs->vd, i, j)->freq; num++; } -- 2.54.0
From: Heechan Kang <gganji11@naver.com> QEMU's VNC extended clipboard handler inflates a client-controlled compressed clipboard payload. The code checks the declared text size against the total inflated buffer size: if (tsize < size) but then copies from: tbuf = buf + 4; qemu_clipboard_set_data(..., tsize, tbuf, true); The correct bound is the remaining data length after the 4-byte length field, not the total inflated buffer length. As a result, a VNC client can make QEMU copy up to 3 bytes past the end of the inflated heap buffer. With a second VNC client, those copied bytes are observable through the normal VNC extended clipboard PROVIDE path. Fixes: CVE-2026-8343 Reported-by: Heechan Kang <gganji11@naver.com> Reported-by: Feifan Qian <bea1e@proton.me> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Heechan Kang <gganji11@naver.com> [DB: added #include and 'return' statements] Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260512095543.459949-1-berrange@redhat.com> --- ui/vnc-clipboard.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/vnc-clipboard.c b/ui/vnc-clipboard.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc-clipboard.c +++ b/ui/vnc-clipboard.c @@ -XXX,XX +XXX,XX @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "vnc.h" #include "vnc-jobs.h" @@ -XXX,XX +XXX,XX @@ void vnc_client_cut_text_ext(VncState *vs, int32_t len, uint32_t flags, uint8_t buf && size >= 4) { uint32_t tsize = read_u32(buf, 0); uint8_t *tbuf = buf + 4; - if (tsize < size) { + if (tsize <= size - 4) { qemu_clipboard_set_data(&vs->cbpeer, vs->cbinfo, QEMU_CLIPBOARD_TYPE_TEXT, tsize, tbuf, true); + } else { + error_report("vnc: malformed extended clipboard payload " + "with text length %u exceeding available %u", + tsize, size - 4); + vnc_client_error(vs); + return; } } } -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Add QemuInputEvent as the input subsystem's name for InputEvent and use it in input handler, queue, and replay interfaces. This prepares for decoupling QEMU's internal input event representation from the QAPI InputEvent type. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-1-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- include/qemu/typedefs.h | 1 + include/system/replay.h | 2 +- include/ui/input.h | 6 +++--- replay/replay-internal.h | 6 +++--- chardev/msmouse.c | 2 +- chardev/wctablet.c | 2 +- hw/arm/musicpal.c | 2 +- hw/char/escc.c | 4 ++-- hw/display/xenfb.c | 4 ++-- hw/input/adb-kbd.c | 2 +- hw/input/adb-mouse.c | 2 +- hw/input/hid.c | 4 ++-- hw/input/ps2.c | 4 ++-- hw/input/stellaris_gamepad.c | 2 +- hw/input/virtio-input-hid.c | 2 +- hw/m68k/next-kbd.c | 3 ++- replay/replay-events.c | 4 ++-- replay/replay-input.c | 8 ++++---- replay/stubs-system.c | 2 +- ui/input-legacy.c | 2 +- ui/input.c | 26 +++++++++++++------------- ui/vdagent.c | 2 +- 22 files changed, 47 insertions(+), 45 deletions(-) diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index XXXXXXX..XXXXXXX 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -XXX,XX +XXX,XX @@ typedef struct QBool QBool; typedef struct QDict QDict; typedef struct QEMUBH QEMUBH; typedef struct QemuConsole QemuConsole; +typedef struct InputEvent QemuInputEvent; typedef struct QEMUCursor QEMUCursor; typedef struct QEMUFile QEMUFile; typedef struct QemuMutex QemuMutex; diff --git a/include/system/replay.h b/include/system/replay.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/replay.h +++ b/include/system/replay.h @@ -XXX,XX +XXX,XX @@ void replay_bh_schedule_event(QEMUBH *bh); void replay_bh_schedule_oneshot_event(AioContext *ctx, QEMUBHFunc *cb, void *opaque); /*! Adds input event to the queue */ -void replay_input_event(QemuConsole *src, InputEvent *evt); +void replay_input_event(QemuConsole *src, QemuInputEvent *evt); /*! Adds input sync event to the queue */ void replay_input_sync_event(void); /*! Adds block layer event to the queue */ diff --git a/include/ui/input.h b/include/ui/input.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -XXX,XX +XXX,XX @@ typedef struct QemuInputHandler QemuInputHandler; typedef struct QemuInputHandlerState QemuInputHandlerState; typedef void (*QemuInputHandlerEvent)(DeviceState *dev, QemuConsole *src, - InputEvent *evt); + QemuInputEvent *evt); typedef void (*QemuInputHandlerSync)(DeviceState *dev); struct QemuInputHandler { @@ -XXX,XX +XXX,XX @@ void qemu_input_handler_unregister(QemuInputHandlerState *s); void qemu_input_handler_bind(QemuInputHandlerState *s, const char *device_id, int head, Error **errp); -void qemu_input_event_send(QemuConsole *src, InputEvent *evt); -void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt); +void qemu_input_event_send(QemuConsole *src, QemuInputEvent *evt); +void qemu_input_event_send_impl(QemuConsole *src, QemuInputEvent *evt); void qemu_input_event_sync(void); void qemu_input_event_sync_impl(void); diff --git a/replay/replay-internal.h b/replay/replay-internal.h index XXXXXXX..XXXXXXX 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -XXX,XX +XXX,XX @@ void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque, /* Input events */ /*! Saves input event to the log */ -void replay_save_input_event(InputEvent *evt); +void replay_save_input_event(QemuInputEvent *evt); /*! Reads input event from the log */ -InputEvent *replay_read_input_event(void); +QemuInputEvent *replay_read_input_event(void); /*! Adds input event to the queue */ -void replay_add_input_event(struct InputEvent *event); +void replay_add_input_event(QemuInputEvent *event); /*! Adds input sync event to the queue */ void replay_add_input_sync_event(void); diff --git a/chardev/msmouse.c b/chardev/msmouse.c index XXXXXXX..XXXXXXX 100644 --- a/chardev/msmouse.c +++ b/chardev/msmouse.c @@ -XXX,XX +XXX,XX @@ static void msmouse_queue_event(MouseChardev *mouse) } static void msmouse_input_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { MouseChardev *mouse = MOUSE_CHARDEV(dev); InputMoveEvent *move; diff --git a/chardev/wctablet.c b/chardev/wctablet.c index XXXXXXX..XXXXXXX 100644 --- a/chardev/wctablet.c +++ b/chardev/wctablet.c @@ -XXX,XX +XXX,XX @@ static void wctablet_queue_event(TabletChardev *tablet) } static void wctablet_input_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { TabletChardev *tablet = (TabletChardev *)dev; InputMoveEvent *move; diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -XXX,XX +XXX,XX @@ struct musicpal_key_state { }; static void musicpal_key_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { musicpal_key_state *s = MUSICPAL_KEY(dev); InputKeyEvent *key = evt->u.key.data; diff --git a/hw/char/escc.c b/hw/char/escc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_escc = { }; static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { ESCCChannelState *s = (ESCCChannelState *)dev; int qcode, keycode; @@ -XXX,XX +XXX,XX @@ static void handle_kbd_command(ESCCChannelState *s, int val) } static void sunmouse_handle_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { ESCCChannelState *s = (ESCCChannelState *)dev; InputMoveEvent *move; diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -XXX,XX +XXX,XX @@ static int xenfb_send_position(struct XenInput *xenfb, * already has code for dealing with this... */ static void xenfb_key_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { struct XenInput *xenfb = (struct XenInput *)dev; InputKeyEvent *key = evt->u.key.data; @@ -XXX,XX +XXX,XX @@ static void xenfb_key_event(DeviceState *dev, QemuConsole *src, * the button state. */ static void xenfb_mouse_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { struct XenInput *xenfb = (struct XenInput *)dev; InputBtnEvent *btn; diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -XXX,XX +XXX,XX @@ static bool adb_kbd_has_data(ADBDevice *d) /* This is where keyboard events enter this file */ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { KBDState *s = (KBDState *)dev; int qcode, keycode; diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -XXX,XX +XXX,XX @@ struct ADBMouseClass { #define ADB_MOUSE_BUTTON_RIGHT 0x02 static void adb_mouse_handle_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { MouseState *s = (MouseState *)dev; InputMoveEvent *move; diff --git a/hw/input/hid.c b/hw/input/hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -XXX,XX +XXX,XX @@ void hid_set_next_idle(HIDState *hs) } static void hid_pointer_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = 0x01, @@ -XXX,XX +XXX,XX @@ static void hid_pointer_sync(DeviceState *dev) } static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { HIDState *hs = (HIDState *)dev; int scancodes[3], i, count; diff --git a/hw/input/ps2.c b/hw/input/ps2.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -XXX,XX +XXX,XX @@ static void ps2_put_keycode(void *opaque, int keycode) } static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { PS2KbdState *s = (PS2KbdState *)dev; InputKeyEvent *key = evt->u.key.data; @@ -XXX,XX +XXX,XX @@ static int ps2_mouse_send_packet(PS2MouseState *s) } static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = PS2_MOUSE_BUTTON_LEFT, diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -XXX,XX +XXX,XX @@ #include "ui/console.h" static void stellaris_gamepad_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { StellarisGamepad *s = STELLARIS_GAMEPAD(dev); InputKeyEvent *key = evt->u.key.data; diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -XXX,XX +XXX,XX @@ static void virtio_input_extend_config(VirtIOInput *vinput, } static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { VirtIOInput *vinput = VIRTIO_INPUT(dev); virtio_input_event event; diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -XXX,XX +XXX,XX @@ static void nextkbd_put_keycode(NextKBDState *s, int keycode) /* s->update_irq(s->update_arg, 1); */ } -static void nextkbd_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) +static void nextkbd_event(DeviceState *dev, QemuConsole *src, + QemuInputEvent *evt) { NextKBDState *s = NEXTKBD(dev); int qcode, keycode; diff --git a/replay/replay-events.c b/replay/replay-events.c index XXXXXXX..XXXXXXX 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -XXX,XX +XXX,XX @@ static void replay_run_event(Event *event) ((QEMUBHFunc *)event->opaque)(event->opaque2); break; case REPLAY_ASYNC_EVENT_INPUT: - qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque); + qemu_input_event_send_impl(NULL, (QemuInputEvent *)event->opaque); qapi_free_InputEvent((InputEvent *)event->opaque); break; case REPLAY_ASYNC_EVENT_INPUT_SYNC: @@ -XXX,XX +XXX,XX @@ void replay_bh_schedule_oneshot_event(AioContext *ctx, } } -void replay_add_input_event(struct InputEvent *event) +void replay_add_input_event(QemuInputEvent *event) { replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0); } diff --git a/replay/replay-input.c b/replay/replay-input.c index XXXXXXX..XXXXXXX 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -XXX,XX +XXX,XX @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-ui.h" -void replay_save_input_event(InputEvent *evt) +void replay_save_input_event(QemuInputEvent *evt) { InputKeyEvent *key; InputBtnEvent *btn; @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(InputEvent *evt) } } -InputEvent *replay_read_input_event(void) +QemuInputEvent *replay_read_input_event(void) { - InputEvent evt; + QemuInputEvent evt; KeyValue keyValue; InputKeyEvent key; key.key = &keyValue; @@ -XXX,XX +XXX,XX @@ InputEvent *replay_read_input_event(void) return QAPI_CLONE(InputEvent, &evt); } -void replay_input_event(QemuConsole *src, InputEvent *evt) +void replay_input_event(QemuConsole *src, QemuInputEvent *evt) { if (replay_mode == REPLAY_MODE_PLAY) { /* Nothing */ diff --git a/replay/stubs-system.c b/replay/stubs-system.c index XXXXXXX..XXXXXXX 100644 --- a/replay/stubs-system.c +++ b/replay/stubs-system.c @@ -XXX,XX +XXX,XX @@ #include "system/replay.h" #include "ui/input.h" -void replay_input_event(QemuConsole *src, InputEvent *evt) +void replay_input_event(QemuConsole *src, QemuInputEvent *evt) { qemu_input_event_send_impl(src, evt); } diff --git a/ui/input-legacy.c b/ui/input-legacy.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -XXX,XX +XXX,XX @@ void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, } static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, diff --git a/ui/input.c b/ui/input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input.c +++ b/ui/input.c @@ -XXX,XX +XXX,XX @@ struct QemuInputEventQueue { QEMUTimer *timer; uint32_t delay_ms; QemuConsole *src; - InputEvent *evt; + QemuInputEvent *evt; QTAILQ_ENTRY(QemuInputEventQueue) node; }; @@ -XXX,XX +XXX,XX @@ void qmp_input_send_event(const char *device, qemu_input_event_sync(); } -static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) +static void qemu_input_event_trace(QemuConsole *src, QemuInputEvent *evt) { const char *name; int qcode, idx = -1; @@ -XXX,XX +XXX,XX @@ static void qemu_input_queue_delay(QemuInputEventQueueHead *queue, } static void qemu_input_queue_event(QemuInputEventQueueHead *queue, - QemuConsole *src, InputEvent *evt) + QemuConsole *src, QemuInputEvent *evt) { QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); @@ -XXX,XX +XXX,XX @@ static void qemu_input_queue_sync(QemuInputEventQueueHead *queue) queue_count++; } -void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt) +void qemu_input_event_send_impl(QemuConsole *src, QemuInputEvent *evt) { QemuInputHandlerState *s; @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt) s->events++; } -void qemu_input_event_send(QemuConsole *src, InputEvent *evt) +void qemu_input_event_send(QemuConsole *src, QemuInputEvent *evt) { /* Expect all parts of QEMU to send events with QCodes exclusively. * Key numbers are only supported as end-user input via QMP */ @@ -XXX,XX +XXX,XX @@ void qemu_input_event_sync(void) replay_input_sync_event(); } -static InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) +static QemuInputEvent *qemu_input_event_new_key(KeyValue *key, bool down) { - InputEvent *evt = g_new0(InputEvent, 1); + QemuInputEvent *evt = g_new0(QemuInputEvent, 1); evt->u.key.data = g_new0(InputKeyEvent, 1); evt->type = INPUT_EVENT_KIND_KEY; evt->u.key.data->key = key; @@ -XXX,XX +XXX,XX @@ static InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) { - InputEvent *evt; + QemuInputEvent *evt; evt = qemu_input_event_new_key(key, down); if (QTAILQ_EMPTY(&kbd_queue)) { qemu_input_event_send(src, evt); @@ -XXX,XX +XXX,XX @@ void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down) .button = btn, .down = down, }; - InputEvent evt = { + QemuInputEvent evt = { .type = INPUT_EVENT_KIND_BTN, .u.btn.data = &bevt, }; @@ -XXX,XX +XXX,XX @@ void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value) .axis = axis, .value = value, }; - InputEvent evt = { + QemuInputEvent evt = { .type = INPUT_EVENT_KIND_REL, .u.rel.data = &move, }; @@ -XXX,XX +XXX,XX @@ void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, INPUT_EVENT_ABS_MIN, INPUT_EVENT_ABS_MAX), }; - InputEvent evt = { + QemuInputEvent evt = { .type = INPUT_EVENT_KIND_ABS, .u.abs.data = &move, }; @@ -XXX,XX +XXX,XX @@ void qemu_input_queue_mtt(QemuConsole *src, InputMultiTouchType type, .slot = slot, .tracking_id = tracking_id, }; - InputEvent evt = { + QemuInputEvent evt = { .type = INPUT_EVENT_KIND_MTT, .u.mtt.data = &mtt, }; @@ -XXX,XX +XXX,XX @@ void qemu_input_queue_mtt_abs(QemuConsole *src, InputAxis axis, int value, INPUT_EVENT_ABS_MIN, INPUT_EVENT_ABS_MAX), }; - InputEvent evt = { + QemuInputEvent evt = { .type = INPUT_EVENT_KIND_MTT, .u.mtt.data = &mtt, }; diff --git a/ui/vdagent.c b/ui/vdagent.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -XXX,XX +XXX,XX @@ static void vdagent_send_mouse(VDAgentChardev *vd) } static void vdagent_pointer_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) + QemuInputEvent *evt) { static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = VD_AGENT_LBUTTON_MASK, -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QAPI represents union members with wrapper structs and pointer indirections. They are useful at the QMP boundary, but unnecessary for QEMU's internal input events and make handlers more verbose. Define QemuInputEvent as a plain internal tagged union and convert input handlers, queues, and replay code to access payloads directly. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-2-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- include/qemu/typedefs.h | 2 +- include/ui/input.h | 16 ++++ chardev/msmouse.c | 10 +- chardev/wctablet.c | 8 +- hw/arm/musicpal.c | 11 +-- hw/char/escc.c | 32 +++---- hw/display/xenfb.c | 32 +++---- hw/input/adb-kbd.c | 4 +- hw/input/adb-mouse.c | 20 ++-- hw/input/hid.c | 36 +++---- hw/input/ps2.c | 67 ++++++------- hw/input/stellaris_gamepad.c | 9 +- hw/input/virtio-input-hid.c | 53 +++++------ hw/m68k/next-kbd.c | 9 +- replay/replay-events.c | 2 +- replay/replay-input.c | 101 ++++++++------------ ui/input-legacy.c | 23 ++--- ui/input.c | 177 ++++++++++++++++++++--------------- ui/vdagent.c | 18 ++-- 19 files changed, 299 insertions(+), 331 deletions(-) diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index XXXXXXX..XXXXXXX 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -XXX,XX +XXX,XX @@ typedef struct QBool QBool; typedef struct QDict QDict; typedef struct QEMUBH QEMUBH; typedef struct QemuConsole QemuConsole; -typedef struct InputEvent QemuInputEvent; +typedef struct QemuInputEvent QemuInputEvent; typedef struct QEMUCursor QEMUCursor; typedef struct QEMUFile QEMUFile; typedef struct QemuMutex QemuMutex; diff --git a/include/ui/input.h b/include/ui/input.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -XXX,XX +XXX,XX @@ typedef struct QemuInputHandler QemuInputHandler; typedef struct QemuInputHandlerState QemuInputHandlerState; +typedef struct QemuInputKeyEvent { + KeyValue key; + bool down; +} QemuInputKeyEvent; + +typedef struct QemuInputEvent { + InputEventKind type; + union { + QemuInputKeyEvent key; + InputBtnEvent btn; + InputMoveEvent rel; + InputMoveEvent abs; + InputMultiTouchEvent mtt; + }; +} QemuInputEvent; + typedef void (*QemuInputHandlerEvent)(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt); typedef void (*QemuInputHandlerSync)(DeviceState *dev); diff --git a/chardev/msmouse.c b/chardev/msmouse.c index XXXXXXX..XXXXXXX 100644 --- a/chardev/msmouse.c +++ b/chardev/msmouse.c @@ -XXX,XX +XXX,XX @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { MouseChardev *mouse = MOUSE_CHARDEV(dev); - InputMoveEvent *move; - InputBtnEvent *btn; /* Ignore events if serial mouse powered down. */ if (!MSMOUSE_PWR(mouse->tiocm)) { @@ -XXX,XX +XXX,XX @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src, switch (evt->type) { case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; - mouse->axis[move->axis] += move->value; + mouse->axis[evt->rel.axis] += evt->rel.value; break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - mouse->btns[btn->button] = btn->down; - mouse->btnc[btn->button] = true; + mouse->btns[evt->btn.button] = evt->btn.down; + mouse->btnc[evt->btn.button] = true; break; default: diff --git a/chardev/wctablet.c b/chardev/wctablet.c index XXXXXXX..XXXXXXX 100644 --- a/chardev/wctablet.c +++ b/chardev/wctablet.c @@ -XXX,XX +XXX,XX @@ static void wctablet_input_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { TabletChardev *tablet = (TabletChardev *)dev; - InputMoveEvent *move; - InputBtnEvent *btn; switch (evt->type) { case INPUT_EVENT_KIND_ABS: - move = evt->u.abs.data; - tablet->axis[move->axis] = move->value; + tablet->axis[evt->abs.axis] = evt->abs.value; break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - tablet->btns[btn->button] = btn->down; + tablet->btns[evt->btn.button] = evt->btn.down; break; default: diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -XXX,XX +XXX,XX @@ static void musicpal_key_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { musicpal_key_state *s = MUSICPAL_KEY(dev); - InputKeyEvent *key = evt->u.key.data; - int qcode = qemu_input_key_value_to_qcode(key->key); + int qcode = qemu_input_key_value_to_qcode(&evt->key.key); uint32_t event = 0; int i; @@ -XXX,XX +XXX,XX @@ static void musicpal_key_event(DeviceState *dev, QemuConsole *src, * but do not repeat already-pressed buttons for the other key inputs. */ if (!(event & (MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_VOL))) { - if (key->down && (s->pressed_keys & event)) { + if (evt->key.down && (s->pressed_keys & event)) { event = 0; } } if (event) { /* Raise GPIO pin first if repeating a key */ - if (key->down && (s->pressed_keys & event)) { + if (evt->key.down && (s->pressed_keys & event)) { for (i = 0; i <= 7; i++) { if (event & (1 << i)) { qemu_set_irq(s->out[i], 1); @@ -XXX,XX +XXX,XX @@ static void musicpal_key_event(DeviceState *dev, QemuConsole *src, } for (i = 0; i <= 7; i++) { if (event & (1 << i)) { - qemu_set_irq(s->out[i], !key->down); + qemu_set_irq(s->out[i], !evt->key.down); } } - if (key->down) { + if (evt->key.down) { s->pressed_keys |= event; } else { s->pressed_keys &= ~event; diff --git a/hw/char/escc.c b/hw/char/escc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -XXX,XX +XXX,XX @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, { ESCCChannelState *s = (ESCCChannelState *)dev; int qcode, keycode; - InputKeyEvent *key; assert(evt->type == INPUT_EVENT_KIND_KEY); - key = evt->u.key.data; - qcode = qemu_input_key_value_to_qcode(key->key); + qcode = qemu_input_key_value_to_qcode(&evt->key.key); trace_escc_sunkbd_event_in(qcode, QKeyCode_str(qcode), - key->down); + evt->key.down); if (qcode == Q_KEY_CODE_CAPS_LOCK) { - if (key->down) { + if (evt->key.down) { s->caps_lock_mode ^= 1; if (s->caps_lock_mode == 2) { return; /* Drop second press */ @@ -XXX,XX +XXX,XX @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } if (qcode == Q_KEY_CODE_NUM_LOCK) { - if (key->down) { + if (evt->key.down) { s->num_lock_mode ^= 1; if (s->num_lock_mode == 2) { return; /* Drop second press */ @@ -XXX,XX +XXX,XX @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } keycode = qemu_input_map_qcode_to_sun[qcode]; - if (!key->down) { + if (!evt->key.down) { keycode |= 0x80; } trace_escc_sunkbd_event_out(keycode); @@ -XXX,XX +XXX,XX @@ static void sunmouse_handle_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { ESCCChannelState *s = (ESCCChannelState *)dev; - InputMoveEvent *move; - InputBtnEvent *btn; static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = 0x4, [INPUT_BUTTON_MIDDLE] = 0x2, @@ -XXX,XX +XXX,XX @@ static void sunmouse_handle_event(DeviceState *dev, QemuConsole *src, switch (evt->type) { case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; - if (move->axis == INPUT_AXIS_X) { - s->sunmouse_dx += move->value; - } else if (move->axis == INPUT_AXIS_Y) { - s->sunmouse_dy -= move->value; + if (evt->rel.axis == INPUT_AXIS_X) { + s->sunmouse_dx += evt->rel.value; + } else if (evt->rel.axis == INPUT_AXIS_Y) { + s->sunmouse_dy -= evt->rel.value; } break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - if (bmap[btn->button]) { - if (btn->down) { - s->sunmouse_buttons |= bmap[btn->button]; + if (bmap[evt->btn.button]) { + if (evt->btn.down) { + s->sunmouse_buttons |= bmap[evt->btn.button]; } else { - s->sunmouse_buttons &= ~bmap[btn->button]; + s->sunmouse_buttons &= ~bmap[evt->btn.button]; } /* Indicate we have a supported button event */ s->sunmouse_buttons |= 0x80; diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -XXX,XX +XXX,XX @@ static void xenfb_key_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { struct XenInput *xenfb = (struct XenInput *)dev; - InputKeyEvent *key = evt->u.key.data; - int qcode = qemu_input_key_value_to_qcode(key->key); + int qcode = qemu_input_key_value_to_qcode(&evt->key.key); int lnx; if (qcode < qemu_input_map_qcode_to_linux_len) { lnx = qemu_input_map_qcode_to_linux[qcode]; if (lnx) { - trace_xenfb_key_event(xenfb, lnx, key->down); - xenfb_send_key(xenfb, key->down, lnx); + trace_xenfb_key_event(xenfb, lnx, evt->key.down); + xenfb_send_key(xenfb, evt->key.down, lnx); } } } @@ -XXX,XX +XXX,XX @@ static void xenfb_mouse_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { struct XenInput *xenfb = (struct XenInput *)dev; - InputBtnEvent *btn; - InputMoveEvent *move; QemuConsole *con; DisplaySurface *surface; int scale; switch (evt->type) { case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - switch (btn->button) { + switch (evt->btn.button) { case INPUT_BUTTON_LEFT: - xenfb_send_key(xenfb, btn->down, BTN_LEFT); + xenfb_send_key(xenfb, evt->btn.down, BTN_LEFT); break; case INPUT_BUTTON_RIGHT: - xenfb_send_key(xenfb, btn->down, BTN_LEFT + 1); + xenfb_send_key(xenfb, evt->btn.down, BTN_LEFT + 1); break; case INPUT_BUTTON_MIDDLE: - xenfb_send_key(xenfb, btn->down, BTN_LEFT + 2); + xenfb_send_key(xenfb, evt->btn.down, BTN_LEFT + 2); break; case INPUT_BUTTON_WHEEL_UP: - if (btn->down) { + if (evt->btn.down) { xenfb->wheel--; } break; case INPUT_BUTTON_WHEEL_DOWN: - if (btn->down) { + if (evt->btn.down) { xenfb->wheel++; } break; @@ -XXX,XX +XXX,XX @@ static void xenfb_mouse_event(DeviceState *dev, QemuConsole *src, break; case INPUT_EVENT_KIND_ABS: - move = evt->u.abs.data; if (xenfb->raw_pointer_wanted) { - xenfb->axis[move->axis] = move->value; + xenfb->axis[evt->abs.axis] = evt->abs.value; } else { con = qemu_console_lookup_by_index(0); if (!con) { @@ -XXX,XX +XXX,XX @@ static void xenfb_mouse_event(DeviceState *dev, QemuConsole *src, return; } surface = qemu_console_surface(con); - switch (move->axis) { + switch (evt->abs.axis) { case INPUT_AXIS_X: scale = surface_width(surface) - 1; break; @@ -XXX,XX +XXX,XX @@ static void xenfb_mouse_event(DeviceState *dev, QemuConsole *src, default: g_assert_not_reached(); } - xenfb->axis[move->axis] = move->value * scale / 0x7fff; + xenfb->axis[evt->abs.axis] = evt->abs.value * scale / 0x7fff; } break; case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; - xenfb->axis[move->axis] += move->value; + xenfb->axis[evt->rel.axis] += evt->rel.value; break; default: diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -XXX,XX +XXX,XX @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, KBDState *s = (KBDState *)dev; int qcode, keycode; - qcode = qemu_input_key_value_to_qcode(evt->u.key.data->key); + qcode = qemu_input_key_value_to_qcode(&evt->key.key); if (qcode >= ARRAY_SIZE(qcode_to_adb_keycode)) { return; } @@ -XXX,XX +XXX,XX @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, trace_adb_device_kbd_no_key(); return; } - if (evt->u.key.data->down == false) { /* if key release event */ + if (evt->key.down == false) { /* if key release event */ keycode = keycode | 0x80; /* create keyboard break code */ } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -XXX,XX +XXX,XX @@ static void adb_mouse_handle_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { MouseState *s = (MouseState *)dev; - InputMoveEvent *move; - InputBtnEvent *btn; static const int bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = ADB_MOUSE_BUTTON_LEFT, [INPUT_BUTTON_RIGHT] = ADB_MOUSE_BUTTON_RIGHT, @@ -XXX,XX +XXX,XX @@ static void adb_mouse_handle_event(DeviceState *dev, QemuConsole *src, switch (evt->type) { case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; - if (move->axis == INPUT_AXIS_X) { - s->dx += move->value; - } else if (move->axis == INPUT_AXIS_Y) { - s->dy += move->value; + if (evt->rel.axis == INPUT_AXIS_X) { + s->dx += evt->rel.value; + } else if (evt->rel.axis == INPUT_AXIS_Y) { + s->dy += evt->rel.value; } break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - if (bmap[btn->button]) { - if (btn->down) { - s->buttons_state |= bmap[btn->button]; + if (bmap[evt->btn.button]) { + if (evt->btn.down) { + s->buttons_state |= bmap[evt->btn.button]; } else { - s->buttons_state &= ~bmap[btn->button]; + s->buttons_state &= ~bmap[evt->btn.button]; } } break; diff --git a/hw/input/hid.c b/hw/input/hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -XXX,XX +XXX,XX @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src, }; HIDState *hs = (HIDState *)dev; HIDPointerEvent *e; - InputMoveEvent *move; - InputBtnEvent *btn; assert(hs->n < QUEUE_LENGTH); e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK]; switch (evt->type) { case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; - if (move->axis == INPUT_AXIS_X) { - e->xdx += move->value; - } else if (move->axis == INPUT_AXIS_Y) { - e->ydy += move->value; + if (evt->rel.axis == INPUT_AXIS_X) { + e->xdx += evt->rel.value; + } else if (evt->rel.axis == INPUT_AXIS_Y) { + e->ydy += evt->rel.value; } break; case INPUT_EVENT_KIND_ABS: - move = evt->u.abs.data; - if (move->axis == INPUT_AXIS_X) { - e->xdx = move->value; - } else if (move->axis == INPUT_AXIS_Y) { - e->ydy = move->value; + if (evt->abs.axis == INPUT_AXIS_X) { + e->xdx = evt->abs.value; + } else if (evt->abs.axis == INPUT_AXIS_Y) { + e->ydy = evt->abs.value; } break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - if (btn->down) { - e->buttons_state |= bmap[btn->button]; - if (btn->button == INPUT_BUTTON_WHEEL_UP) { + if (evt->btn.down) { + e->buttons_state |= bmap[evt->btn.button]; + if (evt->btn.button == INPUT_BUTTON_WHEEL_UP) { e->dz--; - } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) { + } else if (evt->btn.button == INPUT_BUTTON_WHEEL_DOWN) { e->dz++; } } else { - e->buttons_state &= ~bmap[btn->button]; + e->buttons_state &= ~bmap[evt->btn.button]; } break; @@ -XXX,XX +XXX,XX @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, HIDState *hs = (HIDState *)dev; int scancodes[3], i, count; int slot; - InputKeyEvent *key = evt->u.key.data; - count = qemu_input_key_value_to_scancode(key->key, - key->down, + count = qemu_input_key_value_to_scancode(&evt->key.key, + evt->key.down, scancodes); if (hs->n + count > QUEUE_LENGTH) { trace_hid_kbd_queue_full(); diff --git a/hw/input/ps2.c b/hw/input/ps2.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { PS2KbdState *s = (PS2KbdState *)dev; - InputKeyEvent *key = evt->u.key.data; int qcode; uint16_t keycode = 0; int mod; @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); assert(evt->type == INPUT_EVENT_KIND_KEY); - qcode = qemu_input_key_value_to_qcode(key->key); + qcode = qemu_input_key_value_to_qcode(&evt->key.key); mod = ps2_modifier_bit(qcode); - trace_ps2_keyboard_event(s, qcode, key->down, mod, + trace_ps2_keyboard_event(s, qcode, evt->key.down, mod, s->modifiers, s->scancode_set, s->translate); - if (key->down) { + if (evt->key.down) { s->modifiers |= mod; } else { s->modifiers &= ~mod; @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, if (s->scancode_set == 1) { if (qcode == Q_KEY_CODE_PAUSE) { if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0x46); ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0xc6); } } else { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe1); ps2_put_keycode(s, 0x1d); ps2_put_keycode(s, 0x45); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } else if (qcode == Q_KEY_CODE_PRINT) { if (s->modifiers & MOD_ALT_L) { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xb8); ps2_put_keycode(s, 0x38); ps2_put_keycode(s, 0x54); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0x38); } } else if (s->modifiers & MOD_ALT_R) { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0xb8); ps2_put_keycode(s, 0xe0); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L | MOD_SHIFT_R | MOD_CTRL_R)) { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0x37); } else { @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0xb7); } } else { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0x2a); ps2_put_keycode(s, 0xe0); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } } else if ((qcode == Q_KEY_CODE_LANG1 || qcode == Q_KEY_CODE_LANG2) - && !key->down) { + && !evt->key.down) { /* Ignore release for these keys */ } else { if (qcode < qemu_input_map_qcode_to_atset1_len) { @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, if (keycode & 0xff00) { ps2_put_keycode(s, keycode >> 8); } - if (!key->down) { + if (!evt->key.down) { keycode |= 0x80; } ps2_put_keycode(s, keycode & 0xff); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } else if (s->scancode_set == 2) { if (qcode == Q_KEY_CODE_PAUSE) { if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0x7e); ps2_put_keycode(s, 0xe0); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0x7e); } } else { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe1); ps2_put_keycode(s, 0x14); ps2_put_keycode(s, 0x77); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } else if (qcode == Q_KEY_CODE_PRINT) { if (s->modifiers & MOD_ALT_L) { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xf0); ps2_put_keycode(s, 0x11); ps2_put_keycode(s, 0x11); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0x11); } } else if (s->modifiers & MOD_ALT_R) { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0xf0); ps2_put_keycode(s, 0x11); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L | MOD_SHIFT_R | MOD_CTRL_R)) { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0x7c); } else { @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0x7c); } } else { - if (key->down) { + if (evt->key.down) { ps2_put_keycode(s, 0xe0); ps2_put_keycode(s, 0x12); ps2_put_keycode(s, 0xe0); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } } else if ((qcode == Q_KEY_CODE_LANG1 || qcode == Q_KEY_CODE_LANG2) && - !key->down) { + !evt->key.down) { /* Ignore release for these keys */ } else { if (qcode < qemu_input_map_qcode_to_atset2_len) { @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, if (keycode & 0xff00) { ps2_put_keycode(s, keycode >> 8); } - if (!key->down) { + if (!evt->key.down) { ps2_put_keycode(s, 0xf0); } ps2_put_keycode(s, keycode & 0xff); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } if (keycode) { /* FIXME: break code should be configured on a key by key basis */ - if (!key->down) { + if (!evt->key.down) { ps2_put_keycode(s, 0xf0); } ps2_put_keycode(s, keycode); @@ -XXX,XX +XXX,XX @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, [INPUT_BUTTON_EXTRA] = PS2_MOUSE_BUTTON_EXTRA, }; PS2MouseState *s = (PS2MouseState *)dev; - InputMoveEvent *move; - InputBtnEvent *btn; /* check if deltas are recorded when disabled */ if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) { @@ -XXX,XX +XXX,XX @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, switch (evt->type) { case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; - if (move->axis == INPUT_AXIS_X) { - s->mouse_dx += move->value; - } else if (move->axis == INPUT_AXIS_Y) { - s->mouse_dy -= move->value; + if (evt->rel.axis == INPUT_AXIS_X) { + s->mouse_dx += evt->rel.value; + } else if (evt->rel.axis == INPUT_AXIS_Y) { + s->mouse_dy -= evt->rel.value; } break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - if (btn->down) { - s->mouse_buttons |= bmap[btn->button]; - if (btn->button == INPUT_BUTTON_WHEEL_UP) { + if (evt->btn.down) { + s->mouse_buttons |= bmap[evt->btn.button]; + if (evt->btn.button == INPUT_BUTTON_WHEEL_UP) { s->mouse_dz--; - } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) { + } else if (evt->btn.button == INPUT_BUTTON_WHEEL_DOWN) { s->mouse_dz++; } - if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) { + if (evt->btn.button == INPUT_BUTTON_WHEEL_RIGHT) { s->mouse_dw--; - } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) { + } else if (evt->btn.button == INPUT_BUTTON_WHEEL_LEFT) { s->mouse_dw++; } } else { - s->mouse_buttons &= ~bmap[btn->button]; + s->mouse_buttons &= ~bmap[evt->btn.button]; } break; diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -XXX,XX +XXX,XX @@ static void stellaris_gamepad_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { StellarisGamepad *s = STELLARIS_GAMEPAD(dev); - InputKeyEvent *key = evt->u.key.data; - int qcode = qemu_input_key_value_to_qcode(key->key); + int qcode = qemu_input_key_value_to_qcode(&evt->key.key); int i; for (i = 0; i < s->num_buttons; i++) { - if (s->keycodes[i] == qcode && s->pressed[i] != key->down) { - s->pressed[i] = key->down; - qemu_set_irq(s->irqs[i], key->down); + if (s->keycodes[i] == qcode && s->pressed[i] != evt->key.down) { + s->pressed[i] = evt->key.down; + qemu_set_irq(s->irqs[i], evt->key.down); } } } diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -XXX,XX +XXX,XX @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, VirtIOInput *vinput = VIRTIO_INPUT(dev); virtio_input_event event; int qcode; - InputKeyEvent *key; - InputMoveEvent *move; - InputBtnEvent *btn; - InputMultiTouchEvent *mtt; switch (evt->type) { case INPUT_EVENT_KIND_KEY: - key = evt->u.key.data; - qcode = qemu_input_key_value_to_qcode(key->key); + qcode = qemu_input_key_value_to_qcode(&evt->key.key); if (qcode < qemu_input_map_qcode_to_linux_len && qemu_input_map_qcode_to_linux[qcode]) { event.type = cpu_to_le16(EV_KEY); event.code = cpu_to_le16(qemu_input_map_qcode_to_linux[qcode]); - event.value = cpu_to_le32(key->down ? 1 : 0); + event.value = cpu_to_le32(evt->key.down ? 1 : 0); virtio_input_send(vinput, &event); } else { - if (key->down) { + if (evt->key.down) { fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__, qcode, QKeyCode_str(qcode)); } } break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - if ((btn->button == INPUT_BUTTON_WHEEL_UP || - btn->button == INPUT_BUTTON_WHEEL_DOWN) && - btn->down) { + if ((evt->btn.button == INPUT_BUTTON_WHEEL_UP || + evt->btn.button == INPUT_BUTTON_WHEEL_DOWN) && + evt->btn.down) { event.type = cpu_to_le16(EV_REL); event.code = cpu_to_le16(REL_WHEEL); - event.value = cpu_to_le32(btn->button == INPUT_BUTTON_WHEEL_UP + event.value = cpu_to_le32(evt->btn.button == INPUT_BUTTON_WHEEL_UP ? 1 : -1); virtio_input_send(vinput, &event); - } else if (keymap_button[btn->button]) { + } else if (keymap_button[evt->btn.button]) { event.type = cpu_to_le16(EV_KEY); - event.code = cpu_to_le16(keymap_button[btn->button]); - event.value = cpu_to_le32(btn->down ? 1 : 0); + event.code = cpu_to_le16(keymap_button[evt->btn.button]); + event.value = cpu_to_le32(evt->btn.down ? 1 : 0); virtio_input_send(vinput, &event); } else { - if (btn->down) { + if (evt->btn.down) { fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__, - btn->button, - InputButton_str(btn->button)); + evt->btn.button, + InputButton_str(evt->btn.button)); } } break; case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; event.type = cpu_to_le16(EV_REL); - event.code = cpu_to_le16(axismap_rel[move->axis]); - event.value = cpu_to_le32(move->value); + event.code = cpu_to_le16(axismap_rel[evt->rel.axis]); + event.value = cpu_to_le32(evt->rel.value); virtio_input_send(vinput, &event); break; case INPUT_EVENT_KIND_ABS: - move = evt->u.abs.data; event.type = cpu_to_le16(EV_ABS); - event.code = cpu_to_le16(axismap_abs[move->axis]); - event.value = cpu_to_le32(move->value); + event.code = cpu_to_le16(axismap_abs[evt->abs.axis]); + event.value = cpu_to_le32(evt->abs.value); virtio_input_send(vinput, &event); break; case INPUT_EVENT_KIND_MTT: - mtt = evt->u.mtt.data; - if (mtt->type == INPUT_MULTI_TOUCH_TYPE_DATA) { + if (evt->mtt.type == INPUT_MULTI_TOUCH_TYPE_DATA) { event.type = cpu_to_le16(EV_ABS); - event.code = cpu_to_le16(axismap_tch[mtt->axis]); - event.value = cpu_to_le32(mtt->value); + event.code = cpu_to_le16(axismap_tch[evt->mtt.axis]); + event.value = cpu_to_le32(evt->mtt.value); virtio_input_send(vinput, &event); } else { event.type = cpu_to_le16(EV_ABS); event.code = cpu_to_le16(ABS_MT_SLOT); - event.value = cpu_to_le32(mtt->slot); + event.value = cpu_to_le32(evt->mtt.slot); virtio_input_send(vinput, &event); event.type = cpu_to_le16(EV_ABS); event.code = cpu_to_le16(ABS_MT_TRACKING_ID); - event.value = cpu_to_le32(mtt->tracking_id); + event.value = cpu_to_le32(evt->mtt.tracking_id); virtio_input_send(vinput, &event); } break; diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -XXX,XX +XXX,XX @@ static void nextkbd_event(DeviceState *dev, QemuConsole *src, { NextKBDState *s = NEXTKBD(dev); int qcode, keycode; - bool key_down = evt->u.key.data->down; - qcode = qemu_input_key_value_to_qcode(evt->u.key.data->key); + qcode = qemu_input_key_value_to_qcode(&evt->key.key); if (qcode >= ARRAY_SIZE(qcode_to_nextkbd_keycode)) { return; } /* Shift key currently has no keycode, so handle separately */ if (qcode == Q_KEY_CODE_SHIFT) { - if (key_down) { + if (evt->key.down) { s->shift |= KD_LSHIFT; } else { s->shift &= ~KD_LSHIFT; @@ -XXX,XX +XXX,XX @@ static void nextkbd_event(DeviceState *dev, QemuConsole *src, } if (qcode == Q_KEY_CODE_SHIFT_R) { - if (key_down) { + if (evt->key.down) { s->shift |= KD_RSHIFT; } else { s->shift &= ~KD_RSHIFT; @@ -XXX,XX +XXX,XX @@ static void nextkbd_event(DeviceState *dev, QemuConsole *src, } /* If key release event, create keyboard break code */ - if (!key_down) { + if (!evt->key.down) { keycode |= 0x80; } diff --git a/replay/replay-events.c b/replay/replay-events.c index XXXXXXX..XXXXXXX 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -XXX,XX +XXX,XX @@ static void replay_run_event(Event *event) break; case REPLAY_ASYNC_EVENT_INPUT: qemu_input_event_send_impl(NULL, (QemuInputEvent *)event->opaque); - qapi_free_InputEvent((InputEvent *)event->opaque); + g_free(event->opaque); break; case REPLAY_ASYNC_EVENT_INPUT_SYNC: qemu_input_event_sync_impl(); diff --git a/replay/replay-input.c b/replay/replay-input.c index XXXXXXX..XXXXXXX 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(QemuInputEvent *evt) { - InputKeyEvent *key; - InputBtnEvent *btn; - InputMoveEvent *move; - InputMultiTouchEvent *mtt; replay_put_dword(evt->type); switch (evt->type) { case INPUT_EVENT_KIND_KEY: - key = evt->u.key.data; - replay_put_dword(key->key->type); + replay_put_dword(evt->key.key.type); - switch (key->key->type) { + switch (evt->key.key.type) { case KEY_VALUE_KIND_NUMBER: - replay_put_qword(key->key->u.number.data); - replay_put_byte(key->down); + replay_put_qword(evt->key.key.u.number.data); + replay_put_byte(evt->key.down); break; case KEY_VALUE_KIND_QCODE: - replay_put_dword(key->key->u.qcode.data); - replay_put_byte(key->down); + replay_put_dword(evt->key.key.u.qcode.data); + replay_put_byte(evt->key.down); break; case KEY_VALUE_KIND__MAX: /* keep gcc happy */ @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(QemuInputEvent *evt) } break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - replay_put_dword(btn->button); - replay_put_byte(btn->down); + replay_put_dword(evt->btn.button); + replay_put_byte(evt->btn.down); break; case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; - replay_put_dword(move->axis); - replay_put_qword(move->value); + replay_put_dword(evt->rel.axis); + replay_put_qword(evt->rel.value); break; case INPUT_EVENT_KIND_ABS: - move = evt->u.abs.data; - replay_put_dword(move->axis); - replay_put_qword(move->value); + replay_put_dword(evt->abs.axis); + replay_put_qword(evt->abs.value); break; case INPUT_EVENT_KIND_MTT: - mtt = evt->u.mtt.data; - replay_put_dword(mtt->type); - replay_put_qword(mtt->slot); - replay_put_qword(mtt->tracking_id); - replay_put_dword(mtt->axis); - replay_put_qword(mtt->value); + replay_put_dword(evt->mtt.type); + replay_put_qword(evt->mtt.slot); + replay_put_qword(evt->mtt.tracking_id); + replay_put_dword(evt->mtt.axis); + replay_put_qword(evt->mtt.value); break; case INPUT_EVENT_KIND__MAX: /* keep gcc happy */ @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(QemuInputEvent *evt) QemuInputEvent *replay_read_input_event(void) { - QemuInputEvent evt; - KeyValue keyValue; - InputKeyEvent key; - key.key = &keyValue; - InputBtnEvent btn; - InputMoveEvent rel; - InputMoveEvent abs; - InputMultiTouchEvent mtt; + QemuInputEvent *evt = g_new(QemuInputEvent, 1); - evt.type = replay_get_dword(); - switch (evt.type) { + evt->type = replay_get_dword(); + switch (evt->type) { case INPUT_EVENT_KIND_KEY: - evt.u.key.data = &key; - evt.u.key.data->key->type = replay_get_dword(); + evt->key.key.type = replay_get_dword(); - switch (evt.u.key.data->key->type) { + switch (evt->key.key.type) { case KEY_VALUE_KIND_NUMBER: - evt.u.key.data->key->u.number.data = replay_get_qword(); - evt.u.key.data->down = replay_get_byte(); + evt->key.key.u.number.data = replay_get_qword(); + evt->key.down = replay_get_byte(); break; case KEY_VALUE_KIND_QCODE: - evt.u.key.data->key->u.qcode.data = (QKeyCode)replay_get_dword(); - evt.u.key.data->down = replay_get_byte(); + evt->key.key.u.qcode.data = (QKeyCode)replay_get_dword(); + evt->key.down = replay_get_byte(); break; case KEY_VALUE_KIND__MAX: /* keep gcc happy */ @@ -XXX,XX +XXX,XX @@ QemuInputEvent *replay_read_input_event(void) } break; case INPUT_EVENT_KIND_BTN: - evt.u.btn.data = &btn; - evt.u.btn.data->button = (InputButton)replay_get_dword(); - evt.u.btn.data->down = replay_get_byte(); + evt->btn.button = (InputButton)replay_get_dword(); + evt->btn.down = replay_get_byte(); break; case INPUT_EVENT_KIND_REL: - evt.u.rel.data = &rel; - evt.u.rel.data->axis = (InputAxis)replay_get_dword(); - evt.u.rel.data->value = replay_get_qword(); + evt->rel.axis = (InputAxis)replay_get_dword(); + evt->rel.value = replay_get_qword(); break; case INPUT_EVENT_KIND_ABS: - evt.u.abs.data = &abs; - evt.u.abs.data->axis = (InputAxis)replay_get_dword(); - evt.u.abs.data->value = replay_get_qword(); + evt->abs.axis = (InputAxis)replay_get_dword(); + evt->abs.value = replay_get_qword(); break; case INPUT_EVENT_KIND_MTT: - evt.u.mtt.data = &mtt; - evt.u.mtt.data->type = (InputMultiTouchType)replay_get_dword(); - evt.u.mtt.data->slot = replay_get_qword(); - evt.u.mtt.data->tracking_id = replay_get_qword(); - evt.u.mtt.data->axis = (InputAxis)replay_get_dword(); - evt.u.mtt.data->value = replay_get_qword(); + evt->mtt.type = (InputMultiTouchType)replay_get_dword(); + evt->mtt.slot = replay_get_qword(); + evt->mtt.tracking_id = replay_get_qword(); + evt->mtt.axis = (InputAxis)replay_get_dword(); + evt->mtt.value = replay_get_qword(); break; case INPUT_EVENT_KIND__MAX: /* keep gcc happy */ break; } - return QAPI_CLONE(InputEvent, &evt); + return evt; } void replay_input_event(QemuConsole *src, QemuInputEvent *evt) @@ -XXX,XX +XXX,XX @@ void replay_input_event(QemuConsole *src, QemuInputEvent *evt) if (replay_mode == REPLAY_MODE_PLAY) { /* Nothing */ } else if (replay_mode == REPLAY_MODE_RECORD) { - replay_add_input_event(QAPI_CLONE(InputEvent, evt)); + QemuInputEvent *clone = g_new(QemuInputEvent, 1); + *clone = *evt; + replay_add_input_event(clone); } else { qemu_input_event_send_impl(src, evt); } diff --git a/ui/input-legacy.c b/ui/input-legacy.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -XXX,XX +XXX,XX @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, }; QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev; - InputBtnEvent *btn; - InputMoveEvent *move; switch (evt->type) { case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - if (btn->down) { - s->buttons |= bmap[btn->button]; + if (evt->btn.down) { + s->buttons |= bmap[evt->btn.button]; } else { - s->buttons &= ~bmap[btn->button]; + s->buttons &= ~bmap[evt->btn.button]; } - if (btn->down && btn->button == INPUT_BUTTON_WHEEL_UP) { + if (evt->btn.down && evt->btn.button == INPUT_BUTTON_WHEEL_UP) { s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, s->axis[INPUT_AXIS_X], s->axis[INPUT_AXIS_Y], -1, s->buttons); } - if (btn->down && btn->button == INPUT_BUTTON_WHEEL_DOWN) { + if (evt->btn.down && evt->btn.button == INPUT_BUTTON_WHEEL_DOWN) { s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, s->axis[INPUT_AXIS_X], s->axis[INPUT_AXIS_Y], 1, s->buttons); } - if (btn->down && btn->button == INPUT_BUTTON_WHEEL_RIGHT) { + if (evt->btn.down && evt->btn.button == INPUT_BUTTON_WHEEL_RIGHT) { s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, s->axis[INPUT_AXIS_X], s->axis[INPUT_AXIS_Y], -2, s->buttons); } - if (btn->down && btn->button == INPUT_BUTTON_WHEEL_LEFT) { + if (evt->btn.down && evt->btn.button == INPUT_BUTTON_WHEEL_LEFT) { s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, s->axis[INPUT_AXIS_X], s->axis[INPUT_AXIS_Y], @@ -XXX,XX +XXX,XX @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, } break; case INPUT_EVENT_KIND_ABS: - move = evt->u.abs.data; - s->axis[move->axis] = move->value; + s->axis[evt->abs.axis] = evt->abs.value; break; case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; - s->axis[move->axis] += move->value; + s->axis[evt->rel.axis] += evt->rel.value; break; default: break; diff --git a/ui/input.c b/ui/input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input.c +++ b/ui/input.c @@ -XXX,XX +XXX,XX @@ struct QemuInputEventQueue { QEMUTimer *timer; uint32_t delay_ms; QemuConsole *src; - QemuInputEvent *evt; + QemuInputEvent evt; QTAILQ_ENTRY(QemuInputEventQueue) node; }; @@ -XXX,XX +XXX,XX @@ void qmp_input_send_event(const char *device, } for (e = events; e != NULL; e = e->next) { - InputEvent *evt = e->value; + InputEvent *qapi = e->value; + QemuInputEvent evt; + + evt.type = qapi->type; + + switch (qapi->type) { + case INPUT_EVENT_KIND_KEY: { + KeyValue *key = qapi->u.key.data->key; + QKeyCode code; + + switch (key->type) { + case KEY_VALUE_KIND_NUMBER: + code = qemu_input_key_number_to_qcode(key->u.number.data); + break; + case KEY_VALUE_KIND_QCODE: + code = key->u.qcode.data; + break; + default: + g_assert_not_reached(); + } + + evt.key.key.type = KEY_VALUE_KIND_QCODE; + evt.key.key.u.qcode.data = code; + evt.key.down = qapi->u.key.data->down; + break; + } - if (evt->type == INPUT_EVENT_KIND_KEY && - evt->u.key.data->key->type == KEY_VALUE_KIND_NUMBER) { - KeyValue *key = evt->u.key.data->key; - QKeyCode code = qemu_input_key_number_to_qcode(key->u.number.data); - qemu_input_event_send_key_qcode(con, code, evt->u.key.data->down); - } else { - qemu_input_event_send(con, evt); + case INPUT_EVENT_KIND_BTN: + evt.btn = *qapi->u.btn.data; + break; + + case INPUT_EVENT_KIND_REL: + evt.rel = *qapi->u.rel.data; + break; + + case INPUT_EVENT_KIND_ABS: + evt.abs = *qapi->u.abs.data; + break; + + case INPUT_EVENT_KIND_MTT: + evt.mtt = *qapi->u.mtt.data; + break; + + default: + g_assert_not_reached(); } + + qemu_input_event_send(con, &evt); } qemu_input_event_sync(); @@ -XXX,XX +XXX,XX @@ static void qemu_input_event_trace(QemuConsole *src, QemuInputEvent *evt) { const char *name; int qcode, idx = -1; - InputKeyEvent *key; + QemuInputKeyEvent *key; InputBtnEvent *btn; InputMoveEvent *move; InputMultiTouchEvent *mtt; @@ -XXX,XX +XXX,XX @@ static void qemu_input_event_trace(QemuConsole *src, QemuInputEvent *evt) } switch (evt->type) { case INPUT_EVENT_KIND_KEY: - key = evt->u.key.data; - switch (key->key->type) { + key = &evt->key; + switch (evt->key.key.type) { case KEY_VALUE_KIND_NUMBER: - qcode = qemu_input_key_number_to_qcode(key->key->u.number.data); + qcode = qemu_input_key_number_to_qcode(key->key.u.number.data); name = QKeyCode_str(qcode); - trace_input_event_key_number(idx, key->key->u.number.data, + trace_input_event_key_number(idx, key->key.u.number.data, name, key->down); break; case KEY_VALUE_KIND_QCODE: - name = QKeyCode_str(key->key->u.qcode.data); + name = QKeyCode_str(key->key.u.qcode.data); trace_input_event_key_qcode(idx, name, key->down); break; case KEY_VALUE_KIND__MAX: @@ -XXX,XX +XXX,XX @@ static void qemu_input_event_trace(QemuConsole *src, QemuInputEvent *evt) } break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; + btn = &evt->btn; name = btn->button < INPUT_BUTTON__MAX ? InputButton_str(btn->button) : "invalid"; trace_input_event_btn(idx, name, btn->down); break; case INPUT_EVENT_KIND_REL: - move = evt->u.rel.data; + move = &evt->rel; name = move->axis < INPUT_AXIS__MAX ? InputAxis_str(move->axis) : "invalid"; trace_input_event_rel(idx, name, move->value); break; case INPUT_EVENT_KIND_ABS: - move = evt->u.abs.data; + move = &evt->abs; name = move->axis < INPUT_AXIS__MAX ? InputAxis_str(move->axis) : "invalid"; trace_input_event_abs(idx, name, move->value); break; case INPUT_EVENT_KIND_MTT: - mtt = evt->u.mtt.data; + mtt = &evt->mtt; name = mtt->axis < INPUT_AXIS__MAX ? InputAxis_str(mtt->axis) : "invalid"; trace_input_event_mtt(idx, name, mtt->value); break; @@ -XXX,XX +XXX,XX @@ static void qemu_input_queue_process(void *opaque) + item->delay_ms); return; case QEMU_INPUT_QUEUE_EVENT: - qemu_input_event_send(item->src, item->evt); - qapi_free_InputEvent(item->evt); + qemu_input_event_send(item->src, &item->evt); break; case QEMU_INPUT_QUEUE_SYNC: qemu_input_event_sync(); @@ -XXX,XX +XXX,XX @@ static void qemu_input_queue_event(QemuInputEventQueueHead *queue, item->type = QEMU_INPUT_QUEUE_EVENT; item->src = src; - item->evt = evt; + item->evt = *evt; QTAILQ_INSERT_TAIL(queue, item, node); queue_count++; } @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send(QemuConsole *src, QemuInputEvent *evt) /* Expect all parts of QEMU to send events with QCodes exclusively. * Key numbers are only supported as end-user input via QMP */ assert(!(evt->type == INPUT_EVENT_KIND_KEY && - evt->u.key.data->key->type == KEY_VALUE_KIND_NUMBER)); + evt->key.key.type == KEY_VALUE_KIND_NUMBER)); /* @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send(QemuConsole *src, QemuInputEvent *evt) * need to deal with this mistake */ if (evt->type == INPUT_EVENT_KIND_KEY && - evt->u.key.data->key->u.qcode.data == Q_KEY_CODE_SYSRQ) { - evt->u.key.data->key->u.qcode.data = Q_KEY_CODE_PRINT; + evt->key.key.u.qcode.data == Q_KEY_CODE_SYSRQ) { + evt->key.key.u.qcode.data = Q_KEY_CODE_PRINT; } if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { @@ -XXX,XX +XXX,XX @@ void qemu_input_event_sync(void) replay_input_sync_event(); } -static QemuInputEvent *qemu_input_event_new_key(KeyValue *key, bool down) -{ - QemuInputEvent *evt = g_new0(QemuInputEvent, 1); - evt->u.key.data = g_new0(InputKeyEvent, 1); - evt->type = INPUT_EVENT_KIND_KEY; - evt->u.key.data->key = key; - evt->u.key.data->down = down; - return evt; -} - void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) { - QemuInputEvent *evt; - evt = qemu_input_event_new_key(key, down); + QemuInputEvent evt = { + .type = INPUT_EVENT_KIND_KEY, + .key = { + .key = *key, + .down = down, + }, + }; + + g_free(key); + if (QTAILQ_EMPTY(&kbd_queue)) { - qemu_input_event_send(src, evt); + qemu_input_event_send(src, &evt); qemu_input_event_sync(); - qapi_free_InputEvent(evt); } else if (queue_count < queue_limit) { - qemu_input_queue_event(&kbd_queue, src, evt); + qemu_input_queue_event(&kbd_queue, src, &evt); qemu_input_queue_sync(&kbd_queue); - } else { - qapi_free_InputEvent(evt); } } @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_key_delay(uint32_t delay_ms) void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down) { - InputBtnEvent bevt = { - .button = btn, - .down = down, - }; QemuInputEvent evt = { .type = INPUT_EVENT_KIND_BTN, - .u.btn.data = &bevt, + .btn = { + .button = btn, + .down = down, + } }; qemu_input_event_send(src, &evt); @@ -XXX,XX +XXX,XX @@ int qemu_input_scale_axis(int value, void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value) { - InputMoveEvent move = { - .axis = axis, - .value = value, - }; QemuInputEvent evt = { .type = INPUT_EVENT_KIND_REL, - .u.rel.data = &move, + .rel = { + .axis = axis, + .value = value, + }, }; qemu_input_event_send(src, &evt); @@ -XXX,XX +XXX,XX @@ void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value) void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int min_in, int max_in) { - InputMoveEvent move = { - .axis = axis, - .value = qemu_input_scale_axis(value, min_in, max_in, - INPUT_EVENT_ABS_MIN, - INPUT_EVENT_ABS_MAX), - }; QemuInputEvent evt = { .type = INPUT_EVENT_KIND_ABS, - .u.abs.data = &move, + .abs = { + .axis = axis, + .value = qemu_input_scale_axis(value, min_in, max_in, + INPUT_EVENT_ABS_MIN, + INPUT_EVENT_ABS_MAX), + }, }; qemu_input_event_send(src, &evt); @@ -XXX,XX +XXX,XX @@ void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, void qemu_input_queue_mtt(QemuConsole *src, InputMultiTouchType type, int slot, int tracking_id) { - InputMultiTouchEvent mtt = { - .type = type, - .slot = slot, - .tracking_id = tracking_id, - }; QemuInputEvent evt = { .type = INPUT_EVENT_KIND_MTT, - .u.mtt.data = &mtt, + .mtt = { + .type = type, + .slot = slot, + .tracking_id = tracking_id, + }, }; qemu_input_event_send(src, &evt); @@ -XXX,XX +XXX,XX @@ void qemu_input_queue_mtt(QemuConsole *src, InputMultiTouchType type, void qemu_input_queue_mtt_abs(QemuConsole *src, InputAxis axis, int value, int min_in, int max_in, int slot, int tracking_id) { - InputMultiTouchEvent mtt = { - .type = INPUT_MULTI_TOUCH_TYPE_DATA, - .slot = slot, - .tracking_id = tracking_id, - .axis = axis, - .value = qemu_input_scale_axis(value, min_in, max_in, - INPUT_EVENT_ABS_MIN, - INPUT_EVENT_ABS_MAX), - }; QemuInputEvent evt = { .type = INPUT_EVENT_KIND_MTT, - .u.mtt.data = &mtt, + .mtt = { + .type = INPUT_MULTI_TOUCH_TYPE_DATA, + .slot = slot, + .tracking_id = tracking_id, + .axis = axis, + .value = qemu_input_scale_axis(value, min_in, max_in, + INPUT_EVENT_ABS_MIN, + INPUT_EVENT_ABS_MAX), + } }; qemu_input_event_send(src, &evt); diff --git a/ui/vdagent.c b/ui/vdagent.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -XXX,XX +XXX,XX @@ static void vdagent_pointer_event(DeviceState *dev, QemuConsole *src, }; VDAgentChardev *vd = container_of(dev, struct VDAgentChardev, mouse_dev); - InputMoveEvent *move; - InputBtnEvent *btn; uint32_t xres, yres; switch (evt->type) { case INPUT_EVENT_KIND_ABS: - move = evt->u.abs.data; xres = qemu_console_get_width(src, 1024); yres = qemu_console_get_height(src, 768); - if (move->axis == INPUT_AXIS_X) { - vd->mouse_x = qemu_input_scale_axis(move->value, + if (evt->abs.axis == INPUT_AXIS_X) { + vd->mouse_x = qemu_input_scale_axis(evt->abs.value, INPUT_EVENT_ABS_MIN, INPUT_EVENT_ABS_MAX, 0, xres); - } else if (move->axis == INPUT_AXIS_Y) { - vd->mouse_y = qemu_input_scale_axis(move->value, + } else if (evt->abs.axis == INPUT_AXIS_Y) { + vd->mouse_y = qemu_input_scale_axis(evt->abs.value, INPUT_EVENT_ABS_MIN, INPUT_EVENT_ABS_MAX, 0, yres); @@ -XXX,XX +XXX,XX @@ static void vdagent_pointer_event(DeviceState *dev, QemuConsole *src, break; case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - if (btn->down) { - vd->mouse_btn |= bmap[btn->button]; + if (evt->btn.down) { + vd->mouse_btn |= bmap[evt->btn.button]; } else { - vd->mouse_btn &= ~bmap[btn->button]; + vd->mouse_btn &= ~bmap[evt->btn.button]; } break; -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Since commit af07e5ff02ae ("ui: convert key events to QKeyCodes immediately"), all internal key events are expected to be represented as QKeyCode. Replace KeyValue in QemuInputKeyEvent with QKeyCode to enforce that and simplify key code retrieval. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-3-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- include/ui/input.h | 5 ++--- hw/arm/musicpal.c | 2 +- hw/char/escc.c | 2 +- hw/display/xenfb.c | 2 +- hw/input/adb-kbd.c | 2 +- hw/input/hid.c | 5 ++--- hw/input/ps2.c | 2 +- hw/input/stellaris_gamepad.c | 2 +- hw/input/virtio-input-hid.c | 2 +- hw/m68k/next-kbd.c | 2 +- replay/replay-input.c | 29 ++++++----------------------- ui/input-keymap.c | 9 ++++----- ui/input.c | 34 +++++++--------------------------- ui/trace-events | 1 - 14 files changed, 29 insertions(+), 70 deletions(-) diff --git a/include/ui/input.h b/include/ui/input.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -XXX,XX +XXX,XX @@ typedef struct QemuInputHandler QemuInputHandler; typedef struct QemuInputHandlerState QemuInputHandlerState; typedef struct QemuInputKeyEvent { - KeyValue key; + QKeyCode key; bool down; } QemuInputKeyEvent; @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_key_delay(uint32_t delay_ms); int qemu_input_key_number_to_qcode(unsigned int nr); int qemu_input_key_value_to_number(const KeyValue *value); int qemu_input_key_value_to_qcode(const KeyValue *value); -int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, - int *codes); +int qemu_input_qcode_to_scancode(QKeyCode qcode, bool down, int *codes); int qemu_input_linux_to_qcode(unsigned int lnx); void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -XXX,XX +XXX,XX @@ static void musicpal_key_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { musicpal_key_state *s = MUSICPAL_KEY(dev); - int qcode = qemu_input_key_value_to_qcode(&evt->key.key); + int qcode = evt->key.key; uint32_t event = 0; int i; diff --git a/hw/char/escc.c b/hw/char/escc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -XXX,XX +XXX,XX @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, int qcode, keycode; assert(evt->type == INPUT_EVENT_KIND_KEY); - qcode = qemu_input_key_value_to_qcode(&evt->key.key); + qcode = evt->key.key; trace_escc_sunkbd_event_in(qcode, QKeyCode_str(qcode), evt->key.down); diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -XXX,XX +XXX,XX @@ static void xenfb_key_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { struct XenInput *xenfb = (struct XenInput *)dev; - int qcode = qemu_input_key_value_to_qcode(&evt->key.key); + int qcode = evt->key.key; int lnx; if (qcode < qemu_input_map_qcode_to_linux_len) { diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -XXX,XX +XXX,XX @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, KBDState *s = (KBDState *)dev; int qcode, keycode; - qcode = qemu_input_key_value_to_qcode(&evt->key.key); + qcode = evt->key.key; if (qcode >= ARRAY_SIZE(qcode_to_adb_keycode)) { return; } diff --git a/hw/input/hid.c b/hw/input/hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -XXX,XX +XXX,XX @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, int scancodes[3], i, count; int slot; - count = qemu_input_key_value_to_scancode(&evt->key.key, - evt->key.down, - scancodes); + count = qemu_input_qcode_to_scancode(evt->key.key, evt->key.down, + scancodes); if (hs->n + count > QUEUE_LENGTH) { trace_hid_kbd_queue_full(); return; diff --git a/hw/input/ps2.c b/hw/input/ps2.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); assert(evt->type == INPUT_EVENT_KIND_KEY); - qcode = qemu_input_key_value_to_qcode(&evt->key.key); + qcode = evt->key.key; mod = ps2_modifier_bit(qcode); trace_ps2_keyboard_event(s, qcode, evt->key.down, mod, diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -XXX,XX +XXX,XX @@ static void stellaris_gamepad_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { StellarisGamepad *s = STELLARIS_GAMEPAD(dev); - int qcode = qemu_input_key_value_to_qcode(&evt->key.key); + int qcode = evt->key.key; int i; for (i = 0; i < s->num_buttons; i++) { diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -XXX,XX +XXX,XX @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, switch (evt->type) { case INPUT_EVENT_KIND_KEY: - qcode = qemu_input_key_value_to_qcode(&evt->key.key); + qcode = evt->key.key; if (qcode < qemu_input_map_qcode_to_linux_len && qemu_input_map_qcode_to_linux[qcode]) { event.type = cpu_to_le16(EV_KEY); diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -XXX,XX +XXX,XX @@ static void nextkbd_event(DeviceState *dev, QemuConsole *src, NextKBDState *s = NEXTKBD(dev); int qcode, keycode; - qcode = qemu_input_key_value_to_qcode(&evt->key.key); + qcode = evt->key.key; if (qcode >= ARRAY_SIZE(qcode_to_nextkbd_keycode)) { return; } diff --git a/replay/replay-input.c b/replay/replay-input.c index XXXXXXX..XXXXXXX 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(QemuInputEvent *evt) switch (evt->type) { case INPUT_EVENT_KIND_KEY: - replay_put_dword(evt->key.key.type); - - switch (evt->key.key.type) { - case KEY_VALUE_KIND_NUMBER: - replay_put_qword(evt->key.key.u.number.data); - replay_put_byte(evt->key.down); - break; - case KEY_VALUE_KIND_QCODE: - replay_put_dword(evt->key.key.u.qcode.data); - replay_put_byte(evt->key.down); - break; - case KEY_VALUE_KIND__MAX: - /* keep gcc happy */ - break; - } + replay_put_dword(KEY_VALUE_KIND_QCODE); + replay_put_dword(evt->key.key); + replay_put_byte(evt->key.down); break; case INPUT_EVENT_KIND_BTN: replay_put_dword(evt->btn.button); @@ -XXX,XX +XXX,XX @@ QemuInputEvent *replay_read_input_event(void) evt->type = replay_get_dword(); switch (evt->type) { case INPUT_EVENT_KIND_KEY: - evt->key.key.type = replay_get_dword(); - - switch (evt->key.key.type) { + switch (replay_get_dword()) { case KEY_VALUE_KIND_NUMBER: - evt->key.key.u.number.data = replay_get_qword(); + evt->key.key = qemu_input_key_number_to_qcode(replay_get_qword()); evt->key.down = replay_get_byte(); break; case KEY_VALUE_KIND_QCODE: - evt->key.key.u.qcode.data = (QKeyCode)replay_get_dword(); + evt->key.key = (QKeyCode)replay_get_dword(); evt->key.down = replay_get_byte(); break; - case KEY_VALUE_KIND__MAX: - /* keep gcc happy */ - break; } break; case INPUT_EVENT_KIND_BTN: diff --git a/ui/input-keymap.c b/ui/input-keymap.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -XXX,XX +XXX,XX @@ int qemu_input_key_value_to_qcode(const KeyValue *value) } } -int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, - int *codes) +int qemu_input_qcode_to_scancode(QKeyCode qcode, bool down, int *codes) { - int keycode = qemu_input_key_value_to_number(value); + int keycode = qcode < qemu_input_map_qcode_to_qnum_len ? + qemu_input_map_qcode_to_qnum[qcode] : 0; int count = 0; - if (value->type == KEY_VALUE_KIND_QCODE && - value->u.qcode.data == Q_KEY_CODE_PAUSE) { + if (qcode == Q_KEY_CODE_PAUSE) { /* specific case */ int v = down ? 0 : 0x80; codes[count++] = 0xe1; diff --git a/ui/input.c b/ui/input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input.c +++ b/ui/input.c @@ -XXX,XX +XXX,XX @@ void qmp_input_send_event(const char *device, g_assert_not_reached(); } - evt.key.key.type = KEY_VALUE_KIND_QCODE; - evt.key.key.u.qcode.data = code; + evt.key.key = code; evt.key.down = qapi->u.key.data->down; break; } @@ -XXX,XX +XXX,XX @@ void qmp_input_send_event(const char *device, static void qemu_input_event_trace(QemuConsole *src, QemuInputEvent *evt) { const char *name; - int qcode, idx = -1; + int idx = -1; QemuInputKeyEvent *key; InputBtnEvent *btn; InputMoveEvent *move; @@ -XXX,XX +XXX,XX @@ static void qemu_input_event_trace(QemuConsole *src, QemuInputEvent *evt) switch (evt->type) { case INPUT_EVENT_KIND_KEY: key = &evt->key; - switch (evt->key.key.type) { - case KEY_VALUE_KIND_NUMBER: - qcode = qemu_input_key_number_to_qcode(key->key.u.number.data); - name = QKeyCode_str(qcode); - trace_input_event_key_number(idx, key->key.u.number.data, - name, key->down); - break; - case KEY_VALUE_KIND_QCODE: - name = QKeyCode_str(key->key.u.qcode.data); - trace_input_event_key_qcode(idx, name, key->down); - break; - case KEY_VALUE_KIND__MAX: - /* keep gcc happy */ - break; - } + name = QKeyCode_str(key->key); + trace_input_event_key_qcode(idx, name, key->down); break; case INPUT_EVENT_KIND_BTN: btn = &evt->btn; @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_impl(QemuConsole *src, QemuInputEvent *evt) void qemu_input_event_send(QemuConsole *src, QemuInputEvent *evt) { - /* Expect all parts of QEMU to send events with QCodes exclusively. - * Key numbers are only supported as end-user input via QMP */ - assert(!(evt->type == INPUT_EVENT_KIND_KEY && - evt->key.key.type == KEY_VALUE_KIND_NUMBER)); - - /* * 'sysrq' was mistakenly added to hack around the fact that * the ps2 driver was not generating correct scancodes sequences @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send(QemuConsole *src, QemuInputEvent *evt) * need to deal with this mistake */ if (evt->type == INPUT_EVENT_KIND_KEY && - evt->key.key.u.qcode.data == Q_KEY_CODE_SYSRQ) { - evt->key.key.u.qcode.data = Q_KEY_CODE_PRINT; + evt->key.key == Q_KEY_CODE_SYSRQ) { + evt->key.key = Q_KEY_CODE_PRINT; } if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) QemuInputEvent evt = { .type = INPUT_EVENT_KIND_KEY, .key = { - .key = *key, + .key = qemu_input_key_value_to_qcode(key), .down = down, }, }; diff --git a/ui/trace-events b/ui/trace-events index XXXXXXX..XXXXXXX 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -XXX,XX +XXX,XX @@ vnc_tight_zlib_init(void *state, int stream_id, const void *opaque) "VNC tight z # input.c -input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d" input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d" input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d" input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d" -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Linux input key codes are a better internal representation than QKeyCode: - With Linux input key codes as the internal representation, keys previously lost solely because the middle layer between event sources and sinks used QKeyCode will be preserved, since Linux key codes cover all keys that those sources and sinks use. For example, KEY_KPJPCOMMA cannot be represented with QKeyCode, but it is widely supported by event sources and sinks since it is included in linux, atset1, atset2, usb, x11, osx, qnum (derived from atset1), xorgxquartz, and xorgevdev (derived from linux). - They make it possible to pass through Linux host key codes to Linux guests to preserve all key inputs. - They simplify consumers by avoiding QKeyCode aliases, namely asterisk/kp_multiply and sysrq/print. This matches the approach used by virtio and Xen. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-4-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- include/ui/input.h | 49 ++++++++++++++++++++++++++++++- hw/arm/musicpal.c | 2 +- hw/char/escc.c | 2 +- hw/display/xenfb.c | 2 +- hw/input/adb-kbd.c | 2 +- hw/input/hid.c | 3 +- hw/input/ps2.c | 2 +- hw/input/stellaris_gamepad.c | 2 +- hw/input/virtio-input-hid.c | 2 +- hw/m68k/next-kbd.c | 2 +- replay/replay-input.c | 11 +++++-- tools/qemu-vnc/input.c | 13 +++++++-- ui/input-keymap.c | 56 +++++++++++++++++++++++++++++------- ui/input.c | 51 ++++++++++---------------------- tools/qemu-vnc/trace-events | 2 +- ui/meson.build | 14 +++++++++ 16 files changed, 151 insertions(+), 64 deletions(-) diff --git a/include/ui/input.h b/include/ui/input.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -XXX,XX +XXX,XX @@ typedef struct QemuInputHandler QemuInputHandler; typedef struct QemuInputHandlerState QemuInputHandlerState; typedef struct QemuInputKeyEvent { - QKeyCode key; + unsigned int key; bool down; } QemuInputKeyEvent; @@ -XXX,XX +XXX,XX @@ void qemu_input_event_sync(void); void qemu_input_event_sync_impl(void); void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); +void qemu_input_event_send_key_linux(QemuConsole *src, unsigned int lnx, + bool down); void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down); void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down); void qemu_input_event_send_key_delay(uint32_t delay_ms); int qemu_input_key_number_to_qcode(unsigned int nr); +unsigned int qemu_input_key_number_to_linux(unsigned int nr); int qemu_input_key_value_to_number(const KeyValue *value); int qemu_input_key_value_to_qcode(const KeyValue *value); +unsigned int qemu_input_key_value_to_linux(const KeyValue *value); int qemu_input_qcode_to_scancode(QKeyCode qcode, bool down, int *codes); +int qemu_input_linux_to_scancode(unsigned int lnx, bool down, int *codes); int qemu_input_linux_to_qcode(unsigned int lnx); void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down); @@ -XXX,XX +XXX,XX @@ void qemu_remove_mouse_mode_change_notifier(Notifier *notify); extern const guint qemu_input_map_atset1_to_qcode_len; extern const guint16 qemu_input_map_atset1_to_qcode[]; +extern const guint qemu_input_map_atset1_to_linux_len; +extern const guint16 qemu_input_map_atset1_to_linux[]; + extern const guint qemu_input_map_linux_to_qcode_len; extern const guint16 qemu_input_map_linux_to_qcode[]; extern const guint qemu_input_map_qcode_to_atset1_len; extern const guint16 qemu_input_map_qcode_to_atset1[]; +extern const guint qemu_input_map_linux_to_atset1_len; +extern const guint16 qemu_input_map_linux_to_atset1[]; + extern const guint qemu_input_map_qcode_to_atset2_len; extern const guint16 qemu_input_map_qcode_to_atset2[]; +extern const guint qemu_input_map_linux_to_atset2_len; +extern const guint16 qemu_input_map_linux_to_atset2[]; + extern const guint qemu_input_map_qcode_to_atset3_len; extern const guint16 qemu_input_map_qcode_to_atset3[]; +extern const guint qemu_input_map_linux_to_atset3_len; +extern const guint16 qemu_input_map_linux_to_atset3[]; + extern const guint qemu_input_map_qcode_to_linux_len; extern const guint16 qemu_input_map_qcode_to_linux[]; extern const guint qemu_input_map_qcode_to_qnum_len; extern const guint16 qemu_input_map_qcode_to_qnum[]; +extern const guint qemu_input_map_linux_to_qnum_len; +extern const guint16 qemu_input_map_linux_to_qnum[]; + extern const guint qemu_input_map_qcode_to_sun_len; extern const guint16 qemu_input_map_qcode_to_sun[]; +extern const guint qemu_input_map_linux_to_sun_len; +extern const guint16 qemu_input_map_linux_to_sun[]; + extern const guint qemu_input_map_qnum_to_qcode_len; extern const guint16 qemu_input_map_qnum_to_qcode[]; +extern const guint qemu_input_map_qnum_to_linux_len; +extern const guint16 qemu_input_map_qnum_to_linux[]; + extern const guint qemu_input_map_usb_to_qcode_len; extern const guint16 qemu_input_map_usb_to_qcode[]; +extern const guint qemu_input_map_usb_to_linux_len; +extern const guint16 qemu_input_map_usb_to_linux[]; + extern const guint qemu_input_map_win32_to_qcode_len; extern const guint16 qemu_input_map_win32_to_qcode[]; +extern const guint qemu_input_map_win32_to_linux_len; +extern const guint16 qemu_input_map_win32_to_linux[]; + extern const guint qemu_input_map_x11_to_qcode_len; extern const guint16 qemu_input_map_x11_to_qcode[]; +extern const guint qemu_input_map_x11_to_linux_len; +extern const guint16 qemu_input_map_x11_to_linux[]; + extern const guint qemu_input_map_xorgevdev_to_qcode_len; extern const guint16 qemu_input_map_xorgevdev_to_qcode[]; extern const guint qemu_input_map_xorgkbd_to_qcode_len; extern const guint16 qemu_input_map_xorgkbd_to_qcode[]; +extern const guint qemu_input_map_xorgkbd_to_linux_len; +extern const guint16 qemu_input_map_xorgkbd_to_linux[]; + extern const guint qemu_input_map_xorgxquartz_to_qcode_len; extern const guint16 qemu_input_map_xorgxquartz_to_qcode[]; +extern const guint qemu_input_map_xorgxquartz_to_linux_len; +extern const guint16 qemu_input_map_xorgxquartz_to_linux[]; + extern const guint qemu_input_map_xorgxwin_to_qcode_len; extern const guint16 qemu_input_map_xorgxwin_to_qcode[]; +extern const guint qemu_input_map_xorgxwin_to_linux_len; +extern const guint16 qemu_input_map_xorgxwin_to_linux[]; + extern const guint qemu_input_map_osx_to_qcode_len; extern const guint16 qemu_input_map_osx_to_qcode[]; +extern const guint qemu_input_map_osx_to_linux_len; +extern const guint16 qemu_input_map_osx_to_linux[]; + #endif /* INPUT_H */ diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -XXX,XX +XXX,XX @@ static void musicpal_key_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { musicpal_key_state *s = MUSICPAL_KEY(dev); - int qcode = evt->key.key; + int qcode = qemu_input_linux_to_qcode(evt->key.key); uint32_t event = 0; int i; diff --git a/hw/char/escc.c b/hw/char/escc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -XXX,XX +XXX,XX @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, int qcode, keycode; assert(evt->type == INPUT_EVENT_KIND_KEY); - qcode = evt->key.key; + qcode = qemu_input_linux_to_qcode(evt->key.key); trace_escc_sunkbd_event_in(qcode, QKeyCode_str(qcode), evt->key.down); diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -XXX,XX +XXX,XX @@ static void xenfb_key_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { struct XenInput *xenfb = (struct XenInput *)dev; - int qcode = evt->key.key; + int qcode = qemu_input_linux_to_qcode(evt->key.key); int lnx; if (qcode < qemu_input_map_qcode_to_linux_len) { diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -XXX,XX +XXX,XX @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, KBDState *s = (KBDState *)dev; int qcode, keycode; - qcode = evt->key.key; + qcode = qemu_input_linux_to_qcode(evt->key.key); if (qcode >= ARRAY_SIZE(qcode_to_adb_keycode)) { return; } diff --git a/hw/input/hid.c b/hw/input/hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -XXX,XX +XXX,XX @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { HIDState *hs = (HIDState *)dev; + int qcode = qemu_input_linux_to_qcode(evt->key.key); int scancodes[3], i, count; int slot; - count = qemu_input_qcode_to_scancode(evt->key.key, evt->key.down, + count = qemu_input_qcode_to_scancode(qcode, evt->key.down, scancodes); if (hs->n + count > QUEUE_LENGTH) { trace_hid_kbd_queue_full(); diff --git a/hw/input/ps2.c b/hw/input/ps2.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); assert(evt->type == INPUT_EVENT_KIND_KEY); - qcode = evt->key.key; + qcode = qemu_input_linux_to_qcode(evt->key.key); mod = ps2_modifier_bit(qcode); trace_ps2_keyboard_event(s, qcode, evt->key.down, mod, diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -XXX,XX +XXX,XX @@ static void stellaris_gamepad_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { StellarisGamepad *s = STELLARIS_GAMEPAD(dev); - int qcode = evt->key.key; + int qcode = qemu_input_linux_to_qcode(evt->key.key); int i; for (i = 0; i < s->num_buttons; i++) { diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -XXX,XX +XXX,XX @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, switch (evt->type) { case INPUT_EVENT_KIND_KEY: - qcode = evt->key.key; + qcode = qemu_input_linux_to_qcode(evt->key.key); if (qcode < qemu_input_map_qcode_to_linux_len && qemu_input_map_qcode_to_linux[qcode]) { event.type = cpu_to_le16(EV_KEY); diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -XXX,XX +XXX,XX @@ static void nextkbd_event(DeviceState *dev, QemuConsole *src, NextKBDState *s = NEXTKBD(dev); int qcode, keycode; - qcode = evt->key.key; + qcode = qemu_input_linux_to_qcode(evt->key.key); if (qcode >= ARRAY_SIZE(qcode_to_nextkbd_keycode)) { return; } diff --git a/replay/replay-input.c b/replay/replay-input.c index XXXXXXX..XXXXXXX 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(QemuInputEvent *evt) switch (evt->type) { case INPUT_EVENT_KIND_KEY: replay_put_dword(KEY_VALUE_KIND_QCODE); - replay_put_dword(evt->key.key); + replay_put_dword(qemu_input_linux_to_qcode(evt->key.key)); replay_put_byte(evt->key.down); break; case INPUT_EVENT_KIND_BTN: @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(QemuInputEvent *evt) QemuInputEvent *replay_read_input_event(void) { QemuInputEvent *evt = g_new(QemuInputEvent, 1); + int qcode; evt->type = replay_get_dword(); switch (evt->type) { case INPUT_EVENT_KIND_KEY: switch (replay_get_dword()) { case KEY_VALUE_KIND_NUMBER: - evt->key.key = qemu_input_key_number_to_qcode(replay_get_qword()); + qcode = qemu_input_key_number_to_qcode(replay_get_qword()); evt->key.down = replay_get_byte(); break; case KEY_VALUE_KIND_QCODE: - evt->key.key = (QKeyCode)replay_get_dword(); + qcode = (QKeyCode)replay_get_dword(); evt->key.down = replay_get_byte(); break; + default: + g_assert_not_reached(); } + evt->key.key = qcode < qemu_input_map_qcode_to_linux_len ? + qemu_input_map_qcode_to_linux[qcode] : 0; break; case INPUT_EVENT_KIND_BTN: evt->btn.button = (InputButton)replay_get_dword(); diff --git a/tools/qemu-vnc/input.c b/tools/qemu-vnc/input.c index XXXXXXX..XXXXXXX 100644 --- a/tools/qemu-vnc/input.c +++ b/tools/qemu-vnc/input.c @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_key_delay(uint32_t delay_ms) } void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) +{ + unsigned int lnx = qemu_input_map_qcode_to_linux[q]; + qemu_input_event_send_key_linux(src, lnx, down); +} + +void qemu_input_event_send_key_linux(QemuConsole *src, unsigned int lnx, + bool down) { QemuDBusDisplay1Keyboard *kbd; guint qnum; - trace_qemu_vnc_key_event(q, down); + trace_qemu_vnc_key_event(lnx, down); if (!src) { return; @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) return; } - if (q >= qemu_input_map_qcode_to_qnum_len) { + if (lnx >= qemu_input_map_linux_to_qnum_len) { return; } - qnum = qemu_input_map_qcode_to_qnum[q]; + qnum = qemu_input_map_linux_to_qnum[lnx]; if (down) { qemu_dbus_display1_keyboard_call_press( diff --git a/ui/input-keymap.c b/ui/input-keymap.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -XXX,XX +XXX,XX @@ #include "standard-headers/linux/input.h" #include "ui/input-keymap-atset1-to-qcode.c.inc" +#include "ui/input-keymap-atset1-to-linux.c.inc" #include "ui/input-keymap-linux-to-qcode.c.inc" #include "ui/input-keymap-qcode-to-atset1.c.inc" +#include "ui/input-keymap-linux-to-atset1.c.inc" #include "ui/input-keymap-qcode-to-atset2.c.inc" +#include "ui/input-keymap-linux-to-atset2.c.inc" #include "ui/input-keymap-qcode-to-atset3.c.inc" +#include "ui/input-keymap-linux-to-atset3.c.inc" #include "ui/input-keymap-qcode-to-linux.c.inc" #include "ui/input-keymap-qcode-to-qnum.c.inc" +#include "ui/input-keymap-linux-to-qnum.c.inc" #include "ui/input-keymap-qcode-to-sun.c.inc" +#include "ui/input-keymap-linux-to-sun.c.inc" #include "ui/input-keymap-qnum-to-qcode.c.inc" +#include "ui/input-keymap-qnum-to-linux.c.inc" #include "ui/input-keymap-usb-to-qcode.c.inc" +#include "ui/input-keymap-usb-to-linux.c.inc" #include "ui/input-keymap-win32-to-qcode.c.inc" +#include "ui/input-keymap-win32-to-linux.c.inc" #include "ui/input-keymap-x11-to-qcode.c.inc" +#include "ui/input-keymap-x11-to-linux.c.inc" #include "ui/input-keymap-xorgevdev-to-qcode.c.inc" #include "ui/input-keymap-xorgkbd-to-qcode.c.inc" +#include "ui/input-keymap-xorgkbd-to-linux.c.inc" #include "ui/input-keymap-xorgxquartz-to-qcode.c.inc" +#include "ui/input-keymap-xorgxquartz-to-linux.c.inc" #include "ui/input-keymap-xorgxwin-to-qcode.c.inc" +#include "ui/input-keymap-xorgxwin-to-linux.c.inc" #include "ui/input-keymap-osx-to-qcode.c.inc" +#include "ui/input-keymap-osx-to-linux.c.inc" int qemu_input_linux_to_qcode(unsigned int lnx) { @@ -XXX,XX +XXX,XX @@ int qemu_input_key_value_to_number(const KeyValue *value) int qemu_input_key_number_to_qcode(unsigned int nr) { - if (nr >= qemu_input_map_qnum_to_qcode_len) { - return 0; + return qemu_input_linux_to_qcode(qemu_input_key_number_to_linux(nr)); +} + +unsigned int qemu_input_key_number_to_linux(unsigned int nr) +{ + if (nr >= qemu_input_map_qnum_to_linux_len) { + return KEY_RESERVED; } - return qemu_input_map_qnum_to_qcode[nr]; + return qemu_input_map_qnum_to_linux[nr]; } int qemu_input_key_value_to_qcode(const KeyValue *value) { - if (value->type == KEY_VALUE_KIND_QCODE) { - return value->u.qcode.data; - } else { - assert(value->type == KEY_VALUE_KIND_NUMBER); - return qemu_input_key_number_to_qcode(value->u.number.data); + return qemu_input_linux_to_qcode(qemu_input_key_value_to_linux(value)); +} + +unsigned int qemu_input_key_value_to_linux(const KeyValue *value) +{ + switch (value->type) { + case KEY_VALUE_KIND_NUMBER: + return qemu_input_key_number_to_linux(value->u.number.data); + + case KEY_VALUE_KIND_QCODE: + return qemu_input_map_qcode_to_linux[value->u.qcode.data]; + + default: + g_assert_not_reached(); } } int qemu_input_qcode_to_scancode(QKeyCode qcode, bool down, int *codes) { - int keycode = qcode < qemu_input_map_qcode_to_qnum_len ? - qemu_input_map_qcode_to_qnum[qcode] : 0; + return qemu_input_linux_to_scancode(qemu_input_map_qcode_to_linux[qcode], + down, codes); +} + +int qemu_input_linux_to_scancode(unsigned int lnx, bool down, int *codes) +{ + int keycode = lnx < qemu_input_map_linux_to_qnum_len ? + qemu_input_map_linux_to_qnum[lnx] : 0; int count = 0; - if (qcode == Q_KEY_CODE_PAUSE) { + if (lnx == KEY_PAUSE) { /* specific case */ int v = down ? 0 : 0x80; codes[count++] = 0xe1; diff --git a/ui/input.c b/ui/input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input.c +++ b/ui/input.c @@ -XXX,XX +XXX,XX @@ void qmp_input_send_event(const char *device, evt.type = qapi->type; switch (qapi->type) { - case INPUT_EVENT_KIND_KEY: { - KeyValue *key = qapi->u.key.data->key; - QKeyCode code; - - switch (key->type) { - case KEY_VALUE_KIND_NUMBER: - code = qemu_input_key_number_to_qcode(key->u.number.data); - break; - case KEY_VALUE_KIND_QCODE: - code = key->u.qcode.data; - break; - default: - g_assert_not_reached(); - } - - evt.key.key = code; + case INPUT_EVENT_KIND_KEY: + evt.key.key = qemu_input_key_value_to_linux(qapi->u.key.data->key); evt.key.down = qapi->u.key.data->down; break; - } case INPUT_EVENT_KIND_BTN: evt.btn = *qapi->u.btn.data; @@ -XXX,XX +XXX,XX @@ static void qemu_input_event_trace(QemuConsole *src, QemuInputEvent *evt) switch (evt->type) { case INPUT_EVENT_KIND_KEY: key = &evt->key; - name = QKeyCode_str(key->key); + name = QKeyCode_str(qemu_input_linux_to_qcode(key->key)); trace_input_event_key_qcode(idx, name, key->down); break; case INPUT_EVENT_KIND_BTN: @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_impl(QemuConsole *src, QemuInputEvent *evt) void qemu_input_event_send(QemuConsole *src, QemuInputEvent *evt) { - /* - * 'sysrq' was mistakenly added to hack around the fact that - * the ps2 driver was not generating correct scancodes sequences - * when 'alt+print' was pressed. This flaw is now fixed and the - * 'sysrq' key serves no further purpose. We normalize it to - * 'print', so that downstream receivers of the event don't - * need to deal with this mistake - */ - if (evt->type == INPUT_EVENT_KIND_KEY && - evt->key.key == Q_KEY_CODE_SYSRQ) { - evt->key.key = Q_KEY_CODE_PRINT; - } - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { return; } @@ -XXX,XX +XXX,XX @@ void qemu_input_event_sync(void) } void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) +{ + unsigned int lnx = qemu_input_key_value_to_linux(key); + + g_free(key); + qemu_input_event_send_key_linux(src, lnx, down); +} + +void qemu_input_event_send_key_linux(QemuConsole *src, unsigned int lnx, + bool down) { QemuInputEvent evt = { .type = INPUT_EVENT_KIND_KEY, .key = { - .key = qemu_input_key_value_to_qcode(key), + .key = lnx, .down = down, }, }; - g_free(key); - if (QTAILQ_EMPTY(&kbd_queue)) { qemu_input_event_send(src, &evt); qemu_input_event_sync(); @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) { - QKeyCode code = qemu_input_key_number_to_qcode(num); - qemu_input_event_send_key_qcode(src, code, down); + unsigned int lnx = qemu_input_key_number_to_linux(num); + qemu_input_event_send_key_linux(src, lnx, down); } void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) diff --git a/tools/qemu-vnc/trace-events b/tools/qemu-vnc/trace-events index XXXXXXX..XXXXXXX 100644 --- a/tools/qemu-vnc/trace-events +++ b/tools/qemu-vnc/trace-events @@ -XXX,XX +XXX,XX @@ qemu_vnc_cursor_define(int width, int height, int hot_x, int hot_y) "w=%d h=%d h 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_key_event(unsigned int lnx, bool down) "lnx=%u down=%d" qemu_vnc_owner_appeared(const char *name) "peer=%s" 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" diff --git a/ui/meson.build b/ui/meson.build index XXXXXXX..XXXXXXX 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -XXX,XX +XXX,XX @@ keymaps = [ ['atset1', 'qcode'], + ['atset1', 'linux'], ['linux', 'qcode'], ['qcode', 'atset1'], + ['linux', 'atset1'], ['qcode', 'atset2'], + ['linux', 'atset2'], ['qcode', 'atset3'], + ['linux', 'atset3'], ['qcode', 'linux'], ['qcode', 'qnum'], + ['linux', 'qnum'], ['qcode', 'sun'], + ['linux', 'sun'], ['qnum', 'qcode'], + ['qnum', 'linux'], ['usb', 'qcode'], + ['usb', 'linux'], ['win32', 'qcode'], + ['win32', 'linux'], ['x11', 'qcode'], + ['x11', 'linux'], ['xorgevdev', 'qcode'], ['xorgkbd', 'qcode'], + ['xorgkbd', 'linux'], ['xorgxquartz', 'qcode'], + ['xorgxquartz', 'linux'], ['xorgxwin', 'qcode'], + ['xorgxwin', 'linux'], ['osx', 'qcode'], + ['osx', 'linux'], ] if have_system or xkbcommon.found() -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> xenfb and virtio-input no longer need key mapping because they operate on Linux key codes, but removing the key mapping code loses the ability to filter out KEY_RESERVED. Drop KEY_RESERVED at the common input event entry point so the logic is shared by both devices and no downstream input handler receives KEY_RESERVED accidentally. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-5-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/input.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/input.c b/ui/input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input.c +++ b/ui/input.c @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_impl(QemuConsole *src, QemuInputEvent *evt) void qemu_input_event_send(QemuConsole *src, QemuInputEvent *evt) { + if (evt->type == INPUT_EVENT_KIND_KEY && !evt->key.key) { + return; + } + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { return; } -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Add a text console helper that accepts Linux input key codes and use it as the common implementation for qemu_text_console_put_qcode(). This lets callers that already use Linux key codes avoid converting them back to QKeyCode. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-6-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- include/ui/console.h | 2 ++ ui/console.c | 66 +++++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -XXX,XX +XXX,XX @@ bool qemu_mouse_set(int index, Error **errp); void qemu_text_console_put_keysym(QemuTextConsole *s, int keysym); bool qemu_text_console_put_qcode(QemuTextConsole *s, int qcode, bool ctrl); +bool qemu_text_console_put_linux(QemuTextConsole *s, unsigned int lnx, + bool ctrl); void qemu_text_console_put_string(QemuTextConsole *s, const char *str, int len); /* consoles */ diff --git a/ui/console.c b/ui/console.c index XXXXXXX..XXXXXXX 100644 --- a/ui/console.c +++ b/ui/console.c @@ -XXX,XX +XXX,XX @@ */ #include "qemu/osdep.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/console.h" #include "ui/vgafont.h" #include "hw/core/qdev.h" @@ -XXX,XX +XXX,XX @@ void qemu_text_console_put_keysym(QemuTextConsole *s, int keysym) qemu_text_console_handle_keysym(s, keysym); } -static const int qcode_to_keysym[Q_KEY_CODE__MAX] = { - [Q_KEY_CODE_UP] = QEMU_KEY_UP, - [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN, - [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT, - [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT, - [Q_KEY_CODE_HOME] = QEMU_KEY_HOME, - [Q_KEY_CODE_END] = QEMU_KEY_END, - [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP, - [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN, - [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE, - [Q_KEY_CODE_TAB] = QEMU_KEY_TAB, - [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE, +static const int linux_to_keysym[] = { + [KEY_UP] = QEMU_KEY_UP, + [KEY_DOWN] = QEMU_KEY_DOWN, + [KEY_RIGHT] = QEMU_KEY_RIGHT, + [KEY_LEFT] = QEMU_KEY_LEFT, + [KEY_HOME] = QEMU_KEY_HOME, + [KEY_END] = QEMU_KEY_END, + [KEY_PAGEUP] = QEMU_KEY_PAGEUP, + [KEY_PAGEDOWN] = QEMU_KEY_PAGEDOWN, + [KEY_DELETE] = QEMU_KEY_DELETE, + [KEY_TAB] = QEMU_KEY_TAB, + [KEY_BACKSPACE] = QEMU_KEY_BACKSPACE, }; -static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = { - [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP, - [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN, - [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT, - [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT, - [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME, - [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END, - [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP, - [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN, +static const int ctrl_linux_to_keysym[] = { + [KEY_UP] = QEMU_KEY_CTRL_UP, + [KEY_DOWN] = QEMU_KEY_CTRL_DOWN, + [KEY_RIGHT] = QEMU_KEY_CTRL_RIGHT, + [KEY_LEFT] = QEMU_KEY_CTRL_LEFT, + [KEY_HOME] = QEMU_KEY_CTRL_HOME, + [KEY_END] = QEMU_KEY_CTRL_END, + [KEY_PAGEUP] = QEMU_KEY_CTRL_PAGEUP, + [KEY_PAGEDOWN] = QEMU_KEY_CTRL_PAGEDOWN, }; bool qemu_text_console_put_qcode(QemuTextConsole *s, int qcode, bool ctrl) { + unsigned int lnx = qemu_input_map_qcode_to_linux[qcode]; + return qemu_text_console_put_linux(s, lnx, ctrl); +} + +bool qemu_text_console_put_linux(QemuTextConsole *s, unsigned int lnx, + bool ctrl) +{ + size_t maplen; + const int *map; int keysym; - keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode]; + if (ctrl) { + maplen = ARRAY_SIZE(ctrl_linux_to_keysym); + map = ctrl_linux_to_keysym; + } else { + maplen = ARRAY_SIZE(linux_to_keysym); + map = linux_to_keysym; + } + + if (lnx >= maplen) { + return false; + } + + keysym = map[lnx]; if (keysym == 0) { return false; } -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-7-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- include/ui/kbd-state.h | 12 ++++---- ui/dbus-console.c | 4 +-- ui/gtk.c | 7 +++-- ui/kbd-state.c | 61 +++++++++++++++++++++----------------- ui/keymaps.c | 3 +- ui/sdl2-input.c | 3 +- ui/vnc.c | 10 +++++-- ui/cocoa.m | 67 +++++++++++++++++++++++++++++++----------- 8 files changed, 108 insertions(+), 59 deletions(-) diff --git a/include/ui/kbd-state.h b/include/ui/kbd-state.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/kbd-state.h +++ b/include/ui/kbd-state.h @@ -XXX,XX +XXX,XX @@ void qkbd_state_free(QKbdState *kbd); * This function takes care to not send suspious events (keyup event * for a key not pressed for example). * + * This function drops events with key codes outside the defined range. + * * @kbd: state tracker state. - * @qcode: the key pressed or released. + * @lnx: the key pressed or released. * @down: true for key down events, false otherwise. */ -void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down); +void qkbd_state_key_event(QKbdState *kbd, unsigned int lnx, bool down); /** * qkbd_state_set_delay: set key press delay. @@ -XXX,XX +XXX,XX @@ void qkbd_state_set_delay(QKbdState *kbd, int delay_ms); /** * qkbd_state_key_get: get key state. * - * Returns true when the key is down. + * Returns true when the key code is in the defined range and the key is down. * * @kbd: state tracker state. - * @qcode: the key to query. + * @lnx: the key to query. */ -bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode); +bool qkbd_state_key_get(QKbdState *kbd, unsigned int lnx); /** * qkbd_state_modifier_get: get modifier state. diff --git a/ui/dbus-console.c b/ui/dbus-console.c index XXXXXXX..XXXXXXX 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -XXX,XX +XXX,XX @@ dbus_kbd_press(DBusDisplayConsole *ddc, trace_dbus_kbd_press(arg_keycode); - qkbd_state_key_event(ddc->kbd, qcode, true); + qkbd_state_key_event(ddc->kbd, qemu_input_map_qcode_to_linux[qcode], true); qemu_dbus_display1_keyboard_complete_press(ddc->iface_kbd, invocation); @@ -XXX,XX +XXX,XX @@ dbus_kbd_release(DBusDisplayConsole *ddc, trace_dbus_kbd_release(arg_keycode); - qkbd_state_key_event(ddc->kbd, qcode, false); + qkbd_state_key_event(ddc->kbd, qemu_input_map_qcode_to_linux[qcode], false); qemu_dbus_display1_keyboard_complete_release(ddc->iface_kbd, invocation); diff --git a/ui/gtk.c b/ui/gtk.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -XXX,XX +XXX,XX @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) || key->hardware_keycode == VK_PAUSE #endif ) { - qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE, + qkbd_state_key_event(vc->gfx.kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_PAUSE], key->type == GDK_KEY_PRESS); return TRUE; } @@ -XXX,XX +XXX,XX @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) keycode = gd_get_keycode(key); qcode = gd_map_keycode(keycode); - trace_gd_key_event(vc->label, keycode, qcode, + trace_gd_key_event(vc->label, keycode, qemu_input_map_qcode_to_linux[qcode], (key->type == GDK_KEY_PRESS) ? "down" : "up"); - qkbd_state_key_event(vc->gfx.kbd, qcode, + qkbd_state_key_event(vc->gfx.kbd, qemu_input_map_qcode_to_linux[qcode], key->type == GDK_KEY_PRESS); return TRUE; diff --git a/ui/kbd-state.c b/ui/kbd-state.c index XXXXXXX..XXXXXXX 100644 --- a/ui/kbd-state.c +++ b/ui/kbd-state.c @@ -XXX,XX +XXX,XX @@ */ #include "qemu/osdep.h" #include "qemu/bitmap.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/console.h" #include "ui/input.h" #include "ui/kbd-state.h" @@ -XXX,XX +XXX,XX @@ struct QKbdState { QemuConsole *con; int key_delay_ms; - DECLARE_BITMAP(keys, Q_KEY_CODE__MAX); + DECLARE_BITMAP(keys, KEY_CNT); DECLARE_BITMAP(mods, QKBD_MOD__MAX); }; static void qkbd_state_modifier_update(QKbdState *kbd, - QKeyCode qcode1, QKeyCode qcode2, + unsigned int lnx1, unsigned int lnx2, QKbdModifier mod) { - if (test_bit(qcode1, kbd->keys) || test_bit(qcode2, kbd->keys)) { + if (test_bit(lnx1, kbd->keys) || test_bit(lnx2, kbd->keys)) { set_bit(mod, kbd->mods); } else { clear_bit(mod, kbd->mods); @@ -XXX,XX +XXX,XX @@ bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod) return test_bit(mod, kbd->mods); } -bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode) +bool qkbd_state_key_get(QKbdState *kbd, unsigned int lnx) { - return test_bit(qcode, kbd->keys); + return lnx < KEY_CNT && test_bit(lnx, kbd->keys); } -void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down) +void qkbd_state_key_event(QKbdState *kbd, unsigned int lnx, bool down) { - bool state = test_bit(qcode, kbd->keys); + bool state; + + if (lnx >= KEY_CNT) { + return; + } + + state = test_bit(lnx, kbd->keys); if (down == false /* got key-up event */ && state == false /* key is not pressed */) { @@ -XXX,XX +XXX,XX @@ void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down) /* update key and modifier state */ if (down) { - set_bit(qcode, kbd->keys); + set_bit(lnx, kbd->keys); } else { - clear_bit(qcode, kbd->keys); + clear_bit(lnx, kbd->keys); } - switch (qcode) { - case Q_KEY_CODE_SHIFT: - case Q_KEY_CODE_SHIFT_R: - qkbd_state_modifier_update(kbd, Q_KEY_CODE_SHIFT, Q_KEY_CODE_SHIFT_R, + switch (lnx) { + case KEY_LEFTSHIFT: + case KEY_RIGHTSHIFT: + qkbd_state_modifier_update(kbd, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, QKBD_MOD_SHIFT); break; - case Q_KEY_CODE_CTRL: - case Q_KEY_CODE_CTRL_R: - qkbd_state_modifier_update(kbd, Q_KEY_CODE_CTRL, Q_KEY_CODE_CTRL_R, + case KEY_LEFTCTRL: + case KEY_RIGHTCTRL: + qkbd_state_modifier_update(kbd, KEY_LEFTCTRL, KEY_RIGHTCTRL, QKBD_MOD_CTRL); break; - case Q_KEY_CODE_ALT: - qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT, Q_KEY_CODE_ALT, + case KEY_LEFTALT: + qkbd_state_modifier_update(kbd, KEY_LEFTALT, KEY_LEFTALT, QKBD_MOD_ALT); break; - case Q_KEY_CODE_ALT_R: - qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT_R, Q_KEY_CODE_ALT_R, + case KEY_RIGHTALT: + qkbd_state_modifier_update(kbd, KEY_RIGHTALT, KEY_RIGHTALT, QKBD_MOD_ALTGR); break; - case Q_KEY_CODE_CAPS_LOCK: + case KEY_CAPSLOCK: if (down) { change_bit(QKBD_MOD_CAPSLOCK, kbd->mods); } break; - case Q_KEY_CODE_NUM_LOCK: + case KEY_NUMLOCK: if (down) { change_bit(QKBD_MOD_NUMLOCK, kbd->mods); } @@ -XXX,XX +XXX,XX @@ void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down) /* send to guest */ if (qemu_console_is_graphic(kbd->con)) { - qemu_input_event_send_key_qcode(kbd->con, qcode, down); + qemu_input_event_send_key_linux(kbd->con, lnx, down); if (kbd->key_delay_ms) { qemu_input_event_send_key_delay(kbd->key_delay_ms); } @@ -XXX,XX +XXX,XX @@ void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down) void qkbd_state_lift_all_keys(QKbdState *kbd) { - int qcode; + unsigned int lnx; - for (qcode = 0; qcode < Q_KEY_CODE__MAX; qcode++) { - if (test_bit(qcode, kbd->keys)) { - qkbd_state_key_event(kbd, qcode, false); + for (lnx = 0; lnx < KEY_CNT; lnx++) { + if (test_bit(lnx, kbd->keys)) { + qkbd_state_key_event(kbd, lnx, false); } } } diff --git a/ui/keymaps.c b/ui/keymaps.c index XXXXXXX..XXXXXXX 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -XXX,XX +XXX,XX @@ int keysym2scancode(kbd_layout_t *k, int keysym, for (i = 0; i < keysym2code->count; i++) { QKeyCode qcode = qemu_input_key_number_to_qcode (keysym2code->keycodes[i]); - if (kbd && qkbd_state_key_get(kbd, qcode)) { + unsigned int lnx = qemu_input_map_qcode_to_linux[qcode]; + if (kbd && qkbd_state_key_get(kbd, lnx)) { return keysym2code->keycodes[i]; } } diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -XXX,XX +XXX,XX @@ void sdl2_process_key(struct sdl2_console *scon, qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode]; trace_sdl2_process_key(ev->keysym.scancode, qcode, ev->type == SDL_KEYDOWN ? "down" : "up"); - qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN); + qkbd_state_key_event(scon->kbd, qemu_input_map_qcode_to_linux[qcode], + ev->type == SDL_KEYDOWN); if (QEMU_IS_TEXT_CONSOLE(con)) { QemuTextConsole *s = QEMU_TEXT_CONSOLE(con); diff --git a/ui/vnc.c b/ui/vnc.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -XXX,XX +XXX,XX @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) static void press_key(VncState *vs, QKeyCode qcode) { - qkbd_state_key_event(vs->vd->kbd, qcode, true); - qkbd_state_key_event(vs->vd->kbd, qcode, false); + qkbd_state_key_event(vs->vd->kbd, qemu_input_map_qcode_to_linux[qcode], + true); + + qkbd_state_key_event(vs->vd->kbd, qemu_input_map_qcode_to_linux[qcode], + false); } static void vnc_led_state_change(VncState *vs) @@ -XXX,XX +XXX,XX @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } } - qkbd_state_key_event(vs->vd->kbd, qcode, down); + qkbd_state_key_event(vs->vd->kbd, qemu_input_map_qcode_to_linux[qcode], + down); if (QEMU_IS_TEXT_CONSOLE(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); diff --git a/ui/cocoa.m b/ui/cocoa.m index XXXXXXX..XXXXXXX 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -XXX,XX +XXX,XX @@ - (void) setFullGrab:(id)sender } - (void) toggleKey: (int)keycode { - qkbd_state_key_event(kbd, keycode, !qkbd_state_key_get(kbd, keycode)); + unsigned int lnx = qemu_input_map_qcode_to_linux[keycode]; + qkbd_state_key_event(kbd, lnx, !qkbd_state_key_get(kbd, lnx)); } // Does the work of sending input to the monitor @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event */ if (!!(modifiers & NSEventModifierFlagCapsLock) != qkbd_state_modifier_get(kbd, QKBD_MOD_CAPSLOCK)) { - qkbd_state_key_event(kbd, Q_KEY_CODE_CAPS_LOCK, true); - qkbd_state_key_event(kbd, Q_KEY_CODE_CAPS_LOCK, false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_CAPS_LOCK], + true); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_CAPS_LOCK], + false); } if (!(modifiers & NSEventModifierFlagShift)) { - qkbd_state_key_event(kbd, Q_KEY_CODE_SHIFT, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_SHIFT_R, false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_SHIFT], + false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_SHIFT_R], + false); } if (!(modifiers & NSEventModifierFlagControl)) { - qkbd_state_key_event(kbd, Q_KEY_CODE_CTRL, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_CTRL_R, false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_CTRL], + false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_CTRL_R], + false); } if (!(modifiers & NSEventModifierFlagOption)) { if (swap_opt_cmd) { - qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_META_L], + false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_META_R], + false); } else { - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_ALT], + false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_ALT_R], + false); } } if (!(modifiers & NSEventModifierFlagCommand)) { if (swap_opt_cmd) { - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_ALT], + false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_ALT_R], + false); } else { - qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false); - qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_META_L], + false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[Q_KEY_CODE_META_R], + false); } } @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event } if (qemu_console_is_graphic(dcl.con)) { - qkbd_state_key_event(kbd, keycode, true); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[keycode], + true); } else { [self handleMonitorInput: event]; } @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event } if (qemu_console_is_graphic(dcl.con)) { - qkbd_state_key_event(kbd, keycode, false); + qkbd_state_key_event(kbd, + qemu_input_map_qcode_to_linux[keycode], + false); } return true; case NSEventTypeScrollWheel: -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-8-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- hw/arm/musicpal.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -XXX,XX +XXX,XX @@ #include "hw/core/ptimer.h" #include "hw/core/qdev-properties.h" #include "hw/block/flash.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/console.h" #include "hw/i2c/i2c.h" #include "hw/i2c/bitbang_i2c.h" @@ -XXX,XX +XXX,XX @@ static void musicpal_key_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { musicpal_key_state *s = MUSICPAL_KEY(dev); - int qcode = qemu_input_linux_to_qcode(evt->key.key); uint32_t event = 0; int i; - switch (qcode) { - case Q_KEY_CODE_UP: + switch (evt->key.key) { + case KEY_UP: event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; break; - case Q_KEY_CODE_DOWN: + case KEY_DOWN: event = MP_KEY_WHEEL_NAV; break; - case Q_KEY_CODE_LEFT: + case KEY_LEFT: event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; break; - case Q_KEY_CODE_RIGHT: + case KEY_RIGHT: event = MP_KEY_WHEEL_VOL; break; - case Q_KEY_CODE_F: + case KEY_F: event = MP_KEY_BTN_FAVORITS; break; - case Q_KEY_CODE_TAB: + case KEY_TAB: event = MP_KEY_BTN_VOLUME; break; - case Q_KEY_CODE_RET: + case KEY_ENTER: event = MP_KEY_BTN_NAVIGATION; break; - case Q_KEY_CODE_M: + case KEY_M: event = MP_KEY_BTN_MENU; break; } -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-9-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- hw/char/escc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/char/escc.c b/hw/char/escc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -XXX,XX +XXX,XX @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "hw/char/escc.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/console.h" #include "qemu/cutils.h" @@ -XXX,XX +XXX,XX @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, trace_escc_sunkbd_event_in(qcode, QKeyCode_str(qcode), evt->key.down); - if (qcode == Q_KEY_CODE_CAPS_LOCK) { + if (evt->key.key == KEY_CAPSLOCK) { if (evt->key.down) { s->caps_lock_mode ^= 1; if (s->caps_lock_mode == 2) { @@ -XXX,XX +XXX,XX @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } } - if (qcode == Q_KEY_CODE_NUM_LOCK) { + if (evt->key.key == KEY_NUMLOCK) { if (evt->key.down) { s->num_lock_mode ^= 1; if (s->num_lock_mode == 2) { @@ -XXX,XX +XXX,XX @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } } - if (qcode >= qemu_input_map_qcode_to_sun_len) { + if (evt->key.key >= qemu_input_map_linux_to_sun_len) { return; } - keycode = qemu_input_map_qcode_to_sun[qcode]; + keycode = qemu_input_map_linux_to_sun[evt->key.key]; if (!evt->key.down) { keycode |= 0x80; } -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-10-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/xenfb.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -XXX,XX +XXX,XX @@ static int xenfb_send_position(struct XenInput *xenfb, return xenfb_kbd_event(xenfb, &event); } -/* - * Send a key event from the client to the guest OS - * QEMU gives us a QCode. - * We have to turn this into a Linux Input layer keycode. - * - * Wish we could just send scancodes straight to the guest which - * already has code for dealing with this... - */ +/* Send a key event from the client to the guest OS */ static void xenfb_key_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { struct XenInput *xenfb = (struct XenInput *)dev; - int qcode = qemu_input_linux_to_qcode(evt->key.key); - int lnx; - if (qcode < qemu_input_map_qcode_to_linux_len) { - lnx = qemu_input_map_qcode_to_linux[qcode]; - - if (lnx) { - trace_xenfb_key_event(xenfb, lnx, evt->key.down); - xenfb_send_key(xenfb, evt->key.down, lnx); - } - } + trace_xenfb_key_event(xenfb, evt->key.key, evt->key.down); + xenfb_send_key(xenfb, evt->key.down, evt->key.key); } /* -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-11-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- hw/input/adb-kbd.c | 242 ++++++++++++++++++++++----------------------- 1 file changed, 120 insertions(+), 122 deletions(-) diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -XXX,XX +XXX,XX @@ #include "hw/input/adb.h" #include "migration/vmstate.h" #include "qemu/module.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/input.h" #include "hw/input/adb-keys.h" #include "adb-internal.h" @@ -XXX,XX +XXX,XX @@ struct ADBKeyboardClass { /* The adb keyboard doesn't have every key imaginable */ #define NO_KEY 0xff -int qcode_to_adb_keycode[] = { +int linux_to_adb_keycode[] = { /* Make sure future additions are automatically set to NO_KEY */ - [0 ... 0xff] = NO_KEY, - - [Q_KEY_CODE_SHIFT] = ADB_KEY_LEFT_SHIFT, - [Q_KEY_CODE_SHIFT_R] = ADB_KEY_RIGHT_SHIFT, - [Q_KEY_CODE_ALT] = ADB_KEY_LEFT_OPTION, - [Q_KEY_CODE_ALT_R] = ADB_KEY_RIGHT_OPTION, - [Q_KEY_CODE_CTRL] = ADB_KEY_LEFT_CONTROL, - [Q_KEY_CODE_CTRL_R] = ADB_KEY_RIGHT_CONTROL, - [Q_KEY_CODE_META_L] = ADB_KEY_COMMAND, - [Q_KEY_CODE_META_R] = ADB_KEY_COMMAND, - [Q_KEY_CODE_SPC] = ADB_KEY_SPACEBAR, - - [Q_KEY_CODE_ESC] = ADB_KEY_ESC, - [Q_KEY_CODE_1] = ADB_KEY_1, - [Q_KEY_CODE_2] = ADB_KEY_2, - [Q_KEY_CODE_3] = ADB_KEY_3, - [Q_KEY_CODE_4] = ADB_KEY_4, - [Q_KEY_CODE_5] = ADB_KEY_5, - [Q_KEY_CODE_6] = ADB_KEY_6, - [Q_KEY_CODE_7] = ADB_KEY_7, - [Q_KEY_CODE_8] = ADB_KEY_8, - [Q_KEY_CODE_9] = ADB_KEY_9, - [Q_KEY_CODE_0] = ADB_KEY_0, - [Q_KEY_CODE_MINUS] = ADB_KEY_MINUS, - [Q_KEY_CODE_EQUAL] = ADB_KEY_EQUAL, - [Q_KEY_CODE_BACKSPACE] = ADB_KEY_DELETE, - [Q_KEY_CODE_TAB] = ADB_KEY_TAB, - [Q_KEY_CODE_Q] = ADB_KEY_Q, - [Q_KEY_CODE_W] = ADB_KEY_W, - [Q_KEY_CODE_E] = ADB_KEY_E, - [Q_KEY_CODE_R] = ADB_KEY_R, - [Q_KEY_CODE_T] = ADB_KEY_T, - [Q_KEY_CODE_Y] = ADB_KEY_Y, - [Q_KEY_CODE_U] = ADB_KEY_U, - [Q_KEY_CODE_I] = ADB_KEY_I, - [Q_KEY_CODE_O] = ADB_KEY_O, - [Q_KEY_CODE_P] = ADB_KEY_P, - [Q_KEY_CODE_BRACKET_LEFT] = ADB_KEY_LEFT_BRACKET, - [Q_KEY_CODE_BRACKET_RIGHT] = ADB_KEY_RIGHT_BRACKET, - [Q_KEY_CODE_RET] = ADB_KEY_RETURN, - [Q_KEY_CODE_A] = ADB_KEY_A, - [Q_KEY_CODE_S] = ADB_KEY_S, - [Q_KEY_CODE_D] = ADB_KEY_D, - [Q_KEY_CODE_F] = ADB_KEY_F, - [Q_KEY_CODE_G] = ADB_KEY_G, - [Q_KEY_CODE_H] = ADB_KEY_H, - [Q_KEY_CODE_J] = ADB_KEY_J, - [Q_KEY_CODE_K] = ADB_KEY_K, - [Q_KEY_CODE_L] = ADB_KEY_L, - [Q_KEY_CODE_SEMICOLON] = ADB_KEY_SEMICOLON, - [Q_KEY_CODE_APOSTROPHE] = ADB_KEY_APOSTROPHE, - [Q_KEY_CODE_GRAVE_ACCENT] = ADB_KEY_GRAVE_ACCENT, - [Q_KEY_CODE_BACKSLASH] = ADB_KEY_BACKSLASH, - [Q_KEY_CODE_Z] = ADB_KEY_Z, - [Q_KEY_CODE_X] = ADB_KEY_X, - [Q_KEY_CODE_C] = ADB_KEY_C, - [Q_KEY_CODE_V] = ADB_KEY_V, - [Q_KEY_CODE_B] = ADB_KEY_B, - [Q_KEY_CODE_N] = ADB_KEY_N, - [Q_KEY_CODE_M] = ADB_KEY_M, - [Q_KEY_CODE_COMMA] = ADB_KEY_COMMA, - [Q_KEY_CODE_DOT] = ADB_KEY_PERIOD, - [Q_KEY_CODE_SLASH] = ADB_KEY_FORWARD_SLASH, - [Q_KEY_CODE_ASTERISK] = ADB_KEY_KP_MULTIPLY, - [Q_KEY_CODE_CAPS_LOCK] = ADB_KEY_CAPS_LOCK, - - [Q_KEY_CODE_F1] = ADB_KEY_F1, - [Q_KEY_CODE_F2] = ADB_KEY_F2, - [Q_KEY_CODE_F3] = ADB_KEY_F3, - [Q_KEY_CODE_F4] = ADB_KEY_F4, - [Q_KEY_CODE_F5] = ADB_KEY_F5, - [Q_KEY_CODE_F6] = ADB_KEY_F6, - [Q_KEY_CODE_F7] = ADB_KEY_F7, - [Q_KEY_CODE_F8] = ADB_KEY_F8, - [Q_KEY_CODE_F9] = ADB_KEY_F9, - [Q_KEY_CODE_F10] = ADB_KEY_F10, - [Q_KEY_CODE_F11] = ADB_KEY_F11, - [Q_KEY_CODE_F12] = ADB_KEY_F12, - [Q_KEY_CODE_PRINT] = ADB_KEY_F13, - [Q_KEY_CODE_SYSRQ] = ADB_KEY_F13, - [Q_KEY_CODE_SCROLL_LOCK] = ADB_KEY_F14, - [Q_KEY_CODE_PAUSE] = ADB_KEY_F15, - - [Q_KEY_CODE_NUM_LOCK] = ADB_KEY_KP_CLEAR, - [Q_KEY_CODE_KP_EQUALS] = ADB_KEY_KP_EQUAL, - [Q_KEY_CODE_KP_DIVIDE] = ADB_KEY_KP_DIVIDE, - [Q_KEY_CODE_KP_MULTIPLY] = ADB_KEY_KP_MULTIPLY, - [Q_KEY_CODE_KP_SUBTRACT] = ADB_KEY_KP_SUBTRACT, - [Q_KEY_CODE_KP_ADD] = ADB_KEY_KP_PLUS, - [Q_KEY_CODE_KP_ENTER] = ADB_KEY_KP_ENTER, - [Q_KEY_CODE_KP_DECIMAL] = ADB_KEY_KP_PERIOD, - [Q_KEY_CODE_KP_0] = ADB_KEY_KP_0, - [Q_KEY_CODE_KP_1] = ADB_KEY_KP_1, - [Q_KEY_CODE_KP_2] = ADB_KEY_KP_2, - [Q_KEY_CODE_KP_3] = ADB_KEY_KP_3, - [Q_KEY_CODE_KP_4] = ADB_KEY_KP_4, - [Q_KEY_CODE_KP_5] = ADB_KEY_KP_5, - [Q_KEY_CODE_KP_6] = ADB_KEY_KP_6, - [Q_KEY_CODE_KP_7] = ADB_KEY_KP_7, - [Q_KEY_CODE_KP_8] = ADB_KEY_KP_8, - [Q_KEY_CODE_KP_9] = ADB_KEY_KP_9, - - [Q_KEY_CODE_UP] = ADB_KEY_UP, - [Q_KEY_CODE_DOWN] = ADB_KEY_DOWN, - [Q_KEY_CODE_LEFT] = ADB_KEY_LEFT, - [Q_KEY_CODE_RIGHT] = ADB_KEY_RIGHT, - - [Q_KEY_CODE_HELP] = ADB_KEY_HELP, - [Q_KEY_CODE_INSERT] = ADB_KEY_HELP, - [Q_KEY_CODE_DELETE] = ADB_KEY_FORWARD_DELETE, - [Q_KEY_CODE_HOME] = ADB_KEY_HOME, - [Q_KEY_CODE_END] = ADB_KEY_END, - [Q_KEY_CODE_PGUP] = ADB_KEY_PAGE_UP, - [Q_KEY_CODE_PGDN] = ADB_KEY_PAGE_DOWN, - - [Q_KEY_CODE_POWER] = ADB_KEY_POWER + [0 ... KEY_MAX] = NO_KEY, + + [KEY_LEFTSHIFT] = ADB_KEY_LEFT_SHIFT, + [KEY_RIGHTSHIFT] = ADB_KEY_RIGHT_SHIFT, + [KEY_LEFTALT] = ADB_KEY_LEFT_OPTION, + [KEY_RIGHTALT] = ADB_KEY_RIGHT_OPTION, + [KEY_LEFTCTRL] = ADB_KEY_LEFT_CONTROL, + [KEY_RIGHTCTRL] = ADB_KEY_RIGHT_CONTROL, + [KEY_LEFTMETA] = ADB_KEY_COMMAND, + [KEY_RIGHTMETA] = ADB_KEY_COMMAND, + [KEY_SPACE] = ADB_KEY_SPACEBAR, + + [KEY_ESC] = ADB_KEY_ESC, + [KEY_1] = ADB_KEY_1, + [KEY_2] = ADB_KEY_2, + [KEY_3] = ADB_KEY_3, + [KEY_4] = ADB_KEY_4, + [KEY_5] = ADB_KEY_5, + [KEY_6] = ADB_KEY_6, + [KEY_7] = ADB_KEY_7, + [KEY_8] = ADB_KEY_8, + [KEY_9] = ADB_KEY_9, + [KEY_0] = ADB_KEY_0, + [KEY_MINUS] = ADB_KEY_MINUS, + [KEY_EQUAL] = ADB_KEY_EQUAL, + [KEY_BACKSPACE] = ADB_KEY_DELETE, + [KEY_TAB] = ADB_KEY_TAB, + [KEY_Q] = ADB_KEY_Q, + [KEY_W] = ADB_KEY_W, + [KEY_E] = ADB_KEY_E, + [KEY_R] = ADB_KEY_R, + [KEY_T] = ADB_KEY_T, + [KEY_Y] = ADB_KEY_Y, + [KEY_U] = ADB_KEY_U, + [KEY_I] = ADB_KEY_I, + [KEY_O] = ADB_KEY_O, + [KEY_P] = ADB_KEY_P, + [KEY_LEFTBRACE] = ADB_KEY_LEFT_BRACKET, + [KEY_RIGHTBRACE] = ADB_KEY_RIGHT_BRACKET, + [KEY_ENTER] = ADB_KEY_RETURN, + [KEY_A] = ADB_KEY_A, + [KEY_S] = ADB_KEY_S, + [KEY_D] = ADB_KEY_D, + [KEY_F] = ADB_KEY_F, + [KEY_G] = ADB_KEY_G, + [KEY_H] = ADB_KEY_H, + [KEY_J] = ADB_KEY_J, + [KEY_K] = ADB_KEY_K, + [KEY_L] = ADB_KEY_L, + [KEY_SEMICOLON] = ADB_KEY_SEMICOLON, + [KEY_APOSTROPHE] = ADB_KEY_APOSTROPHE, + [KEY_GRAVE] = ADB_KEY_GRAVE_ACCENT, + [KEY_BACKSLASH] = ADB_KEY_BACKSLASH, + [KEY_Z] = ADB_KEY_Z, + [KEY_X] = ADB_KEY_X, + [KEY_C] = ADB_KEY_C, + [KEY_V] = ADB_KEY_V, + [KEY_B] = ADB_KEY_B, + [KEY_N] = ADB_KEY_N, + [KEY_M] = ADB_KEY_M, + [KEY_COMMA] = ADB_KEY_COMMA, + [KEY_DOT] = ADB_KEY_PERIOD, + [KEY_SLASH] = ADB_KEY_FORWARD_SLASH, + [KEY_CAPSLOCK] = ADB_KEY_CAPS_LOCK, + + [KEY_F1] = ADB_KEY_F1, + [KEY_F2] = ADB_KEY_F2, + [KEY_F3] = ADB_KEY_F3, + [KEY_F4] = ADB_KEY_F4, + [KEY_F5] = ADB_KEY_F5, + [KEY_F6] = ADB_KEY_F6, + [KEY_F7] = ADB_KEY_F7, + [KEY_F8] = ADB_KEY_F8, + [KEY_F9] = ADB_KEY_F9, + [KEY_F10] = ADB_KEY_F10, + [KEY_F11] = ADB_KEY_F11, + [KEY_F12] = ADB_KEY_F12, + [KEY_SYSRQ] = ADB_KEY_F13, + [KEY_SCROLLLOCK] = ADB_KEY_F14, + [KEY_PAUSE] = ADB_KEY_F15, + + [KEY_NUMLOCK] = ADB_KEY_KP_CLEAR, + [KEY_KPEQUAL] = ADB_KEY_KP_EQUAL, + [KEY_KPSLASH] = ADB_KEY_KP_DIVIDE, + [KEY_KPASTERISK] = ADB_KEY_KP_MULTIPLY, + [KEY_KPMINUS] = ADB_KEY_KP_SUBTRACT, + [KEY_KPPLUS] = ADB_KEY_KP_PLUS, + [KEY_KPENTER] = ADB_KEY_KP_ENTER, + [KEY_KPDOT] = ADB_KEY_KP_PERIOD, + [KEY_KP0] = ADB_KEY_KP_0, + [KEY_KP1] = ADB_KEY_KP_1, + [KEY_KP2] = ADB_KEY_KP_2, + [KEY_KP3] = ADB_KEY_KP_3, + [KEY_KP4] = ADB_KEY_KP_4, + [KEY_KP5] = ADB_KEY_KP_5, + [KEY_KP6] = ADB_KEY_KP_6, + [KEY_KP7] = ADB_KEY_KP_7, + [KEY_KP8] = ADB_KEY_KP_8, + [KEY_KP9] = ADB_KEY_KP_9, + + [KEY_UP] = ADB_KEY_UP, + [KEY_DOWN] = ADB_KEY_DOWN, + [KEY_LEFT] = ADB_KEY_LEFT, + [KEY_RIGHT] = ADB_KEY_RIGHT, + + [KEY_HELP] = ADB_KEY_HELP, + [KEY_INSERT] = ADB_KEY_HELP, + [KEY_DELETE] = ADB_KEY_FORWARD_DELETE, + [KEY_HOME] = ADB_KEY_HOME, + [KEY_END] = ADB_KEY_END, + [KEY_PAGEUP] = ADB_KEY_PAGE_UP, + [KEY_PAGEDOWN] = ADB_KEY_PAGE_DOWN, + + [KEY_POWER] = ADB_KEY_POWER }; static void adb_kbd_put_keycode(void *opaque, int keycode) @@ -XXX,XX +XXX,XX @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { KBDState *s = (KBDState *)dev; - int qcode, keycode; + int keycode; - qcode = qemu_input_linux_to_qcode(evt->key.key); - if (qcode >= ARRAY_SIZE(qcode_to_adb_keycode)) { + if (evt->key.key >= ARRAY_SIZE(linux_to_adb_keycode)) { return; } - /* FIXME: take handler into account when translating qcode */ - keycode = qcode_to_adb_keycode[qcode]; + /* FIXME: take handler into account when translating evt->key.key */ + keycode = linux_to_adb_keycode[evt->key.key]; if (keycode == NO_KEY) { /* We don't want to send this to the guest */ trace_adb_device_kbd_no_key(); return; -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-12-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- hw/input/hid.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/input/hid.c b/hw/input/hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -XXX,XX +XXX,XX @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { HIDState *hs = (HIDState *)dev; - int qcode = qemu_input_linux_to_qcode(evt->key.key); int scancodes[3], i, count; int slot; - count = qemu_input_qcode_to_scancode(qcode, evt->key.down, + count = qemu_input_linux_to_scancode(evt->key.key, evt->key.down, scancodes); if (hs->n + count > QUEUE_LENGTH) { trace_hid_kbd_queue_full(); -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-13-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- hw/input/ps2.c | 51 +++++++++++++++++++++---------------------- hw/input/trace-events | 2 +- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -XXX,XX +XXX,XX @@ #include "hw/core/sysbus.h" #include "hw/input/ps2.h" #include "migration/vmstate.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/console.h" #include "ui/input.h" #include "system/reset.h" @@ -XXX,XX +XXX,XX @@ static uint8_t translate_table[256] = { 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, }; -static unsigned int ps2_modifier_bit(QKeyCode key) +static unsigned int ps2_modifier_bit(unsigned int key) { switch (key) { - case Q_KEY_CODE_CTRL: + case KEY_LEFTCTRL: return MOD_CTRL_L; - case Q_KEY_CODE_CTRL_R: + case KEY_RIGHTCTRL: return MOD_CTRL_R; - case Q_KEY_CODE_SHIFT: + case KEY_LEFTSHIFT: return MOD_SHIFT_L; - case Q_KEY_CODE_SHIFT_R: + case KEY_RIGHTSHIFT: return MOD_SHIFT_R; - case Q_KEY_CODE_ALT: + case KEY_LEFTALT: return MOD_ALT_L; - case Q_KEY_CODE_ALT_R: + case KEY_RIGHTALT: return MOD_ALT_R; default: return 0; @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { PS2KbdState *s = (PS2KbdState *)dev; - int qcode; uint16_t keycode = 0; int mod; @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); assert(evt->type == INPUT_EVENT_KIND_KEY); - qcode = qemu_input_linux_to_qcode(evt->key.key); - mod = ps2_modifier_bit(qcode); - trace_ps2_keyboard_event(s, qcode, evt->key.down, mod, + mod = ps2_modifier_bit(evt->key.key); + trace_ps2_keyboard_event(s, evt->key.key, evt->key.down, mod, s->modifiers, s->scancode_set, s->translate); if (evt->key.down) { s->modifiers |= mod; @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } if (s->scancode_set == 1) { - if (qcode == Q_KEY_CODE_PAUSE) { + if (evt->key.key == KEY_PAUSE) { if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) { if (evt->key.down) { ps2_put_keycode(s, 0xe0); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0xc5); } } - } else if (qcode == Q_KEY_CODE_PRINT) { + } else if (evt->key.key == KEY_SYSRQ) { if (s->modifiers & MOD_ALT_L) { if (evt->key.down) { ps2_put_keycode(s, 0xb8); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0xaa); } } - } else if ((qcode == Q_KEY_CODE_LANG1 || qcode == Q_KEY_CODE_LANG2) + } else if ((evt->key.key == KEY_HANGEUL || evt->key.key == KEY_HANJA) && !evt->key.down) { /* Ignore release for these keys */ } else { - if (qcode < qemu_input_map_qcode_to_atset1_len) { - keycode = qemu_input_map_qcode_to_atset1[qcode]; + if (evt->key.key < qemu_input_map_linux_to_atset1_len) { + keycode = qemu_input_map_linux_to_atset1[evt->key.key]; } if (keycode) { if (keycode & 0xff00) { @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, keycode & 0xff); } else { qemu_log_mask(LOG_UNIMP, - "ps2: ignoring key with qcode %d\n", qcode); + "ps2: ignoring key %u\n", evt->key.key); } } } else if (s->scancode_set == 2) { - if (qcode == Q_KEY_CODE_PAUSE) { + if (evt->key.key == KEY_PAUSE) { if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) { if (evt->key.down) { ps2_put_keycode(s, 0xe0); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0x77); } } - } else if (qcode == Q_KEY_CODE_PRINT) { + } else if (evt->key.key == KEY_SYSRQ) { if (s->modifiers & MOD_ALT_L) { if (evt->key.down) { ps2_put_keycode(s, 0xf0); @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0x12); } } - } else if ((qcode == Q_KEY_CODE_LANG1 || qcode == Q_KEY_CODE_LANG2) && + } else if ((evt->key.key == KEY_HANGEUL || evt->key.key == KEY_HANJA) && !evt->key.down) { /* Ignore release for these keys */ } else { - if (qcode < qemu_input_map_qcode_to_atset2_len) { - keycode = qemu_input_map_qcode_to_atset2[qcode]; + if (evt->key.key < qemu_input_map_linux_to_atset2_len) { + keycode = qemu_input_map_linux_to_atset2[evt->key.key]; } if (keycode) { if (keycode & 0xff00) { @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, keycode & 0xff); } else { qemu_log_mask(LOG_UNIMP, - "ps2: ignoring key with qcode %d\n", qcode); + "ps2: ignoring key %u\n", evt->key.key); } } } else if (s->scancode_set == 3) { - if (qcode < qemu_input_map_qcode_to_atset3_len) { - keycode = qemu_input_map_qcode_to_atset3[qcode]; + if (evt->key.key < qemu_input_map_linux_to_atset3_len) { + keycode = qemu_input_map_linux_to_atset3[evt->key.key]; } if (keycode) { /* FIXME: break code should be configured on a key by key basis */ @@ -XXX,XX +XXX,XX @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, keycode); } else { qemu_log_mask(LOG_UNIMP, - "ps2: ignoring key with qcode %d\n", qcode); + "ps2: ignoring key %u\n", evt->key.key); } } } diff --git a/hw/input/trace-events b/hw/input/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -XXX,XX +XXX,XX @@ pckbd_kbd_write_data(uint64_t val) "0x%02"PRIx64 # ps2.c ps2_put_keycode(void *opaque, int keycode) "%p keycode 0x%02x" -ps2_keyboard_event(void *opaque, int qcode, int down, unsigned int modifier, unsigned int modifiers, int set, int xlate) "%p qcode %d down %d modifier 0x%x modifiers 0x%x set %d xlate %d" +ps2_keyboard_event(void *opaque, unsigned int lnx, int down, unsigned int modifier, unsigned int modifiers, int set, int xlate) "%p lnx %u down %d modifier 0x%x modifiers 0x%x set %d xlate %d" ps2_read_data(void *opaque) "%p" ps2_set_ledstate(void *s, int ledstate) "%p ledstate %d" ps2_reset_keyboard(void *s) "%p" -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-14-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- hw/input/virtio-input-hid.c | 40 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index XXXXXXX..XXXXXXX 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -XXX,XX +XXX,XX @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, { VirtIOInput *vinput = VIRTIO_INPUT(dev); virtio_input_event event; - int qcode; switch (evt->type) { case INPUT_EVENT_KIND_KEY: - qcode = qemu_input_linux_to_qcode(evt->key.key); - if (qcode < qemu_input_map_qcode_to_linux_len && - qemu_input_map_qcode_to_linux[qcode]) { - event.type = cpu_to_le16(EV_KEY); - event.code = cpu_to_le16(qemu_input_map_qcode_to_linux[qcode]); - event.value = cpu_to_le32(evt->key.down ? 1 : 0); - virtio_input_send(vinput, &event); - } else { - if (evt->key.down) { - fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__, - qcode, QKeyCode_str(qcode)); - } - } + event.type = cpu_to_le16(EV_KEY); + event.code = cpu_to_le16(evt->key.key); + event.value = cpu_to_le32(evt->key.down ? 1 : 0); + virtio_input_send(vinput, &event); break; case INPUT_EVENT_KIND_BTN: if ((evt->btn.button == INPUT_BUTTON_WHEEL_UP || @@ -XXX,XX +XXX,XX @@ static void virtio_keyboard_init(Object *obj) { VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj); VirtIOInput *vinput = VIRTIO_INPUT(obj); + virtio_input_config ext = { + .select = VIRTIO_INPUT_CFG_EV_BITS, + .subsel = EV_KEY, + .size = DIV_ROUND_UP(KEY_REPLY, 8) + }; + unsigned int i; vhid->handler = &virtio_keyboard_handler; virtio_input_init_config(vinput, virtio_keyboard_config); - virtio_input_extend_config(vinput, qemu_input_map_qcode_to_linux, - qemu_input_map_qcode_to_linux_len, - VIRTIO_INPUT_CFG_EV_BITS, EV_KEY); + + /* + * Cover as many keys as possible since we cannot tell what keys the host + * supports. This follows Linux xen-kbdfront's approach: + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/diff/drivers/input/xen-kbdfront.c?id=4ee36dc08e5c4d16d078f59acd6d9d536f9718dd + * + * Stop before KEY_REPLY for migration compatibility. + */ + for (i = KEY_ESC; i < KEY_REPLY; i++) { + ext.u.bitmap[i / 8] |= (1 << (i % 8)); + } + + virtio_input_add_config(vinput, &ext); } static const TypeInfo virtio_keyboard_info = { -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-15-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- hw/m68k/next-kbd.c | 118 ++++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c index XXXXXXX..XXXXXXX 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "hw/core/sysbus.h" #include "hw/m68k/next-cube.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/console.h" #include "migration/vmstate.h" #include "qom/object.h" @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps kbd_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static const int qcode_to_nextkbd_keycode[] = { - [Q_KEY_CODE_ESC] = 0x49, - [Q_KEY_CODE_1] = 0x4a, - [Q_KEY_CODE_2] = 0x4b, - [Q_KEY_CODE_3] = 0x4c, - [Q_KEY_CODE_4] = 0x4d, - [Q_KEY_CODE_5] = 0x50, - [Q_KEY_CODE_6] = 0x4f, - [Q_KEY_CODE_7] = 0x4e, - [Q_KEY_CODE_8] = 0x1e, - [Q_KEY_CODE_9] = 0x1f, - [Q_KEY_CODE_0] = 0x20, - [Q_KEY_CODE_MINUS] = 0x1d, - [Q_KEY_CODE_EQUAL] = 0x1c, - [Q_KEY_CODE_BACKSPACE] = 0x1b, - - [Q_KEY_CODE_Q] = 0x42, - [Q_KEY_CODE_W] = 0x43, - [Q_KEY_CODE_E] = 0x44, - [Q_KEY_CODE_R] = 0x45, - [Q_KEY_CODE_T] = 0x48, - [Q_KEY_CODE_Y] = 0x47, - [Q_KEY_CODE_U] = 0x46, - [Q_KEY_CODE_I] = 0x06, - [Q_KEY_CODE_O] = 0x07, - [Q_KEY_CODE_P] = 0x08, - [Q_KEY_CODE_RET] = 0x2a, - [Q_KEY_CODE_A] = 0x39, - [Q_KEY_CODE_S] = 0x3a, - - [Q_KEY_CODE_D] = 0x3b, - [Q_KEY_CODE_F] = 0x3c, - [Q_KEY_CODE_G] = 0x3d, - [Q_KEY_CODE_H] = 0x40, - [Q_KEY_CODE_J] = 0x3f, - [Q_KEY_CODE_K] = 0x3e, - [Q_KEY_CODE_L] = 0x2d, - [Q_KEY_CODE_SEMICOLON] = 0x2c, - [Q_KEY_CODE_APOSTROPHE] = 0x2b, - [Q_KEY_CODE_GRAVE_ACCENT] = 0x26, - [Q_KEY_CODE_Z] = 0x31, - [Q_KEY_CODE_X] = 0x32, - [Q_KEY_CODE_C] = 0x33, - [Q_KEY_CODE_V] = 0x34, - - [Q_KEY_CODE_B] = 0x35, - [Q_KEY_CODE_N] = 0x37, - [Q_KEY_CODE_M] = 0x36, - [Q_KEY_CODE_COMMA] = 0x2e, - [Q_KEY_CODE_DOT] = 0x2f, - [Q_KEY_CODE_SLASH] = 0x30, - - [Q_KEY_CODE_SPC] = 0x38, +static const int linux_to_nextkbd_keycode[] = { + [KEY_ESC] = 0x49, + [KEY_1] = 0x4a, + [KEY_2] = 0x4b, + [KEY_3] = 0x4c, + [KEY_4] = 0x4d, + [KEY_5] = 0x50, + [KEY_6] = 0x4f, + [KEY_7] = 0x4e, + [KEY_8] = 0x1e, + [KEY_9] = 0x1f, + [KEY_0] = 0x20, + [KEY_MINUS] = 0x1d, + [KEY_EQUAL] = 0x1c, + [KEY_BACKSPACE] = 0x1b, + + [KEY_Q] = 0x42, + [KEY_W] = 0x43, + [KEY_E] = 0x44, + [KEY_R] = 0x45, + [KEY_T] = 0x48, + [KEY_Y] = 0x47, + [KEY_U] = 0x46, + [KEY_I] = 0x06, + [KEY_O] = 0x07, + [KEY_P] = 0x08, + [KEY_ENTER] = 0x2a, + [KEY_A] = 0x39, + [KEY_S] = 0x3a, + + [KEY_D] = 0x3b, + [KEY_F] = 0x3c, + [KEY_G] = 0x3d, + [KEY_H] = 0x40, + [KEY_J] = 0x3f, + [KEY_K] = 0x3e, + [KEY_L] = 0x2d, + [KEY_SEMICOLON] = 0x2c, + [KEY_APOSTROPHE] = 0x2b, + [KEY_GRAVE] = 0x26, + [KEY_Z] = 0x31, + [KEY_X] = 0x32, + [KEY_C] = 0x33, + [KEY_V] = 0x34, + + [KEY_B] = 0x35, + [KEY_N] = 0x37, + [KEY_M] = 0x36, + [KEY_COMMA] = 0x2e, + [KEY_DOT] = 0x2f, + [KEY_SLASH] = 0x30, + + [KEY_SPACE] = 0x38, }; static void nextkbd_put_keycode(NextKBDState *s, int keycode) @@ -XXX,XX +XXX,XX @@ static void nextkbd_event(DeviceState *dev, QemuConsole *src, QemuInputEvent *evt) { NextKBDState *s = NEXTKBD(dev); - int qcode, keycode; + int keycode; - qcode = qemu_input_linux_to_qcode(evt->key.key); - if (qcode >= ARRAY_SIZE(qcode_to_nextkbd_keycode)) { + if (evt->key.key >= ARRAY_SIZE(linux_to_nextkbd_keycode)) { return; } /* Shift key currently has no keycode, so handle separately */ - if (qcode == Q_KEY_CODE_SHIFT) { + if (evt->key.key == KEY_LEFTSHIFT) { if (evt->key.down) { s->shift |= KD_LSHIFT; } else { @@ -XXX,XX +XXX,XX @@ static void nextkbd_event(DeviceState *dev, QemuConsole *src, } } - if (qcode == Q_KEY_CODE_SHIFT_R) { + if (evt->key.key == KEY_RIGHTSHIFT) { if (evt->key.down) { s->shift |= KD_RSHIFT; } else { @@ -XXX,XX +XXX,XX @@ static void nextkbd_event(DeviceState *dev, QemuConsole *src, } } - keycode = qcode_to_nextkbd_keycode[qcode]; + keycode = linux_to_nextkbd_keycode[evt->key.key]; if (!keycode) { return; } -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-16-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- replay/replay-input.c | 20 +++----------------- replay/replay.c | 2 +- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/replay/replay-input.c b/replay/replay-input.c index XXXXXXX..XXXXXXX 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(QemuInputEvent *evt) switch (evt->type) { case INPUT_EVENT_KIND_KEY: - replay_put_dword(KEY_VALUE_KIND_QCODE); - replay_put_dword(qemu_input_linux_to_qcode(evt->key.key)); + replay_put_dword(evt->key.key); replay_put_byte(evt->key.down); break; case INPUT_EVENT_KIND_BTN: @@ -XXX,XX +XXX,XX @@ void replay_save_input_event(QemuInputEvent *evt) QemuInputEvent *replay_read_input_event(void) { QemuInputEvent *evt = g_new(QemuInputEvent, 1); - int qcode; evt->type = replay_get_dword(); switch (evt->type) { case INPUT_EVENT_KIND_KEY: - switch (replay_get_dword()) { - case KEY_VALUE_KIND_NUMBER: - qcode = qemu_input_key_number_to_qcode(replay_get_qword()); - evt->key.down = replay_get_byte(); - break; - case KEY_VALUE_KIND_QCODE: - qcode = (QKeyCode)replay_get_dword(); - evt->key.down = replay_get_byte(); - break; - default: - g_assert_not_reached(); - } - evt->key.key = qcode < qemu_input_map_qcode_to_linux_len ? - qemu_input_map_qcode_to_linux[qcode] : 0; + evt->key.key = replay_get_dword(); + evt->key.down = replay_get_byte(); break; case INPUT_EVENT_KIND_BTN: evt->btn.button = (InputButton)replay_get_dword(); diff --git a/replay/replay.c b/replay/replay.c index XXXXXXX..XXXXXXX 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -XXX,XX +XXX,XX @@ /* Current version of the replay mechanism. Increase it when file format changes. */ -#define REPLAY_VERSION 0xe0200d +#define REPLAY_VERSION 0xe0200e /* Size of replay log header */ #define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t)) -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-17-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/cocoa.m | 328 ++++++++++++++++++++++++----------------------------- 1 file changed, 148 insertions(+), 180 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index XXXXXXX..XXXXXXX 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -XXX,XX +XXX,XX @@ #include "qemu/help-texts.h" #include "qemu-main.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/clipboard.h" #include "ui/console.h" #include "ui/input.h" @@ -XXX,XX +XXX,XX @@ static bool bool_with_bql(BoolCodeBlock block) return val; } -// Mac to QKeyCode conversion -static const int mac_to_qkeycode_map[] = { - [kVK_ANSI_A] = Q_KEY_CODE_A, - [kVK_ANSI_B] = Q_KEY_CODE_B, - [kVK_ANSI_C] = Q_KEY_CODE_C, - [kVK_ANSI_D] = Q_KEY_CODE_D, - [kVK_ANSI_E] = Q_KEY_CODE_E, - [kVK_ANSI_F] = Q_KEY_CODE_F, - [kVK_ANSI_G] = Q_KEY_CODE_G, - [kVK_ANSI_H] = Q_KEY_CODE_H, - [kVK_ANSI_I] = Q_KEY_CODE_I, - [kVK_ANSI_J] = Q_KEY_CODE_J, - [kVK_ANSI_K] = Q_KEY_CODE_K, - [kVK_ANSI_L] = Q_KEY_CODE_L, - [kVK_ANSI_M] = Q_KEY_CODE_M, - [kVK_ANSI_N] = Q_KEY_CODE_N, - [kVK_ANSI_O] = Q_KEY_CODE_O, - [kVK_ANSI_P] = Q_KEY_CODE_P, - [kVK_ANSI_Q] = Q_KEY_CODE_Q, - [kVK_ANSI_R] = Q_KEY_CODE_R, - [kVK_ANSI_S] = Q_KEY_CODE_S, - [kVK_ANSI_T] = Q_KEY_CODE_T, - [kVK_ANSI_U] = Q_KEY_CODE_U, - [kVK_ANSI_V] = Q_KEY_CODE_V, - [kVK_ANSI_W] = Q_KEY_CODE_W, - [kVK_ANSI_X] = Q_KEY_CODE_X, - [kVK_ANSI_Y] = Q_KEY_CODE_Y, - [kVK_ANSI_Z] = Q_KEY_CODE_Z, - - [kVK_ANSI_0] = Q_KEY_CODE_0, - [kVK_ANSI_1] = Q_KEY_CODE_1, - [kVK_ANSI_2] = Q_KEY_CODE_2, - [kVK_ANSI_3] = Q_KEY_CODE_3, - [kVK_ANSI_4] = Q_KEY_CODE_4, - [kVK_ANSI_5] = Q_KEY_CODE_5, - [kVK_ANSI_6] = Q_KEY_CODE_6, - [kVK_ANSI_7] = Q_KEY_CODE_7, - [kVK_ANSI_8] = Q_KEY_CODE_8, - [kVK_ANSI_9] = Q_KEY_CODE_9, - - [kVK_ANSI_Grave] = Q_KEY_CODE_GRAVE_ACCENT, - [kVK_ANSI_Minus] = Q_KEY_CODE_MINUS, - [kVK_ANSI_Equal] = Q_KEY_CODE_EQUAL, - [kVK_Delete] = Q_KEY_CODE_BACKSPACE, - [kVK_CapsLock] = Q_KEY_CODE_CAPS_LOCK, - [kVK_Tab] = Q_KEY_CODE_TAB, - [kVK_Return] = Q_KEY_CODE_RET, - [kVK_ANSI_LeftBracket] = Q_KEY_CODE_BRACKET_LEFT, - [kVK_ANSI_RightBracket] = Q_KEY_CODE_BRACKET_RIGHT, - [kVK_ANSI_Backslash] = Q_KEY_CODE_BACKSLASH, - [kVK_ANSI_Semicolon] = Q_KEY_CODE_SEMICOLON, - [kVK_ANSI_Quote] = Q_KEY_CODE_APOSTROPHE, - [kVK_ANSI_Comma] = Q_KEY_CODE_COMMA, - [kVK_ANSI_Period] = Q_KEY_CODE_DOT, - [kVK_ANSI_Slash] = Q_KEY_CODE_SLASH, - [kVK_Space] = Q_KEY_CODE_SPC, - - [kVK_ANSI_Keypad0] = Q_KEY_CODE_KP_0, - [kVK_ANSI_Keypad1] = Q_KEY_CODE_KP_1, - [kVK_ANSI_Keypad2] = Q_KEY_CODE_KP_2, - [kVK_ANSI_Keypad3] = Q_KEY_CODE_KP_3, - [kVK_ANSI_Keypad4] = Q_KEY_CODE_KP_4, - [kVK_ANSI_Keypad5] = Q_KEY_CODE_KP_5, - [kVK_ANSI_Keypad6] = Q_KEY_CODE_KP_6, - [kVK_ANSI_Keypad7] = Q_KEY_CODE_KP_7, - [kVK_ANSI_Keypad8] = Q_KEY_CODE_KP_8, - [kVK_ANSI_Keypad9] = Q_KEY_CODE_KP_9, - [kVK_ANSI_KeypadDecimal] = Q_KEY_CODE_KP_DECIMAL, - [kVK_ANSI_KeypadEnter] = Q_KEY_CODE_KP_ENTER, - [kVK_ANSI_KeypadPlus] = Q_KEY_CODE_KP_ADD, - [kVK_ANSI_KeypadMinus] = Q_KEY_CODE_KP_SUBTRACT, - [kVK_ANSI_KeypadMultiply] = Q_KEY_CODE_KP_MULTIPLY, - [kVK_ANSI_KeypadDivide] = Q_KEY_CODE_KP_DIVIDE, - [kVK_ANSI_KeypadEquals] = Q_KEY_CODE_KP_EQUALS, - [kVK_ANSI_KeypadClear] = Q_KEY_CODE_NUM_LOCK, - - [kVK_UpArrow] = Q_KEY_CODE_UP, - [kVK_DownArrow] = Q_KEY_CODE_DOWN, - [kVK_LeftArrow] = Q_KEY_CODE_LEFT, - [kVK_RightArrow] = Q_KEY_CODE_RIGHT, - - [kVK_Help] = Q_KEY_CODE_INSERT, - [kVK_Home] = Q_KEY_CODE_HOME, - [kVK_PageUp] = Q_KEY_CODE_PGUP, - [kVK_PageDown] = Q_KEY_CODE_PGDN, - [kVK_End] = Q_KEY_CODE_END, - [kVK_ForwardDelete] = Q_KEY_CODE_DELETE, - - [kVK_Escape] = Q_KEY_CODE_ESC, +// Mac to Linux conversion +static const unsigned int mac_to_linux_map[] = { + [kVK_ANSI_A] = KEY_A, + [kVK_ANSI_B] = KEY_B, + [kVK_ANSI_C] = KEY_C, + [kVK_ANSI_D] = KEY_D, + [kVK_ANSI_E] = KEY_E, + [kVK_ANSI_F] = KEY_F, + [kVK_ANSI_G] = KEY_G, + [kVK_ANSI_H] = KEY_H, + [kVK_ANSI_I] = KEY_I, + [kVK_ANSI_J] = KEY_J, + [kVK_ANSI_K] = KEY_K, + [kVK_ANSI_L] = KEY_L, + [kVK_ANSI_M] = KEY_M, + [kVK_ANSI_N] = KEY_N, + [kVK_ANSI_O] = KEY_O, + [kVK_ANSI_P] = KEY_P, + [kVK_ANSI_Q] = KEY_Q, + [kVK_ANSI_R] = KEY_R, + [kVK_ANSI_S] = KEY_S, + [kVK_ANSI_T] = KEY_T, + [kVK_ANSI_U] = KEY_U, + [kVK_ANSI_V] = KEY_V, + [kVK_ANSI_W] = KEY_W, + [kVK_ANSI_X] = KEY_X, + [kVK_ANSI_Y] = KEY_Y, + [kVK_ANSI_Z] = KEY_Z, + + [kVK_ANSI_0] = KEY_0, + [kVK_ANSI_1] = KEY_1, + [kVK_ANSI_2] = KEY_2, + [kVK_ANSI_3] = KEY_3, + [kVK_ANSI_4] = KEY_4, + [kVK_ANSI_5] = KEY_5, + [kVK_ANSI_6] = KEY_6, + [kVK_ANSI_7] = KEY_7, + [kVK_ANSI_8] = KEY_8, + [kVK_ANSI_9] = KEY_9, + + [kVK_ANSI_Grave] = KEY_GRAVE, + [kVK_ANSI_Minus] = KEY_MINUS, + [kVK_ANSI_Equal] = KEY_EQUAL, + [kVK_Delete] = KEY_BACKSPACE, + [kVK_CapsLock] = KEY_CAPSLOCK, + [kVK_Tab] = KEY_TAB, + [kVK_Return] = KEY_ENTER, + [kVK_ANSI_LeftBracket] = KEY_LEFTBRACE, + [kVK_ANSI_RightBracket] = KEY_RIGHTBRACE, + [kVK_ANSI_Backslash] = KEY_BACKSLASH, + [kVK_ANSI_Semicolon] = KEY_SEMICOLON, + [kVK_ANSI_Quote] = KEY_APOSTROPHE, + [kVK_ANSI_Comma] = KEY_COMMA, + [kVK_ANSI_Period] = KEY_DOT, + [kVK_ANSI_Slash] = KEY_SLASH, + [kVK_Space] = KEY_SPACE, + + [kVK_ANSI_Keypad0] = KEY_KP0, + [kVK_ANSI_Keypad1] = KEY_KP1, + [kVK_ANSI_Keypad2] = KEY_KP2, + [kVK_ANSI_Keypad3] = KEY_KP3, + [kVK_ANSI_Keypad4] = KEY_KP4, + [kVK_ANSI_Keypad5] = KEY_KP5, + [kVK_ANSI_Keypad6] = KEY_KP6, + [kVK_ANSI_Keypad7] = KEY_KP7, + [kVK_ANSI_Keypad8] = KEY_KP8, + [kVK_ANSI_Keypad9] = KEY_KP9, + [kVK_ANSI_KeypadDecimal] = KEY_KPDOT, + [kVK_ANSI_KeypadEnter] = KEY_KPENTER, + [kVK_ANSI_KeypadPlus] = KEY_KPPLUS, + [kVK_ANSI_KeypadMinus] = KEY_KPMINUS, + [kVK_ANSI_KeypadMultiply] = KEY_KPASTERISK, + [kVK_ANSI_KeypadDivide] = KEY_KPSLASH, + [kVK_ANSI_KeypadEquals] = KEY_KPEQUAL, + [kVK_ANSI_KeypadClear] = KEY_NUMLOCK, + + [kVK_UpArrow] = KEY_UP, + [kVK_DownArrow] = KEY_DOWN, + [kVK_LeftArrow] = KEY_LEFT, + [kVK_RightArrow] = KEY_RIGHT, + + [kVK_Help] = KEY_INSERT, + [kVK_Home] = KEY_HOME, + [kVK_PageUp] = KEY_PAGEUP, + [kVK_PageDown] = KEY_PAGEDOWN, + [kVK_End] = KEY_END, + [kVK_ForwardDelete] = KEY_DELETE, + + [kVK_Escape] = KEY_ESC, /* The Power key can't be used directly because the operating system uses * it. This key can be emulated by using it in place of another key such as * F1. Don't forget to disable the real key binding. */ - /* [kVK_F1] = Q_KEY_CODE_POWER, */ - - [kVK_F1] = Q_KEY_CODE_F1, - [kVK_F2] = Q_KEY_CODE_F2, - [kVK_F3] = Q_KEY_CODE_F3, - [kVK_F4] = Q_KEY_CODE_F4, - [kVK_F5] = Q_KEY_CODE_F5, - [kVK_F6] = Q_KEY_CODE_F6, - [kVK_F7] = Q_KEY_CODE_F7, - [kVK_F8] = Q_KEY_CODE_F8, - [kVK_F9] = Q_KEY_CODE_F9, - [kVK_F10] = Q_KEY_CODE_F10, - [kVK_F11] = Q_KEY_CODE_F11, - [kVK_F12] = Q_KEY_CODE_F12, - [kVK_F13] = Q_KEY_CODE_PRINT, - [kVK_F14] = Q_KEY_CODE_SCROLL_LOCK, - [kVK_F15] = Q_KEY_CODE_PAUSE, + /* [kVK_F1] = KEY_POWER, */ + + [kVK_F1] = KEY_F1, + [kVK_F2] = KEY_F2, + [kVK_F3] = KEY_F3, + [kVK_F4] = KEY_F4, + [kVK_F5] = KEY_F5, + [kVK_F6] = KEY_F6, + [kVK_F7] = KEY_F7, + [kVK_F8] = KEY_F8, + [kVK_F9] = KEY_F9, + [kVK_F10] = KEY_F10, + [kVK_F11] = KEY_F11, + [kVK_F12] = KEY_F12, + [kVK_F13] = KEY_SYSRQ, + [kVK_F14] = KEY_SCROLLLOCK, + [kVK_F15] = KEY_PAUSE, // JIS keyboards only - [kVK_JIS_Yen] = Q_KEY_CODE_YEN, - [kVK_JIS_Underscore] = Q_KEY_CODE_RO, - [kVK_JIS_KeypadComma] = Q_KEY_CODE_KP_COMMA, - [kVK_JIS_Eisu] = Q_KEY_CODE_MUHENKAN, - [kVK_JIS_Kana] = Q_KEY_CODE_HENKAN, + [kVK_JIS_Yen] = KEY_YEN, + [kVK_JIS_Underscore] = KEY_RO, + [kVK_JIS_KeypadComma] = KEY_KPCOMMA, + [kVK_JIS_Eisu] = KEY_MUHENKAN, + [kVK_JIS_Kana] = KEY_HENKAN, /* * The eject and volume keys can't be used here because they are handled at @@ -XXX,XX +XXX,XX @@ static bool bool_with_bql(BoolCodeBlock block) */ }; -static int cocoa_keycode_to_qemu(int keycode) +static unsigned int cocoa_keycode_to_linux(int keycode) { - if (ARRAY_SIZE(mac_to_qkeycode_map) <= keycode) { + if (ARRAY_SIZE(mac_to_linux_map) <= keycode) { error_report("(cocoa) warning unknown keycode 0x%x", keycode); return 0; } - return mac_to_qkeycode_map[keycode]; + return mac_to_linux_map[keycode]; } /* Displays an alert dialog box with the specified message */ @@ -XXX,XX +XXX,XX @@ - (void) setFullGrab:(id)sender CFRelease(tapEventsSrc); } -- (void) toggleKey: (int)keycode { - unsigned int lnx = qemu_input_map_qcode_to_linux[keycode]; - qkbd_state_key_event(kbd, lnx, !qkbd_state_key_get(kbd, lnx)); +- (void) toggleKey: (unsigned int)keycode { + qkbd_state_key_event(kbd, keycode, !qkbd_state_key_get(kbd, keycode)); } // Does the work of sending input to the monitor @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event /* Return true if we handled the event, false if it should be given to OSX */ COCOA_DEBUG("QemuCocoaView: handleEvent\n"); InputButton button; - int keycode = 0; + unsigned int keycode; NSUInteger modifiers = [event modifierFlags]; /* @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event */ if (!!(modifiers & NSEventModifierFlagCapsLock) != qkbd_state_modifier_get(kbd, QKBD_MOD_CAPSLOCK)) { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_CAPS_LOCK], - true); - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_CAPS_LOCK], - false); + qkbd_state_key_event(kbd, KEY_CAPSLOCK, true); + qkbd_state_key_event(kbd, KEY_CAPSLOCK, false); } if (!(modifiers & NSEventModifierFlagShift)) { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_SHIFT], - false); - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_SHIFT_R], - false); + qkbd_state_key_event(kbd, KEY_LEFTSHIFT, false); + qkbd_state_key_event(kbd, KEY_RIGHTSHIFT, false); } if (!(modifiers & NSEventModifierFlagControl)) { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_CTRL], - false); - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_CTRL_R], - false); + qkbd_state_key_event(kbd, KEY_LEFTCTRL, false); + qkbd_state_key_event(kbd, KEY_RIGHTCTRL, false); } if (!(modifiers & NSEventModifierFlagOption)) { if (swap_opt_cmd) { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_META_L], - false); - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_META_R], - false); + qkbd_state_key_event(kbd, KEY_LEFTMETA, false); + qkbd_state_key_event(kbd, KEY_RIGHTMETA, false); } else { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_ALT], - false); - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_ALT_R], - false); + qkbd_state_key_event(kbd, KEY_LEFTALT, false); + qkbd_state_key_event(kbd, KEY_RIGHTALT, false); } } if (!(modifiers & NSEventModifierFlagCommand)) { if (swap_opt_cmd) { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_ALT], - false); - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_ALT_R], - false); + qkbd_state_key_event(kbd, KEY_LEFTALT, false); + qkbd_state_key_event(kbd, KEY_RIGHTALT, false); } else { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_META_L], - false); - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_META_R], - false); + qkbd_state_key_event(kbd, KEY_LEFTMETA, false); + qkbd_state_key_event(kbd, KEY_RIGHTMETA, false); } } @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event switch ([event keyCode]) { case kVK_Shift: if (!!(modifiers & NSEventModifierFlagShift)) { - [self toggleKey:Q_KEY_CODE_SHIFT]; + [self toggleKey:KEY_LEFTSHIFT]; } break; case kVK_RightShift: if (!!(modifiers & NSEventModifierFlagShift)) { - [self toggleKey:Q_KEY_CODE_SHIFT_R]; + [self toggleKey:KEY_RIGHTSHIFT]; } break; case kVK_Control: if (!!(modifiers & NSEventModifierFlagControl)) { - [self toggleKey:Q_KEY_CODE_CTRL]; + [self toggleKey:KEY_LEFTCTRL]; } break; case kVK_RightControl: if (!!(modifiers & NSEventModifierFlagControl)) { - [self toggleKey:Q_KEY_CODE_CTRL_R]; + [self toggleKey:KEY_RIGHTCTRL]; } break; case kVK_Option: if (!!(modifiers & NSEventModifierFlagOption)) { if (swap_opt_cmd) { - [self toggleKey:Q_KEY_CODE_META_L]; + [self toggleKey:KEY_LEFTMETA]; } else { - [self toggleKey:Q_KEY_CODE_ALT]; + [self toggleKey:KEY_LEFTALT]; } } break; @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event case kVK_RightOption: if (!!(modifiers & NSEventModifierFlagOption)) { if (swap_opt_cmd) { - [self toggleKey:Q_KEY_CODE_META_R]; + [self toggleKey:KEY_RIGHTMETA]; } else { - [self toggleKey:Q_KEY_CODE_ALT_R]; + [self toggleKey:KEY_RIGHTALT]; } } break; @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event !!(modifiers & NSEventModifierFlagCommand) && left_command_key_enabled) { if (swap_opt_cmd) { - [self toggleKey:Q_KEY_CODE_ALT]; + [self toggleKey:KEY_LEFTALT]; } else { - [self toggleKey:Q_KEY_CODE_META_L]; + [self toggleKey:KEY_LEFTMETA]; } } break; @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event if (isMouseGrabbed && !!(modifiers & NSEventModifierFlagCommand)) { if (swap_opt_cmd) { - [self toggleKey:Q_KEY_CODE_ALT_R]; + [self toggleKey:KEY_RIGHTALT]; } else { - [self toggleKey:Q_KEY_CODE_META_R]; + [self toggleKey:KEY_RIGHTMETA]; } } break; } return true; case NSEventTypeKeyDown: - keycode = cocoa_keycode_to_qemu([event keyCode]); + keycode = cocoa_keycode_to_linux([event keyCode]); // forward command key combos to the host UI unless the mouse is grabbed if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) { @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event } if (qemu_console_is_graphic(dcl.con)) { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[keycode], - true); + qkbd_state_key_event(kbd, keycode, true); } else { [self handleMonitorInput: event]; } return true; case NSEventTypeKeyUp: - keycode = cocoa_keycode_to_qemu([event keyCode]); + keycode = cocoa_keycode_to_linux([event keyCode]); // don't pass the guest a spurious key-up if we treated this // command-key combo as a host UI action @@ -XXX,XX +XXX,XX @@ - (bool) handleEventLocked:(NSEvent *)event } if (qemu_console_is_graphic(dcl.con)) { - qkbd_state_key_event(kbd, - qemu_input_map_qcode_to_linux[keycode], - false); + qkbd_state_key_event(kbd, keycode, false); } return true; case NSEventTypeScrollWheel: -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-18-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/dbus-console.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/dbus-console.c b/ui/dbus-console.c index XXXXXXX..XXXXXXX 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -XXX,XX +XXX,XX @@ dbus_kbd_press(DBusDisplayConsole *ddc, GDBusMethodInvocation *invocation, guint arg_keycode) { - QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode); + unsigned int lnx = qemu_input_key_number_to_linux(arg_keycode); trace_dbus_kbd_press(arg_keycode); - qkbd_state_key_event(ddc->kbd, qemu_input_map_qcode_to_linux[qcode], true); + qkbd_state_key_event(ddc->kbd, lnx, true); qemu_dbus_display1_keyboard_complete_press(ddc->iface_kbd, invocation); @@ -XXX,XX +XXX,XX @@ dbus_kbd_release(DBusDisplayConsole *ddc, GDBusMethodInvocation *invocation, guint arg_keycode) { - QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode); + unsigned int lnx = qemu_input_key_number_to_linux(arg_keycode); trace_dbus_kbd_release(arg_keycode); - qkbd_state_key_event(ddc->kbd, qemu_input_map_qcode_to_linux[qcode], false); + qkbd_state_key_event(ddc->kbd, lnx, false); qemu_dbus_display1_keyboard_complete_release(ddc->iface_kbd, invocation); -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-19-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/x_keymap.h | 3 ++- ui/gtk.c | 51 ++++++++++++++++++++++++++++--------------------- ui/x_keymap.c | 24 ++++++++++++----------- ui/trace-events | 2 +- 4 files changed, 45 insertions(+), 35 deletions(-) diff --git a/ui/x_keymap.h b/ui/x_keymap.h index XXXXXXX..XXXXXXX 100644 --- a/ui/x_keymap.h +++ b/ui/x_keymap.h @@ -XXX,XX +XXX,XX @@ #include <X11/Xlib.h> -const guint16 *qemu_xkeymap_mapping_table(Display *dpy, size_t *maplen); +const guint16 *qemu_xkeymap_mapping_table(Display *dpy, size_t *maplen, + bool *evdev); #endif diff --git a/ui/gtk.c b/ui/gtk.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -XXX,XX +XXX,XX @@ #include <math.h> #include "trace.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/input.h" #include "system/runstate.h" #include "system/system.h" @@ -XXX,XX +XXX,XX @@ static const guint16 *keycode_map; static size_t keycode_maplen; +static bool keycode_xorgevdev; struct VCChardev { Chardev parent; @@ -XXX,XX +XXX,XX @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch, return TRUE; } -static const guint16 *gd_get_keymap(size_t *maplen) +static const guint16 *gd_get_keymap(size_t *maplen, bool *xorgevdev) { GdkDisplay *dpy = gdk_display_get_default(); + *maplen = 0; + *xorgevdev = false; + #ifdef GDK_WINDOWING_X11 if (GDK_IS_X11_DISPLAY(dpy)) { trace_gd_keymap_windowing("x11"); return qemu_xkeymap_mapping_table( - gdk_x11_display_get_xdisplay(dpy), maplen); + gdk_x11_display_get_xdisplay(dpy), maplen, xorgevdev); } #endif #ifdef GDK_WINDOWING_WAYLAND if (GDK_IS_WAYLAND_DISPLAY(dpy)) { trace_gd_keymap_windowing("wayland"); - *maplen = qemu_input_map_xorgevdev_to_qcode_len; - return qemu_input_map_xorgevdev_to_qcode; + *xorgevdev = true; + return NULL; } #endif #ifdef GDK_WINDOWING_WIN32 if (GDK_IS_WIN32_DISPLAY(dpy)) { trace_gd_keymap_windowing("win32"); - *maplen = qemu_input_map_atset1_to_qcode_len; - return qemu_input_map_atset1_to_qcode; + *maplen = qemu_input_map_atset1_to_linux_len; + return qemu_input_map_atset1_to_linux; } #endif #ifdef GDK_WINDOWING_QUARTZ if (GDK_IS_QUARTZ_DISPLAY(dpy)) { trace_gd_keymap_windowing("quartz"); - *maplen = qemu_input_map_osx_to_qcode_len; - return qemu_input_map_osx_to_qcode; + *maplen = qemu_input_map_osx_to_linux_len; + return qemu_input_map_osx_to_linux; } #endif @@ -XXX,XX +XXX,XX @@ static const guint16 *gd_get_keymap(size_t *maplen) g_warning("experimental: using broadway, x11 virtual keysym\n" "mapping - with very limited support. See also\n" "https://bugzilla.gnome.org/show_bug.cgi?id=700105"); - *maplen = qemu_input_map_x11_to_qcode_len; - return qemu_input_map_x11_to_qcode; + *maplen = qemu_input_map_x11_to_linux_len; + return qemu_input_map_x11_to_linux; } #endif @@ -XXX,XX +XXX,XX @@ static const guint16 *gd_get_keymap(size_t *maplen) } -static int gd_map_keycode(int scancode) +static unsigned int gd_map_keycode(int scancode) { + if (keycode_xorgevdev) { + return scancode < 8 ? KEY_RESERVED : scancode - 8; + } if (!keycode_map) { return 0; } @@ -XXX,XX +XXX,XX @@ static gboolean gd_text_key_down(GtkWidget *widget, QemuTextConsole *con = QEMU_TEXT_CONSOLE(vc->gfx.dcl.con); if (key->keyval == GDK_KEY_Delete) { - qemu_text_console_put_qcode(con, Q_KEY_CODE_DELETE, false); + qemu_text_console_put_linux(con, KEY_DELETE, false); } else if (key->length) { qemu_text_console_put_string(con, key->string, key->length); } else { - int qcode = gd_map_keycode(gd_get_keycode(key)); - qemu_text_console_put_qcode(con, qcode, false); + unsigned int lnx = gd_map_keycode(gd_get_keycode(key)); + qemu_text_console_put_linux(con, lnx, false); } return TRUE; } @@ -XXX,XX +XXX,XX @@ static gboolean gd_text_key_down(GtkWidget *widget, static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) { VirtualConsole *vc = opaque; - int keycode, qcode; + int keycode; + unsigned int lnx; #ifdef G_OS_WIN32 /* on windows, we ought to ignore the reserved key event? */ @@ -XXX,XX +XXX,XX @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) || key->hardware_keycode == VK_PAUSE #endif ) { - qkbd_state_key_event(vc->gfx.kbd, - qemu_input_map_qcode_to_linux[Q_KEY_CODE_PAUSE], + qkbd_state_key_event(vc->gfx.kbd, KEY_PAUSE, key->type == GDK_KEY_PRESS); return TRUE; } keycode = gd_get_keycode(key); - qcode = gd_map_keycode(keycode); + lnx = gd_map_keycode(keycode); - trace_gd_key_event(vc->label, keycode, qemu_input_map_qcode_to_linux[qcode], + trace_gd_key_event(vc->label, keycode, lnx, (key->type == GDK_KEY_PRESS) ? "down" : "up"); - qkbd_state_key_event(vc->gfx.kbd, qemu_input_map_qcode_to_linux[qcode], - key->type == GDK_KEY_PRESS); + qkbd_state_key_event(vc->gfx.kbd, lnx, key->type == GDK_KEY_PRESS); return TRUE; } @@ -XXX,XX +XXX,XX @@ static void early_gtk_display_init(DisplayOptions *opts) #endif } - keycode_map = gd_get_keymap(&keycode_maplen); + keycode_map = gd_get_keymap(&keycode_maplen, &keycode_xorgevdev); #if defined(CONFIG_VTE) type_register_static(&char_gd_vc_type_info); diff --git a/ui/x_keymap.c b/ui/x_keymap.c index XXXXXXX..XXXXXXX 100644 --- a/ui/x_keymap.c +++ b/ui/x_keymap.c @@ -XXX,XX +XXX,XX @@ static gboolean check_for_xquartz(Display *dpy) return match; } -const guint16 *qemu_xkeymap_mapping_table(Display *dpy, size_t *maplen) +const guint16 *qemu_xkeymap_mapping_table(Display *dpy, size_t *maplen, + bool *evdev) { XkbDescPtr desc; const gchar *keycodes = NULL; - const guint16 *map; + const guint16 *map = NULL; /* There is no easy way to determine what X11 server * and platform & keyboard driver is in use. Thus we @@ -XXX,XX +XXX,XX @@ const guint16 *qemu_xkeymap_mapping_table(Display *dpy, size_t *maplen) XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True); } + *maplen = 0; + *evdev = false; + if (check_for_xwin(dpy)) { trace_xkeymap_keymap("xwin"); - *maplen = qemu_input_map_xorgxwin_to_qcode_len; - map = qemu_input_map_xorgxwin_to_qcode; + *maplen = qemu_input_map_xorgxwin_to_linux_len; + map = qemu_input_map_xorgxwin_to_linux; } else if (check_for_xquartz(dpy)) { trace_xkeymap_keymap("xquartz"); - *maplen = qemu_input_map_xorgxquartz_to_qcode_len; - map = qemu_input_map_xorgxquartz_to_qcode; + *maplen = qemu_input_map_xorgxquartz_to_linux_len; + map = qemu_input_map_xorgxquartz_to_linux; } else if ((keycodes && g_str_has_prefix(keycodes, "evdev")) || (XKeysymToKeycode(dpy, XK_Page_Up) == 0x70)) { trace_xkeymap_keymap("evdev"); - *maplen = qemu_input_map_xorgevdev_to_qcode_len; - map = qemu_input_map_xorgevdev_to_qcode; + *evdev = true; } else if ((keycodes && g_str_has_prefix(keycodes, "xfree86")) || (XKeysymToKeycode(dpy, XK_Page_Up) == 0x63)) { trace_xkeymap_keymap("kbd"); - *maplen = qemu_input_map_xorgkbd_to_qcode_len; - map = qemu_input_map_xorgkbd_to_qcode; + *maplen = qemu_input_map_xorgkbd_to_linux_len; + map = qemu_input_map_xorgkbd_to_linux; } else { trace_xkeymap_keymap("NULL"); g_warning("Unknown X11 keycode mapping '%s'.\n" @@ -XXX,XX +XXX,XX @@ const guint16 *qemu_xkeymap_mapping_table(Display *dpy, size_t *maplen) " - xprop -root\n" " - xdpyinfo\n", keycodes ? keycodes : "<null>"); - map = NULL; } if (keycodes) { XFree((void *)keycodes); diff --git a/ui/trace-events b/ui/trace-events index XXXXXXX..XXXXXXX 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -XXX,XX +XXX,XX @@ ppm_save(int fd, void *image) "fd=%d image=%p" # gtk.c gd_switch(const char *tab, int width, int height) "tab=%s, width=%d, height=%d" gd_update(const char *tab, int x, int y, int w, int h) "tab=%s, x=%d, y=%d, w=%d, h=%d" -gd_key_event(const char *tab, int gdk_keycode, int qkeycode, const char *action) "tab=%s, translated GDK keycode %d to QKeyCode %d (%s)" +gd_key_event(const char *tab, int gdk_keycode, unsigned int lnx, const char *action) "tab=%s, translated GDK keycode %d to Linux %u (%s)" gd_grab(const char *tab, const char *device, const char *reason) "tab=%s, dev=%s, reason=%s" gd_ungrab(const char *tab, const char *device) "tab=%s, dev=%s" gd_keymap_windowing(const char *name) "backend=%s" -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-20-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/input-barrier.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ui/input-barrier.c b/ui/input-barrier.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -XXX,XX +XXX,XX @@ static const char *cmd_names[] = { static kbd_layout_t *kbd_layout; -static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode) +static unsigned int input_barrier_to_linux(uint16_t keyid, uint16_t keycode) { /* keycode is optional, if it is not provided use keyid */ - if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) { - return qemu_input_map_xorgkbd_to_qcode[keycode]; + if (keycode && keycode <= qemu_input_map_xorgkbd_to_linux_len) { + return qemu_input_map_xorgkbd_to_linux[keycode]; } if (keyid >= 0xE000 && keyid <= 0xEFFF) { @@ -XXX,XX +XXX,XX @@ static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode) if (kbd_layout) { keycode = keysym2scancode(kbd_layout, keyid, NULL, false); - return qemu_input_key_number_to_qcode(keycode); + return qemu_input_key_number_to_linux(keycode); } - return qemu_input_map_x11_to_qcode[keyid]; + return qemu_input_map_x11_to_linux[keyid]; } static int input_barrier_to_mouse(uint8_t buttonid) @@ -XXX,XX +XXX,XX @@ static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg) /* keyboard */ case barrierCmdDKeyDown: - qemu_input_event_send_key_qcode(NULL, - input_barrier_to_qcode(msg->key.keyid, msg->key.button), + qemu_input_event_send_key_linux(NULL, + input_barrier_to_linux(msg->key.keyid, msg->key.button), true); break; case barrierCmdDKeyRepeat: for (i = 0; i < msg->repeat.repeat; i++) { - qemu_input_event_send_key_qcode(NULL, - input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button), + qemu_input_event_send_key_linux(NULL, + input_barrier_to_linux(msg->repeat.keyid, msg->repeat.button), false); - qemu_input_event_send_key_qcode(NULL, - input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button), + qemu_input_event_send_key_linux(NULL, + input_barrier_to_linux(msg->repeat.keyid, msg->repeat.button), true); } break; case barrierCmdDKeyUp: - qemu_input_event_send_key_qcode(NULL, - input_barrier_to_qcode(msg->key.keyid, msg->key.button), + qemu_input_event_send_key_linux(NULL, + input_barrier_to_linux(msg->key.keyid, msg->key.button), false); break; default: -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-21-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/input-legacy.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/ui/input-legacy.c b/ui/input-legacy.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -XXX,XX +XXX,XX @@ int index_from_key(const char *key, size_t key_length) return i; } -static KeyValue *copy_key_value(KeyValue *src) -{ - KeyValue *dst = g_new(KeyValue, 1); - memcpy(dst, src, sizeof(*src)); - if (dst->type == KEY_VALUE_KIND_NUMBER) { - QKeyCode code = qemu_input_key_number_to_qcode(dst->u.number.data); - dst->type = KEY_VALUE_KIND_QCODE; - dst->u.qcode.data = code; - } - return dst; -} - void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, Error **errp) { KeyValueList *p; - KeyValue **up = NULL; + unsigned int *up = NULL; int count = 0; if (!has_hold_time) { @@ -XXX,XX +XXX,XX @@ void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, } for (p = keys; p != NULL; p = p->next) { - qemu_input_event_send_key(NULL, copy_key_value(p->value), true); - qemu_input_event_send_key_delay(hold_time); up = g_realloc(up, sizeof(*up) * (count+1)); - up[count] = copy_key_value(p->value); + up[count] = qemu_input_key_value_to_linux(p->value); + qemu_input_event_send_key_linux(NULL, up[count], true); + qemu_input_event_send_key_delay(hold_time); count++; } while (count) { count--; - qemu_input_event_send_key(NULL, up[count], false); + qemu_input_event_send_key_linux(NULL, up[count], false); qemu_input_event_send_key_delay(hold_time); } g_free(up); -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-22-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/input-linux.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/input-linux.c b/ui/input-linux.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -XXX,XX +XXX,XX @@ static void input_linux_handle_keyboard(InputLinux *il, /* send event to guest when grab is active */ if (il->grab_active && !input_linux_should_skip(il, event)) { - int qcode = qemu_input_linux_to_qcode(event->code); - qemu_input_event_send_key_qcode(NULL, qcode, event->value); + qemu_input_event_send_key_linux(NULL, event->code, event->value); } /* hotkey -> record switch request ... */ -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-23-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/keymaps.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/keymaps.c b/ui/keymaps.c index XXXXXXX..XXXXXXX 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -XXX,XX +XXX,XX @@ int keysym2scancode(kbd_layout_t *k, int keysym, * On keyup: Try find a key which is actually down. */ for (i = 0; i < keysym2code->count; i++) { - QKeyCode qcode = qemu_input_key_number_to_qcode + unsigned int lnx = qemu_input_key_number_to_linux (keysym2code->keycodes[i]); - unsigned int lnx = qemu_input_map_qcode_to_linux[qcode]; if (kbd && qkbd_state_key_get(kbd, lnx)) { return keysym2code->keycodes[i]; } -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-24-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/sdl2-input.c | 18 +++++++++--------- ui/trace-events | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -XXX,XX +XXX,XX @@ /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ #include "qemu/osdep.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/console.h" #include "ui/input.h" #include "ui/sdl2.h" @@ -XXX,XX +XXX,XX @@ void sdl2_process_key(struct sdl2_console *scon, SDL_KeyboardEvent *ev) { - int qcode; + unsigned int lnx; QemuConsole *con = scon->dcl.con; - if (ev->keysym.scancode >= qemu_input_map_usb_to_qcode_len) { + if (ev->keysym.scancode >= qemu_input_map_usb_to_linux_len) { return; } - qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode]; - trace_sdl2_process_key(ev->keysym.scancode, qcode, + lnx = qemu_input_map_usb_to_linux[ev->keysym.scancode]; + trace_sdl2_process_key(ev->keysym.scancode, lnx, ev->type == SDL_KEYDOWN ? "down" : "up"); - qkbd_state_key_event(scon->kbd, qemu_input_map_qcode_to_linux[qcode], - ev->type == SDL_KEYDOWN); + qkbd_state_key_event(scon->kbd, lnx, ev->type == SDL_KEYDOWN); if (QEMU_IS_TEXT_CONSOLE(con)) { QemuTextConsole *s = QEMU_TEXT_CONSOLE(con); bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL); if (ev->type == SDL_KEYDOWN) { - switch (qcode) { - case Q_KEY_CODE_RET: + switch (lnx) { + case KEY_ENTER: qemu_text_console_put_keysym(s, '\n'); break; default: - qemu_text_console_put_qcode(s, qcode, ctrl); + qemu_text_console_put_linux(s, lnx, ctrl); break; } } diff --git a/ui/trace-events b/ui/trace-events index XXXXXXX..XXXXXXX 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -XXX,XX +XXX,XX @@ input_event_mtt(int conidx, const char *axis, int value) "con %d, axis %s, value input_event_sync(void) "" # sdl2-input.c -sdl2_process_key(int sdl_scancode, int qcode, const char *action) "translated SDL scancode %d to QKeyCode %d (%s)" +sdl2_process_key(int sdl_scancode, unsigned int lnx, const char *action) "translated SDL scancode %d to Linux scancode %u (%s)" # spice-display.c qemu_spice_add_memslot(int qid, uint32_t slot_id, unsigned long virt_start, unsigned long virt_end, int async) "%d %u: host virt 0x%lx - 0x%lx async=%d" -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-25-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/spice-input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/spice-input.c b/ui/spice-input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -XXX,XX +XXX,XX @@ #include <spice.h> #include <spice/enums.h> +#include "standard-headers/linux/input-event-codes.h" #include "ui/qemu-spice.h" #include "ui/console.h" #include "keymaps.h" @@ -XXX,XX +XXX,XX @@ static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode) if (scancode == pauseseq[kbd->pauseseq]) { kbd->pauseseq++; if (kbd->pauseseq == G_N_ELEMENTS(pauseseq)) { - qemu_input_event_send_key_qcode(NULL, Q_KEY_CODE_PAUSE, true); + qemu_input_event_send_key_linux(NULL, KEY_PAUSE, true); kbd->pauseseq = 0; } return; -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-26-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- ui/vnc.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index XXXXXXX..XXXXXXX 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -XXX,XX +XXX,XX @@ #include "qapi/qapi-events-ui.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/console.h" #include "ui/input.h" #include "crypto/hash.h" @@ -XXX,XX +XXX,XX @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) qemu_input_event_sync(); } -static void press_key(VncState *vs, QKeyCode qcode) +static void press_key(VncState *vs, unsigned int lnx) { - qkbd_state_key_event(vs->vd->kbd, qemu_input_map_qcode_to_linux[qcode], - true); - - qkbd_state_key_event(vs->vd->kbd, qemu_input_map_qcode_to_linux[qcode], - false); + qkbd_state_key_event(vs->vd->kbd, lnx, true); + qkbd_state_key_event(vs->vd->kbd, lnx, false); } static void vnc_led_state_change(VncState *vs) @@ -XXX,XX +XXX,XX @@ static void kbd_leds(void *opaque, int ledstate) static void do_key_event(VncState *vs, int down, int keycode, int sym) { - QKeyCode qcode = qemu_input_key_number_to_qcode(keycode); + unsigned int lnx = qemu_input_key_number_to_linux(keycode); /* QEMU console switch */ - switch (qcode) { - case Q_KEY_CODE_1 ... Q_KEY_CODE_9: /* '1' to '9' keys */ + switch (lnx) { + case KEY_1 ... KEY_9: /* '1' to '9' keys */ if (down && qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL) && qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) { - QemuConsole *con = qemu_console_lookup_by_index(qcode - Q_KEY_CODE_1); + QemuConsole *con = qemu_console_lookup_by_index(lnx - KEY_1); if (con) { qemu_console_unregister_listener(&vs->vd->dcl); qkbd_state_switch_console(vs->vd->kbd, con); @@ -XXX,XX +XXX,XX @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) { if (!qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) { trace_vnc_key_sync_numlock(true); - press_key(vs, Q_KEY_CODE_NUM_LOCK); + press_key(vs, KEY_NUMLOCK); } } else { if (qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) { trace_vnc_key_sync_numlock(false); - press_key(vs, Q_KEY_CODE_NUM_LOCK); + press_key(vs, KEY_NUMLOCK); } } } @@ -XXX,XX +XXX,XX @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) if (capslock) { if (uppercase == shift) { trace_vnc_key_sync_capslock(false); - press_key(vs, Q_KEY_CODE_CAPS_LOCK); + press_key(vs, KEY_CAPSLOCK); } } else { if (uppercase != shift) { trace_vnc_key_sync_capslock(true); - press_key(vs, Q_KEY_CODE_CAPS_LOCK); + press_key(vs, KEY_CAPSLOCK); } } } - qkbd_state_key_event(vs->vd->kbd, qemu_input_map_qcode_to_linux[qcode], - down); + qkbd_state_key_event(vs->vd->kbd, lnx, down); if (QEMU_IS_TEXT_CONSOLE(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); -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> QemuInputEvent now stores Linux key codes for key events. Use those codes directly instead of translating between internal key code representations. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-27-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- qemu-keymap.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/qemu-keymap.c b/qemu-keymap.c index XXXXXXX..XXXXXXX 100644 --- a/qemu-keymap.c +++ b/qemu-keymap.c @@ -XXX,XX +XXX,XX @@ */ #include "qemu/osdep.h" #include "qemu/notify.h" +#include "standard-headers/linux/input-event-codes.h" #include "ui/input.h" #include <xkbcommon/xkbcommon.h> @@ -XXX,XX +XXX,XX @@ static FILE *outfile; /* ------------------------------------------------------------------------ */ -static uint32_t qcode_to_number(uint32_t qcode) +static uint32_t linux_to_number(uint32_t lnx) { - KeyValue keyvalue; uint32_t number; - keyvalue.type = KEY_VALUE_KIND_QCODE; - keyvalue.u.qcode.data = qcode; - number = qemu_input_key_value_to_number(&keyvalue); + assert(lnx < qemu_input_map_linux_to_qnum_len); + number = qemu_input_map_linux_to_qnum[lnx]; assert(number != 0); return number; } -static void print_sym(xkb_keysym_t sym, uint32_t qcode, const char *mod) +static void print_sym(xkb_keysym_t sym, uint32_t lnx, const char *mod) { char name[64]; @@ -XXX,XX +XXX,XX @@ static void print_sym(xkb_keysym_t sym, uint32_t qcode, const char *mod) xkb_keysym_get_name(sym, name, sizeof(name)); /* TODO: make ui/keymap.c parser accept QKeyCode names */ - fprintf(outfile, "%s 0x%02x%s\n", name, qcode_to_number(qcode), mod); + fprintf(outfile, "%s 0x%02x%s\n", name, linux_to_number(lnx), mod); } static void walk_map(struct xkb_keymap *map, xkb_keycode_t code, void *data) @@ -XXX,XX +XXX,XX @@ static void walk_map(struct xkb_keymap *map, xkb_keycode_t code, void *data) fprintf(outfile, "# evdev %d (0x%x), QKeyCode \"%s\", number 0x%x\n", evdev, evdev, QKeyCode_str(qcode), - qcode_to_number(qcode)); + linux_to_number(evdev)); /* * check which modifier states generate which keysyms */ xkb_state_update_mask(state, 0, 0, 0, 0, 0, 0); kbase = xkb_state_key_get_one_sym(state, code); - print_sym(kbase, qcode, ""); + print_sym(kbase, evdev, ""); xkb_state_update_mask(state, 0, 0, numlock, 0, 0, 0); knumlock = xkb_state_key_get_one_sym(state, code); if (kbase != knumlock) { - print_sym(knumlock, qcode, " numlock"); + print_sym(knumlock, evdev, " numlock"); } xkb_state_update_mask(state, shift, 0, 0, 0, 0, 0); kshift = xkb_state_key_get_one_sym(state, code); if (kbase != kshift && knumlock != kshift) { - print_sym(kshift, qcode, " shift"); + print_sym(kshift, evdev, " shift"); } xkb_state_update_mask(state, altgr, 0, 0, 0, 0, 0); kaltgr = xkb_state_key_get_one_sym(state, code); if (kbase != kaltgr) { - print_sym(kaltgr, qcode, " altgr"); + print_sym(kaltgr, evdev, " altgr"); } xkb_state_update_mask(state, altgr | shift, 0, 0, 0, 0, 0); kaltgrshift = xkb_state_key_get_one_sym(state, code); if (kshift != kaltgrshift && kaltgr != kaltgrshift) { - print_sym(kaltgrshift, qcode, " shift altgr"); + print_sym(kaltgrshift, evdev, " shift altgr"); } } @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) "# keysyms. So append them here.\n" "#\n" "\n"); - print_sym(XKB_KEY_Print, Q_KEY_CODE_SYSRQ, ""); - print_sym(XKB_KEY_Sys_Req, Q_KEY_CODE_SYSRQ, ""); - print_sym(XKB_KEY_Execute, Q_KEY_CODE_SYSRQ, ""); + print_sym(XKB_KEY_Print, KEY_SYSRQ, ""); + print_sym(XKB_KEY_Sys_Req, KEY_SYSRQ, ""); + print_sym(XKB_KEY_Execute, KEY_SYSRQ, ""); - print_sym(XKB_KEY_KP_Decimal, Q_KEY_CODE_KP_DECIMAL, " numlock"); - print_sym(XKB_KEY_KP_Separator, Q_KEY_CODE_KP_DECIMAL, " numlock"); + print_sym(XKB_KEY_KP_Decimal, KEY_KPDOT, " numlock"); + print_sym(XKB_KEY_KP_Separator, KEY_KPDOT, " numlock"); - print_sym(XKB_KEY_Alt_R, Q_KEY_CODE_ALT_R, ""); - print_sym(XKB_KEY_ISO_Level3_Shift, Q_KEY_CODE_ALT_R, ""); - print_sym(XKB_KEY_Mode_switch, Q_KEY_CODE_ALT_R, ""); + print_sym(XKB_KEY_Alt_R, KEY_RIGHTALT, ""); + print_sym(XKB_KEY_ISO_Level3_Shift, KEY_RIGHTALT, ""); + print_sym(XKB_KEY_Mode_switch, KEY_RIGHTALT, ""); fprintf(outfile, "\n" -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> It is no longer used. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-28-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- include/ui/console.h | 1 - ui/console.c | 6 ------ 2 files changed, 7 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -XXX,XX +XXX,XX @@ bool qemu_mouse_set(int index, Error **errp); #define QEMU_KEY_CTRL_PAGEDOWN 0xe407 void qemu_text_console_put_keysym(QemuTextConsole *s, int keysym); -bool qemu_text_console_put_qcode(QemuTextConsole *s, int qcode, bool ctrl); bool qemu_text_console_put_linux(QemuTextConsole *s, unsigned int lnx, bool ctrl); void qemu_text_console_put_string(QemuTextConsole *s, const char *str, int len); diff --git a/ui/console.c b/ui/console.c index XXXXXXX..XXXXXXX 100644 --- a/ui/console.c +++ b/ui/console.c @@ -XXX,XX +XXX,XX @@ static const int ctrl_linux_to_keysym[] = { [KEY_PAGEDOWN] = QEMU_KEY_CTRL_PAGEDOWN, }; -bool qemu_text_console_put_qcode(QemuTextConsole *s, int qcode, bool ctrl) -{ - unsigned int lnx = qemu_input_map_qcode_to_linux[qcode]; - return qemu_text_console_put_linux(s, lnx, ctrl); -} - bool qemu_text_console_put_linux(QemuTextConsole *s, unsigned int lnx, bool ctrl) { -- 2.54.0
From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Their users have migrated to Linux key codes. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260520-input-v3-29-7c9e4c7abe34@rsg.ci.i.u-tokyo.ac.jp> --- include/ui/input.h | 50 ------------------------------------------ tools/qemu-vnc/input.c | 6 ----- ui/input-keymap.c | 39 -------------------------------- ui/input.c | 16 -------------- ui/meson.build | 15 ------------- 5 files changed, 126 deletions(-) diff --git a/include/ui/input.h b/include/ui/input.h index XXXXXXX..XXXXXXX 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_impl(QemuConsole *src, QemuInputEvent *evt); void qemu_input_event_sync(void); void qemu_input_event_sync_impl(void); -void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); void qemu_input_event_send_key_linux(QemuConsole *src, unsigned int lnx, bool down); void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down); -void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down); void qemu_input_event_send_key_delay(uint32_t delay_ms); int qemu_input_key_number_to_qcode(unsigned int nr); unsigned int qemu_input_key_number_to_linux(unsigned int nr); -int qemu_input_key_value_to_number(const KeyValue *value); -int qemu_input_key_value_to_qcode(const KeyValue *value); unsigned int qemu_input_key_value_to_linux(const KeyValue *value); -int qemu_input_qcode_to_scancode(QKeyCode qcode, bool down, int *codes); int qemu_input_linux_to_scancode(unsigned int lnx, bool down, int *codes); int qemu_input_linux_to_qcode(unsigned int lnx); @@ -XXX,XX +XXX,XX @@ 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); -extern const guint qemu_input_map_atset1_to_qcode_len; -extern const guint16 qemu_input_map_atset1_to_qcode[]; - extern const guint qemu_input_map_atset1_to_linux_len; extern const guint16 qemu_input_map_atset1_to_linux[]; extern const guint qemu_input_map_linux_to_qcode_len; extern const guint16 qemu_input_map_linux_to_qcode[]; -extern const guint qemu_input_map_qcode_to_atset1_len; -extern const guint16 qemu_input_map_qcode_to_atset1[]; - extern const guint qemu_input_map_linux_to_atset1_len; extern const guint16 qemu_input_map_linux_to_atset1[]; -extern const guint qemu_input_map_qcode_to_atset2_len; -extern const guint16 qemu_input_map_qcode_to_atset2[]; - extern const guint qemu_input_map_linux_to_atset2_len; extern const guint16 qemu_input_map_linux_to_atset2[]; -extern const guint qemu_input_map_qcode_to_atset3_len; -extern const guint16 qemu_input_map_qcode_to_atset3[]; - extern const guint qemu_input_map_linux_to_atset3_len; extern const guint16 qemu_input_map_linux_to_atset3[]; extern const guint qemu_input_map_qcode_to_linux_len; extern const guint16 qemu_input_map_qcode_to_linux[]; -extern const guint qemu_input_map_qcode_to_qnum_len; -extern const guint16 qemu_input_map_qcode_to_qnum[]; - extern const guint qemu_input_map_linux_to_qnum_len; extern const guint16 qemu_input_map_linux_to_qnum[]; -extern const guint qemu_input_map_qcode_to_sun_len; -extern const guint16 qemu_input_map_qcode_to_sun[]; - extern const guint qemu_input_map_linux_to_sun_len; extern const guint16 qemu_input_map_linux_to_sun[]; -extern const guint qemu_input_map_qnum_to_qcode_len; -extern const guint16 qemu_input_map_qnum_to_qcode[]; - extern const guint qemu_input_map_qnum_to_linux_len; extern const guint16 qemu_input_map_qnum_to_linux[]; -extern const guint qemu_input_map_usb_to_qcode_len; -extern const guint16 qemu_input_map_usb_to_qcode[]; - extern const guint qemu_input_map_usb_to_linux_len; extern const guint16 qemu_input_map_usb_to_linux[]; -extern const guint qemu_input_map_win32_to_qcode_len; -extern const guint16 qemu_input_map_win32_to_qcode[]; - extern const guint qemu_input_map_win32_to_linux_len; extern const guint16 qemu_input_map_win32_to_linux[]; -extern const guint qemu_input_map_x11_to_qcode_len; -extern const guint16 qemu_input_map_x11_to_qcode[]; - extern const guint qemu_input_map_x11_to_linux_len; extern const guint16 qemu_input_map_x11_to_linux[]; -extern const guint qemu_input_map_xorgevdev_to_qcode_len; -extern const guint16 qemu_input_map_xorgevdev_to_qcode[]; - -extern const guint qemu_input_map_xorgkbd_to_qcode_len; -extern const guint16 qemu_input_map_xorgkbd_to_qcode[]; - extern const guint qemu_input_map_xorgkbd_to_linux_len; extern const guint16 qemu_input_map_xorgkbd_to_linux[]; -extern const guint qemu_input_map_xorgxquartz_to_qcode_len; -extern const guint16 qemu_input_map_xorgxquartz_to_qcode[]; - extern const guint qemu_input_map_xorgxquartz_to_linux_len; extern const guint16 qemu_input_map_xorgxquartz_to_linux[]; -extern const guint qemu_input_map_xorgxwin_to_qcode_len; -extern const guint16 qemu_input_map_xorgxwin_to_qcode[]; - extern const guint qemu_input_map_xorgxwin_to_linux_len; extern const guint16 qemu_input_map_xorgxwin_to_linux[]; -extern const guint qemu_input_map_osx_to_qcode_len; -extern const guint16 qemu_input_map_osx_to_qcode[]; - extern const guint qemu_input_map_osx_to_linux_len; extern const guint16 qemu_input_map_osx_to_linux[]; diff --git a/tools/qemu-vnc/input.c b/tools/qemu-vnc/input.c index XXXXXXX..XXXXXXX 100644 --- a/tools/qemu-vnc/input.c +++ b/tools/qemu-vnc/input.c @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_key_delay(uint32_t delay_ms) { } -void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) -{ - unsigned int lnx = qemu_input_map_qcode_to_linux[q]; - qemu_input_event_send_key_linux(src, lnx, down); -} - void qemu_input_event_send_key_linux(QemuConsole *src, unsigned int lnx, bool down) { diff --git a/ui/input-keymap.c b/ui/input-keymap.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -XXX,XX +XXX,XX @@ #include "standard-headers/linux/input.h" -#include "ui/input-keymap-atset1-to-qcode.c.inc" #include "ui/input-keymap-atset1-to-linux.c.inc" #include "ui/input-keymap-linux-to-qcode.c.inc" -#include "ui/input-keymap-qcode-to-atset1.c.inc" #include "ui/input-keymap-linux-to-atset1.c.inc" -#include "ui/input-keymap-qcode-to-atset2.c.inc" #include "ui/input-keymap-linux-to-atset2.c.inc" -#include "ui/input-keymap-qcode-to-atset3.c.inc" #include "ui/input-keymap-linux-to-atset3.c.inc" #include "ui/input-keymap-qcode-to-linux.c.inc" -#include "ui/input-keymap-qcode-to-qnum.c.inc" #include "ui/input-keymap-linux-to-qnum.c.inc" -#include "ui/input-keymap-qcode-to-sun.c.inc" #include "ui/input-keymap-linux-to-sun.c.inc" -#include "ui/input-keymap-qnum-to-qcode.c.inc" #include "ui/input-keymap-qnum-to-linux.c.inc" -#include "ui/input-keymap-usb-to-qcode.c.inc" #include "ui/input-keymap-usb-to-linux.c.inc" -#include "ui/input-keymap-win32-to-qcode.c.inc" #include "ui/input-keymap-win32-to-linux.c.inc" -#include "ui/input-keymap-x11-to-qcode.c.inc" #include "ui/input-keymap-x11-to-linux.c.inc" -#include "ui/input-keymap-xorgevdev-to-qcode.c.inc" -#include "ui/input-keymap-xorgkbd-to-qcode.c.inc" #include "ui/input-keymap-xorgkbd-to-linux.c.inc" -#include "ui/input-keymap-xorgxquartz-to-qcode.c.inc" #include "ui/input-keymap-xorgxquartz-to-linux.c.inc" -#include "ui/input-keymap-xorgxwin-to-qcode.c.inc" #include "ui/input-keymap-xorgxwin-to-linux.c.inc" -#include "ui/input-keymap-osx-to-qcode.c.inc" #include "ui/input-keymap-osx-to-linux.c.inc" int qemu_input_linux_to_qcode(unsigned int lnx) @@ -XXX,XX +XXX,XX @@ int qemu_input_linux_to_qcode(unsigned int lnx) return qemu_input_map_linux_to_qcode[lnx]; } -int qemu_input_key_value_to_number(const KeyValue *value) -{ - if (value->type == KEY_VALUE_KIND_QCODE) { - if (value->u.qcode.data >= qemu_input_map_qcode_to_qnum_len) { - return 0; - } - return qemu_input_map_qcode_to_qnum[value->u.qcode.data]; - } else { - assert(value->type == KEY_VALUE_KIND_NUMBER); - return value->u.number.data; - } -} - int qemu_input_key_number_to_qcode(unsigned int nr) { return qemu_input_linux_to_qcode(qemu_input_key_number_to_linux(nr)); @@ -XXX,XX +XXX,XX @@ unsigned int qemu_input_key_number_to_linux(unsigned int nr) return qemu_input_map_qnum_to_linux[nr]; } -int qemu_input_key_value_to_qcode(const KeyValue *value) -{ - return qemu_input_linux_to_qcode(qemu_input_key_value_to_linux(value)); -} - unsigned int qemu_input_key_value_to_linux(const KeyValue *value) { switch (value->type) { @@ -XXX,XX +XXX,XX @@ unsigned int qemu_input_key_value_to_linux(const KeyValue *value) } } -int qemu_input_qcode_to_scancode(QKeyCode qcode, bool down, int *codes) -{ - return qemu_input_linux_to_scancode(qemu_input_map_qcode_to_linux[qcode], - down, codes); -} - int qemu_input_linux_to_scancode(unsigned int lnx, bool down, int *codes) { int keycode = lnx < qemu_input_map_linux_to_qnum_len ? diff --git a/ui/input.c b/ui/input.c index XXXXXXX..XXXXXXX 100644 --- a/ui/input.c +++ b/ui/input.c @@ -XXX,XX +XXX,XX @@ void qemu_input_event_sync(void) replay_input_sync_event(); } -void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) -{ - unsigned int lnx = qemu_input_key_value_to_linux(key); - - g_free(key); - qemu_input_event_send_key_linux(src, lnx, down); -} - void qemu_input_event_send_key_linux(QemuConsole *src, unsigned int lnx, bool down) { @@ -XXX,XX +XXX,XX @@ void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) qemu_input_event_send_key_linux(src, lnx, down); } -void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) -{ - KeyValue *key = g_new0(KeyValue, 1); - key->type = KEY_VALUE_KIND_QCODE; - key->u.qcode.data = q; - qemu_input_event_send_key(src, key, down); -} - void qemu_input_event_send_key_delay(uint32_t delay_ms) { if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { diff --git a/ui/meson.build b/ui/meson.build index XXXXXXX..XXXXXXX 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -XXX,XX +XXX,XX @@ keymaps = [ - ['atset1', 'qcode'], ['atset1', 'linux'], ['linux', 'qcode'], - ['qcode', 'atset1'], ['linux', 'atset1'], - ['qcode', 'atset2'], ['linux', 'atset2'], - ['qcode', 'atset3'], ['linux', 'atset3'], ['qcode', 'linux'], - ['qcode', 'qnum'], ['linux', 'qnum'], - ['qcode', 'sun'], ['linux', 'sun'], - ['qnum', 'qcode'], ['qnum', 'linux'], - ['usb', 'qcode'], ['usb', 'linux'], - ['win32', 'qcode'], ['win32', 'linux'], - ['x11', 'qcode'], ['x11', 'linux'], - ['xorgevdev', 'qcode'], - ['xorgkbd', 'qcode'], ['xorgkbd', 'linux'], - ['xorgxquartz', 'qcode'], ['xorgxquartz', 'linux'], - ['xorgxwin', 'qcode'], ['xorgxwin', 'linux'], - ['osx', 'qcode'], ['osx', 'linux'], ] -- 2.54.0
From: Dongwon Kim <dongwon.kim@intel.com> When QEMU is launched with a single virtual console (e.g., using --nodefaults), detaching and then closing the detached window leaves the main window's notebook without an active focus target which makes keyboard unfunctional on re-attached VC. Fix this by explicitly calling gtk_widget_grab_focus on the active VC's focus widget during the window close handler. Cc: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Cc: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Dongwon Kim <dongwon.kim@intel.com> Message-ID: <20260520002645.1910740-1-dongwon.kim@intel.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- ui/gtk.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -XXX,XX +XXX,XX @@ static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, vc->gfx.ectx = NULL; } #endif + + if (vc == gd_vc_find_by_menu(s)) { + gtk_widget_grab_focus(vc->focus); + } + return TRUE; } -- 2.54.0