From 90da7d552fbcb19d1fbf68b2051f0f168b8a48f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:35 +0400 Subject: [PATCH 01/52] ui: remove qemu_pixman_color() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usage removed in commit e27bd65a72d ("console: switch color_table_rgb to pixman_color_t") Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230830093843.3531473-2-marcandre.lureau@redhat.com> --- include/ui/qemu-pixman.h | 1 - ui/qemu-pixman.c | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 0c775604d1..fd78d17124 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -78,7 +78,6 @@ pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format, pixman_image_t *image); void qemu_pixman_image_unref(pixman_image_t *image); -pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color); pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font, unsigned int ch); void qemu_pixman_glyph_render(pixman_image_t *glyph, diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index e4f024a85e..c5053cd326 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -226,17 +226,6 @@ void qemu_pixman_image_unref(pixman_image_t *image) pixman_image_unref(image); } -pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color) -{ - pixman_color_t c; - - c.red = ((color & pf->rmask) >> pf->rshift) << (16 - pf->rbits); - c.green = ((color & pf->gmask) >> pf->gshift) << (16 - pf->gbits); - c.blue = ((color & pf->bmask) >> pf->bshift) << (16 - pf->bbits); - c.alpha = ((color & pf->amask) >> pf->ashift) << (16 - pf->abits); - return c; -} - pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font, unsigned int ch) { From 426749a7b79cf735dcd9bd4d134af5224fcf8210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:36 +0400 Subject: [PATCH 02/52] ui: remove qemu_pixman_linebuf_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 43c7d8bd449 ("console: add qemu_pixman_linebuf_copy"), it seems it was never used. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-3-marcandre.lureau@redhat.com> --- include/ui/qemu-pixman.h | 2 -- ui/qemu-pixman.c | 8 -------- 2 files changed, 10 deletions(-) diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index fd78d17124..ce4518e4de 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -72,8 +72,6 @@ pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format, int width); void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb, int width, int x, int y); -void qemu_pixman_linebuf_copy(pixman_image_t *fb, int width, int x, int y, - pixman_image_t *linebuf); pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format, pixman_image_t *image); void qemu_pixman_image_unref(pixman_image_t *image); diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index c5053cd326..be00a96340 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -200,14 +200,6 @@ void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb, x, y, 0, 0, 0, 0, width, 1); } -/* copy linebuf to framebuffer */ -void qemu_pixman_linebuf_copy(pixman_image_t *fb, int width, int x, int y, - pixman_image_t *linebuf) -{ - pixman_image_composite(PIXMAN_OP_SRC, linebuf, NULL, fb, - 0, 0, 0, 0, x, y, width, 1); -} - pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format, pixman_image_t *image) { From 4f2c765ba6b648f406b7d64ebbf0e4eaedf3d8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:37 +0400 Subject: [PATCH 03/52] ui/qmp: move screendump to ui-qmp-cmds.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit console.c unit is over-crowded. This code is specific to the handling of the QMP screendump command, so move it in ui-qmp-cmds. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-4-marcandre.lureau@redhat.com> --- include/ui/console.h | 1 + ui/console.c | 212 ++++--------------------------------------- ui/ui-qmp-cmds.c | 187 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+), 195 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index 3e8b22d6c6..1b08b0f8ad 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -504,6 +504,7 @@ void qemu_console_set_window_id(QemuConsole *con, int window_id); void console_select(unsigned int index); void qemu_console_resize(QemuConsole *con, int width, int height); DisplaySurface *qemu_console_surface(QemuConsole *con); +void coroutine_fn qemu_console_co_wait_update(QemuConsole *con); /* console-gl.c */ #ifdef CONFIG_OPENGL diff --git a/ui/console.c b/ui/console.c index 8da2170a7e..9c17024dbc 100644 --- a/ui/console.c +++ b/ui/console.c @@ -28,8 +28,8 @@ #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" #include "qemu/coroutine.h" -#include "qemu/error-report.h" #include "qemu/fifo8.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" @@ -37,11 +37,7 @@ #include "chardev/char.h" #include "trace.h" #include "exec/memory.h" -#include "io/channel-file.h" #include "qom/object.h" -#ifdef CONFIG_PNG -#include -#endif #define DEFAULT_BACKSCROLL 512 #define CONSOLE_CURSOR_PERIOD 500 @@ -239,6 +235,22 @@ void graphic_hw_update(QemuConsole *con) } } +static void graphic_hw_update_bh(void *con) +{ + graphic_hw_update(con); +} + +void qemu_console_co_wait_update(QemuConsole *con) +{ + if (qemu_co_queue_empty(&con->dump_queue)) { + /* Defer the update, it will restart the pending coroutines */ + aio_bh_schedule_oneshot(qemu_get_aio_context(), + graphic_hw_update_bh, con); + } + qemu_co_queue_wait(&con->dump_queue, NULL); + +} + static void graphic_hw_gl_unblock_timer(void *opaque) { warn_report("console: no gl-unblock within one second"); @@ -292,196 +304,6 @@ void graphic_hw_invalidate(QemuConsole *con) } } -#ifdef CONFIG_PNG -/** - * png_save: Take a screenshot as PNG - * - * Saves screendump as a PNG file - * - * Returns true for success or false for error. - * - * @fd: File descriptor for PNG file. - * @image: Image data in pixman format. - * @errp: Pointer to an error. - */ -static bool png_save(int fd, pixman_image_t *image, Error **errp) -{ - int width = pixman_image_get_width(image); - int height = pixman_image_get_height(image); - png_struct *png_ptr; - png_info *info_ptr; - g_autoptr(pixman_image_t) linebuf = - qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); - uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); - FILE *f = fdopen(fd, "wb"); - int y; - if (!f) { - error_setg_errno(errp, errno, - "Failed to create file from file descriptor"); - return false; - } - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, - NULL, NULL); - if (!png_ptr) { - error_setg(errp, "PNG creation failed. Unable to write struct"); - fclose(f); - return false; - } - - info_ptr = png_create_info_struct(png_ptr); - - if (!info_ptr) { - error_setg(errp, "PNG creation failed. Unable to write info"); - fclose(f); - png_destroy_write_struct(&png_ptr, &info_ptr); - return false; - } - - png_init_io(png_ptr, f); - - png_set_IHDR(png_ptr, info_ptr, width, height, 8, - PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_write_info(png_ptr, info_ptr); - - for (y = 0; y < height; ++y) { - qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); - png_write_row(png_ptr, buf); - } - - png_write_end(png_ptr, NULL); - - png_destroy_write_struct(&png_ptr, &info_ptr); - - if (fclose(f) != 0) { - error_setg_errno(errp, errno, - "PNG creation failed. Unable to close file"); - return false; - } - - return true; -} - -#else /* no png support */ - -static bool png_save(int fd, pixman_image_t *image, Error **errp) -{ - error_setg(errp, "Enable PNG support with libpng for screendump"); - return false; -} - -#endif /* CONFIG_PNG */ - -static bool ppm_save(int fd, pixman_image_t *image, Error **errp) -{ - int width = pixman_image_get_width(image); - int height = pixman_image_get_height(image); - g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd)); - g_autofree char *header = NULL; - g_autoptr(pixman_image_t) linebuf = NULL; - int y; - - trace_ppm_save(fd, image); - - header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255); - if (qio_channel_write_all(QIO_CHANNEL(ioc), - header, strlen(header), errp) < 0) { - return false; - } - - linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); - for (y = 0; y < height; y++) { - qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); - if (qio_channel_write_all(QIO_CHANNEL(ioc), - (char *)pixman_image_get_data(linebuf), - pixman_image_get_stride(linebuf), errp) < 0) { - return false; - } - } - - return true; -} - -static void graphic_hw_update_bh(void *con) -{ - graphic_hw_update(con); -} - -/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ -void coroutine_fn -qmp_screendump(const char *filename, const char *device, - bool has_head, int64_t head, - bool has_format, ImageFormat format, Error **errp) -{ - g_autoptr(pixman_image_t) image = NULL; - QemuConsole *con; - DisplaySurface *surface; - int fd; - - if (device) { - con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, - errp); - if (!con) { - return; - } - } else { - if (has_head) { - error_setg(errp, "'head' must be specified together with 'device'"); - return; - } - con = qemu_console_lookup_by_index(0); - if (!con) { - error_setg(errp, "There is no console to take a screendump from"); - return; - } - } - - if (qemu_co_queue_empty(&con->dump_queue)) { - /* Defer the update, it will restart the pending coroutines */ - aio_bh_schedule_oneshot(qemu_get_aio_context(), - graphic_hw_update_bh, con); - } - qemu_co_queue_wait(&con->dump_queue, NULL); - - /* - * All pending coroutines are woken up, while the BQL is held. No - * further graphic update are possible until it is released. Take - * an image ref before that. - */ - surface = qemu_console_surface(con); - if (!surface) { - error_setg(errp, "no surface"); - return; - } - image = pixman_image_ref(surface->image); - - fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); - if (fd == -1) { - error_setg(errp, "failed to open file '%s': %s", filename, - strerror(errno)); - return; - } - - /* - * The image content could potentially be updated as the coroutine - * yields and releases the BQL. It could produce corrupted dump, but - * it should be otherwise safe. - */ - if (has_format && format == IMAGE_FORMAT_PNG) { - /* PNG format specified for screendump */ - if (!png_save(fd, image, errp)) { - qemu_unlink(filename); - } - } else { - /* PPM format specified/default for screendump */ - if (!ppm_save(fd, image, errp)) { - qemu_unlink(filename); - } - } -} - void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata) { if (!con) { diff --git a/ui/ui-qmp-cmds.c b/ui/ui-qmp-cmds.c index a37a7024f3..debc07d678 100644 --- a/ui/ui-qmp-cmds.c +++ b/ui/ui-qmp-cmds.c @@ -14,13 +14,20 @@ */ #include "qemu/osdep.h" + +#include "io/channel-file.h" #include "monitor/qmp-helpers.h" #include "qapi/qapi-commands-ui.h" #include "qapi/qmp/qerror.h" +#include "qemu/coroutine.h" #include "qemu/cutils.h" +#include "trace.h" #include "ui/console.h" #include "ui/dbus-display.h" #include "ui/qemu-spice.h" +#ifdef CONFIG_PNG +#include +#endif void qmp_set_password(SetPasswordOptions *opts, Error **errp) { @@ -204,3 +211,183 @@ void qmp_client_migrate_info(const char *protocol, const char *hostname, error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'"); } + +#ifdef CONFIG_PNG +/** + * png_save: Take a screenshot as PNG + * + * Saves screendump as a PNG file + * + * Returns true for success or false for error. + * + * @fd: File descriptor for PNG file. + * @image: Image data in pixman format. + * @errp: Pointer to an error. + */ +static bool png_save(int fd, pixman_image_t *image, Error **errp) +{ + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); + png_struct *png_ptr; + png_info *info_ptr; + g_autoptr(pixman_image_t) linebuf = + qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); + uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); + FILE *f = fdopen(fd, "wb"); + int y; + if (!f) { + error_setg_errno(errp, errno, + "Failed to create file from file descriptor"); + return false; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); + if (!png_ptr) { + error_setg(errp, "PNG creation failed. Unable to write struct"); + fclose(f); + return false; + } + + info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) { + error_setg(errp, "PNG creation failed. Unable to write info"); + fclose(f); + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + png_init_io(png_ptr, f); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + for (y = 0; y < height; ++y) { + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); + png_write_row(png_ptr, buf); + } + + png_write_end(png_ptr, NULL); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + if (fclose(f) != 0) { + error_setg_errno(errp, errno, + "PNG creation failed. Unable to close file"); + return false; + } + + return true; +} + +#else /* no png support */ + +static bool png_save(int fd, pixman_image_t *image, Error **errp) +{ + error_setg(errp, "Enable PNG support with libpng for screendump"); + return false; +} + +#endif /* CONFIG_PNG */ + +static bool ppm_save(int fd, pixman_image_t *image, Error **errp) +{ + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); + g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd)); + g_autofree char *header = NULL; + g_autoptr(pixman_image_t) linebuf = NULL; + int y; + + trace_ppm_save(fd, image); + + header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255); + if (qio_channel_write_all(QIO_CHANNEL(ioc), + header, strlen(header), errp) < 0) { + return false; + } + + linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); + for (y = 0; y < height; y++) { + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); + if (qio_channel_write_all(QIO_CHANNEL(ioc), + (char *)pixman_image_get_data(linebuf), + pixman_image_get_stride(linebuf), errp) < 0) { + return false; + } + } + + return true; +} + +/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ +void coroutine_fn +qmp_screendump(const char *filename, const char *device, + bool has_head, int64_t head, + bool has_format, ImageFormat format, Error **errp) +{ + g_autoptr(pixman_image_t) image = NULL; + QemuConsole *con; + DisplaySurface *surface; + int fd; + + if (device) { + con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, + errp); + if (!con) { + return; + } + } else { + if (has_head) { + error_setg(errp, "'head' must be specified together with 'device'"); + return; + } + con = qemu_console_lookup_by_index(0); + if (!con) { + error_setg(errp, "There is no console to take a screendump from"); + return; + } + } + + qemu_console_co_wait_update(con); + + /* + * All pending coroutines are woken up, while the BQL is held. No + * further graphic update are possible until it is released. Take + * an image ref before that. + */ + surface = qemu_console_surface(con); + if (!surface) { + error_setg(errp, "no surface"); + return; + } + image = pixman_image_ref(surface->image); + + fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); + if (fd == -1) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); + return; + } + + /* + * The image content could potentially be updated as the coroutine + * yields and releases the BQL. It could produce corrupted dump, but + * it should be otherwise safe. + */ + if (has_format && format == IMAGE_FORMAT_PNG) { + /* PNG format specified for screendump */ + if (!png_save(fd, image, errp)) { + qemu_unlink(filename); + } + } else { + /* PPM format specified/default for screendump */ + if (!ppm_save(fd, image, errp)) { + qemu_unlink(filename); + } + } +} From f1f7a1e2cfee7beee626552744efcc5a3867501f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:38 +0400 Subject: [PATCH 04/52] ui/vc: replace vc_chr_write() with generic qemu_chr_write() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We shouldn't call the callback directly, but use the chardev API, unless there is a clear reason. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230830093843.3531473-5-marcandre.lureau@redhat.com> --- ui/console.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/console.c b/ui/console.c index 9c17024dbc..a448e4d283 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1169,13 +1169,13 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym) *q++ = '['; *q++ = keysym & 0xff; } else if (s->echo && (keysym == '\r' || keysym == '\n')) { - vc_chr_write(s->chr, (const uint8_t *) "\r", 1); + qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true); *q++ = '\n'; } else { *q++ = keysym; } if (s->echo) { - vc_chr_write(s->chr, buf, q - buf); + qemu_chr_write(s->chr, buf, q - buf, true); } num_free = fifo8_num_free(&s->out_fifo); fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); @@ -2474,7 +2474,7 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds) s->t_attrib.bgcol = QEMU_COLOR_BLUE; msg = g_strdup_printf("%s console\r\n", chr->label); - vc_chr_write(chr, (uint8_t *)msg, strlen(msg)); + qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true); g_free(msg); s->t_attrib = s->t_attrib_default; } From 177422789be54447cfc2d770145968058e5d0b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:39 +0400 Subject: [PATCH 05/52] ui/vc: drop have_text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there are no "text" listener, the callback will simply be ignored. The rest of text handling can be done cheaply. This allows to remove some dependency on DisplayState from VC implementation. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-6-marcandre.lureau@redhat.com> --- ui/console.c | 42 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/ui/console.c b/ui/console.c index a448e4d283..bec2d1a40a 100644 --- a/ui/console.c +++ b/ui/console.c @@ -133,7 +133,6 @@ struct DisplayState { uint64_t update_interval; bool refreshing; bool have_gfx; - bool have_text; QLIST_HEAD(, DisplayChangeListener) listeners; }; @@ -185,7 +184,6 @@ static void gui_setup_refresh(DisplayState *ds) DisplayChangeListener *dcl; bool need_timer = false; bool have_gfx = false; - bool have_text = false; QLIST_FOREACH(dcl, &ds->listeners, next) { if (dcl->ops->dpy_refresh != NULL) { @@ -194,9 +192,6 @@ static void gui_setup_refresh(DisplayState *ds) if (dcl->ops->dpy_gfx_update != NULL) { have_gfx = true; } - if (dcl->ops->dpy_text_update != NULL) { - have_text = true; - } } if (need_timer && ds->gui_timer == NULL) { @@ -209,7 +204,6 @@ static void gui_setup_refresh(DisplayState *ds) } ds->have_gfx = have_gfx; - ds->have_text = have_text; } void graphic_hw_update_done(QemuConsole *con) @@ -456,9 +450,7 @@ static void update_xy(QemuConsole *s, int x, int y) TextCell *c; int y1, y2; - if (s->ds->have_text) { - text_update_xy(s, x, y); - } + text_update_xy(s, x, y); y1 = (s->y_base + y) % s->total_height; y2 = y1 - s->y_displayed; @@ -482,9 +474,7 @@ static void console_show_cursor(QemuConsole *s, int show) int y, y1; int x = s->x; - if (s->ds->have_text) { - s->cursor_invalidate = 1; - } + s->cursor_invalidate = 1; if (x >= s->width) { x = s->width - 1; @@ -513,13 +503,11 @@ static void console_refresh(QemuConsole *s) TextCell *c; int x, y, y1; - if (s->ds->have_text) { - s->text_x[0] = 0; - s->text_y[0] = 0; - s->text_x[1] = s->width - 1; - s->text_y[1] = s->height - 1; - s->cursor_invalidate = 1; - } + s->text_x[0] = 0; + s->text_y[0] = 0; + s->text_x[1] = s->width - 1; + s->text_y[1] = s->height - 1; + s->cursor_invalidate = 1; vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), color_table_rgb[0][QEMU_COLOR_BLACK]); @@ -594,12 +582,10 @@ static void console_put_lf(QemuConsole *s) c++; } if (s->y_displayed == s->y_base) { - if (s->ds->have_text) { - s->text_x[0] = 0; - s->text_y[0] = 0; - s->text_x[1] = s->width - 1; - s->text_y[1] = s->height - 1; - } + s->text_x[0] = 0; + s->text_y[0] = 0; + s->text_x[1] = s->width - 1; + s->text_y[1] = s->height - 1; vga_bitblt(s, 0, FONT_HEIGHT, 0, 0, s->width * FONT_WIDTH, @@ -1069,9 +1055,7 @@ void console_select(unsigned int index) displaychangelistener_display_console(dcl, s, NULL); } } - if (ds->have_text) { - dpy_text_resize(s, s->width, s->height); - } + dpy_text_resize(s, s->width, s->height); text_console_update_cursor(NULL); } } @@ -1239,7 +1223,7 @@ static void text_console_invalidate(void *opaque) { QemuConsole *s = (QemuConsole *) opaque; - if (s->ds->have_text && s->console_type == TEXT_CONSOLE) { + if (s->console_type == TEXT_CONSOLE) { text_console_resize(s); } console_refresh(s); From 074b24094f34c3241956064cf7910bbe11642871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:40 +0400 Subject: [PATCH 06/52] ui/console: console_select() regardless of have_gfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even if we don't have a gfx listener, we should call displaychangelistener_display_console() which handle that case correctly. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-7-marcandre.lureau@redhat.com> --- ui/console.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ui/console.c b/ui/console.c index bec2d1a40a..14717a6f4d 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1047,13 +1047,11 @@ void console_select(unsigned int index) DisplayState *ds = s->ds; active_console = s; - if (ds->have_gfx) { - QLIST_FOREACH(dcl, &ds->listeners, next) { - if (dcl->con != NULL) { - continue; - } - displaychangelistener_display_console(dcl, s, NULL); + QLIST_FOREACH (dcl, &ds->listeners, next) { + if (dcl->con != NULL) { + continue; } + displaychangelistener_display_console(dcl, s, NULL); } dpy_text_resize(s, s->width, s->height); text_console_update_cursor(NULL); From bc9b8bc93cafee6f3c9f73ef5e8a7379004e8699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:41 +0400 Subject: [PATCH 07/52] ui/console: call dpy_gfx_update() regardless of have_gfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function will handle the case when no listeners are gfx, without extra meaningful cost. This allows to get rid of DisplayState dependency in VC implementation. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-8-marcandre.lureau@redhat.com> --- ui/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/console.c b/ui/console.c index 14717a6f4d..2bc4c153de 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1087,7 +1087,7 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) console_putchar(s, buf[i]); } console_show_cursor(s, 1); - if (s->ds->have_gfx && s->update_x0 < s->update_x1) { + if (s->update_x0 < s->update_x1) { dpy_gfx_update(s, s->update_x0, s->update_y0, s->update_x1 - s->update_x0, s->update_y1 - s->update_y0); From cbcf0fa8fd9723ee51af803bf58a8d6d3e6a4194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:42 +0400 Subject: [PATCH 08/52] ui/console: drop have_gfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All usages have been removed. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-9-marcandre.lureau@redhat.com> --- ui/console.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ui/console.c b/ui/console.c index 2bc4c153de..fc1836782d 100644 --- a/ui/console.c +++ b/ui/console.c @@ -132,7 +132,6 @@ struct DisplayState { uint64_t last_update; uint64_t update_interval; bool refreshing; - bool have_gfx; QLIST_HEAD(, DisplayChangeListener) listeners; }; @@ -183,15 +182,11 @@ static void gui_setup_refresh(DisplayState *ds) { DisplayChangeListener *dcl; bool need_timer = false; - bool have_gfx = false; QLIST_FOREACH(dcl, &ds->listeners, next) { if (dcl->ops->dpy_refresh != NULL) { need_timer = true; } - if (dcl->ops->dpy_gfx_update != NULL) { - have_gfx = true; - } } if (need_timer && ds->gui_timer == NULL) { @@ -202,8 +197,6 @@ static void gui_setup_refresh(DisplayState *ds) timer_free(ds->gui_timer); ds->gui_timer = NULL; } - - ds->have_gfx = have_gfx; } void graphic_hw_update_done(QemuConsole *con) From 2fd319cff0ffbc0b54a61a2a34775ec40836e4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:43 +0400 Subject: [PATCH 09/52] ui/console: get the DisplayState from new_console() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no obvious reason to defer text console initialization. We can simply take the global display state in new_console(). This simplify somewhat the code to allow moving the VC to a separate unit. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-10-marcandre.lureau@redhat.com> --- ui/console.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/ui/console.c b/ui/console.c index fc1836782d..24cfd31ad6 100644 --- a/ui/console.c +++ b/ui/console.c @@ -143,7 +143,7 @@ static QTAILQ_HEAD(, QemuConsole) consoles = static bool cursor_visible_phase; static QEMUTimer *cursor_timer; -static void text_console_do_init(Chardev *chr, DisplayState *ds); +static void text_console_do_init(Chardev *chr); static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); @@ -1249,9 +1249,9 @@ static void text_console_update(void *opaque, console_ch_t *chardata) } } -static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, - uint32_t head) +static QemuConsole *new_console(console_type_t console_type, uint32_t head) { + DisplayState *ds = get_alloc_displaystate(); Object *obj; QemuConsole *s; int i; @@ -2049,13 +2049,7 @@ DisplayState *init_displaystate(void) gchar *name; QemuConsole *con; - get_alloc_displaystate(); QTAILQ_FOREACH(con, &consoles, next) { - if (con->console_type != GRAPHIC_CONSOLE && - con->ds == NULL) { - text_console_do_init(con->chr, display_state); - } - /* Hook up into the qom tree here (not in new_console()), once * all QemuConsoles are created and the order / numbering * doesn't change any more */ @@ -2085,10 +2079,8 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, int width = 640; int height = 480; QemuConsole *s; - DisplayState *ds; DisplaySurface *surface; - ds = get_alloc_displaystate(); s = qemu_console_lookup_unused(); if (s) { trace_console_gfx_reuse(s->index); @@ -2096,7 +2088,7 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, height = qemu_console_get_height(s, 0); } else { trace_console_gfx_new(); - s = new_console(ds, GRAPHIC_CONSOLE, head); + s = new_console(GRAPHIC_CONSOLE, head); s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, dpy_set_ui_info_timer, s); } @@ -2405,7 +2397,7 @@ static const GraphicHwOps text_console_ops = { .text_update = text_console_update, }; -static void text_console_do_init(Chardev *chr, DisplayState *ds) +static void text_console_do_init(Chardev *chr) { VCChardev *drv = VC_CHARDEV(chr); QemuConsole *s = drv->console; @@ -2413,7 +2405,6 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds) int g_height = 24 * FONT_HEIGHT; fifo8_create(&s->out_fifo, 16); - s->ds = ds; s->y_displayed = 0; s->y_base = 0; @@ -2482,9 +2473,9 @@ static void vc_chr_open(Chardev *chr, trace_console_txt_new(width, height); if (width == 0 || height == 0) { - s = new_console(NULL, TEXT_CONSOLE, 0); + s = new_console(TEXT_CONSOLE, 0); } else { - s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0); + s = new_console(TEXT_CONSOLE_FIXED_SIZE, 0); s->scanout.kind = SCANOUT_SURFACE; s->surface = qemu_create_displaysurface(width, height); } @@ -2497,9 +2488,7 @@ static void vc_chr_open(Chardev *chr, s->chr = chr; drv->console = s; - if (display_state) { - text_console_do_init(chr, display_state); - } + text_console_do_init(chr); /* console/chardev init sometimes completes elsewhere in a 2nd * stage, so defer OPENED events until they are fully initialized From dc6984bdc3ebe5357b0c1d983ba4e7689a985f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:44 +0400 Subject: [PATCH 10/52] ui/console: new_console() cannot fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no code path that could allow a NULL return there. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-11-marcandre.lureau@redhat.com> --- ui/console.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ui/console.c b/ui/console.c index 24cfd31ad6..ddec68feb7 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2480,11 +2480,6 @@ static void vc_chr_open(Chardev *chr, s->surface = qemu_create_displaysurface(width, height); } - if (!s) { - error_setg(errp, "cannot create text console"); - return; - } - s->chr = chr; drv->console = s; From 6657e41cde73597e61c0165da7be7e76f116f342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:45 +0400 Subject: [PATCH 11/52] ui/vc: VC always has a DisplayState now MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-12-marcandre.lureau@redhat.com> --- ui/console.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/console.c b/ui/console.c index ddec68feb7..f97db295f6 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1067,10 +1067,6 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) QemuConsole *s = drv->console; int i; - if (!s->ds) { - return 0; - } - s->update_x0 = s->width * FONT_WIDTH; s->update_y0 = s->height * FONT_HEIGHT; s->update_x1 = 0; From 8c63667b25cf377fa6ef46149ac55dc7e9e553db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:46 +0400 Subject: [PATCH 12/52] ui/vc: move VCChardev declaration at the top MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow easier refactoring in following patches. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-13-marcandre.lureau@redhat.com> --- ui/console.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/console.c b/ui/console.c index f97db295f6..5c8e3ad1df 100644 --- a/ui/console.c +++ b/ui/console.c @@ -127,6 +127,12 @@ struct QemuConsole { QTAILQ_ENTRY(QemuConsole) next; }; +struct VCChardev { + Chardev parent; + QemuConsole *console; +}; +typedef struct VCChardev VCChardev; + struct DisplayState { QEMUTimer *gui_timer; uint64_t last_update; @@ -1051,12 +1057,6 @@ void console_select(unsigned int index) } } -struct VCChardev { - Chardev parent; - QemuConsole *console; -}; -typedef struct VCChardev VCChardev; - #define TYPE_CHARDEV_VC "chardev-vc" DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV, TYPE_CHARDEV_VC) From 3be82c6a3a983cd382aad2200fede5ec304dbc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:47 +0400 Subject: [PATCH 13/52] ui/vc: replace variable with static text attributes default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-14-marcandre.lureau@redhat.com> --- ui/console.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ui/console.c b/ui/console.c index 5c8e3ad1df..d1855f3fcf 100644 --- a/ui/console.c +++ b/ui/console.c @@ -52,6 +52,11 @@ typedef struct TextAttributes { uint8_t unvisible:1; } TextAttributes; +#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \ + .fgcol = QEMU_COLOR_WHITE, \ + .bgcol = QEMU_COLOR_BLACK \ +}) + typedef struct TextCell { uint8_t ch; TextAttributes t_attrib; @@ -104,7 +109,6 @@ struct QemuConsole { int x_saved, y_saved; int y_displayed; int y_base; - TextAttributes t_attrib_default; /* default text attributes */ TextAttributes t_attrib; /* currently active text attributes */ TextCell *cells; int text_x[2], text_y[2], cursor_invalidate; @@ -413,7 +417,7 @@ static void text_console_resize(QemuConsole *s) } for(x = w1; x < s->width; x++) { c->ch = ' '; - c->t_attrib = s->t_attrib_default; + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; c++; } } @@ -486,7 +490,7 @@ static void console_show_cursor(QemuConsole *s, int show) if (y < s->height) { c = &s->cells[y1 * s->width + x]; if (show && cursor_visible_phase) { - TextAttributes t_attrib = s->t_attrib_default; + TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT; t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ vga_putcharxy(s, x, y, c->ch, &t_attrib); } else { @@ -577,7 +581,7 @@ static void console_put_lf(QemuConsole *s) c = &s->cells[y1 * s->width]; for(x = 0; x < s->width; x++) { c->ch = ' '; - c->t_attrib = s->t_attrib_default; + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; c++; } if (s->y_displayed == s->y_base) { @@ -591,7 +595,7 @@ static void console_put_lf(QemuConsole *s) (s->height - 1) * FONT_HEIGHT); vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT, s->width * FONT_WIDTH, FONT_HEIGHT, - color_table_rgb[0][s->t_attrib_default.bgcol]); + color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]); s->update_x0 = 0; s->update_y0 = 0; s->update_x1 = s->width * FONT_WIDTH; @@ -611,7 +615,7 @@ static void console_handle_escape(QemuConsole *s) for (i=0; inb_esc_params; i++) { switch (s->esc_params[i]) { case 0: /* reset all console attributes to default */ - s->t_attrib = s->t_attrib_default; + s->t_attrib = TEXT_ATTRIBUTES_DEFAULT; break; case 1: s->t_attrib.bold = 1; @@ -705,7 +709,7 @@ static void console_clear_xy(QemuConsole *s, int x, int y) } TextCell *c = &s->cells[y1 * s->width + x]; c->ch = ' '; - c->t_attrib = s->t_attrib_default; + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; update_xy(s, x, y); } @@ -2419,16 +2423,8 @@ static void text_console_do_init(Chardev *chr) s->hw_ops = &text_console_ops; s->hw = s; - /* Set text attribute defaults */ - s->t_attrib_default.bold = 0; - s->t_attrib_default.uline = 0; - s->t_attrib_default.blink = 0; - s->t_attrib_default.invers = 0; - s->t_attrib_default.unvisible = 0; - s->t_attrib_default.fgcol = QEMU_COLOR_WHITE; - s->t_attrib_default.bgcol = QEMU_COLOR_BLACK; /* set current text attributes to default */ - s->t_attrib = s->t_attrib_default; + s->t_attrib = TEXT_ATTRIBUTES_DEFAULT; text_console_resize(s); if (chr->label) { @@ -2438,7 +2434,7 @@ static void text_console_do_init(Chardev *chr) msg = g_strdup_printf("%s console\r\n", chr->label); qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true); g_free(msg); - s->t_attrib = s->t_attrib_default; + s->t_attrib = TEXT_ATTRIBUTES_DEFAULT; } qemu_chr_be_event(chr, CHR_EVENT_OPENED); From d7c634aadf83e029b70b5d508fbfda4671e206d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:48 +0400 Subject: [PATCH 14/52] ui/vc: fold text_update_xy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-15-marcandre.lureau@redhat.com> --- ui/console.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/ui/console.c b/ui/console.c index d1855f3fcf..405aedfcbd 100644 --- a/ui/console.c +++ b/ui/console.c @@ -425,14 +425,6 @@ static void text_console_resize(QemuConsole *s) s->cells = cells; } -static inline void text_update_xy(QemuConsole *s, int x, int y) -{ - s->text_x[0] = MIN(s->text_x[0], x); - s->text_x[1] = MAX(s->text_x[1], x); - s->text_y[0] = MIN(s->text_y[0], y); - s->text_y[1] = MAX(s->text_y[1], y); -} - static void invalidate_xy(QemuConsole *s, int x, int y) { if (!qemu_console_is_visible(s)) { @@ -453,7 +445,10 @@ static void update_xy(QemuConsole *s, int x, int y) TextCell *c; int y1, y2; - text_update_xy(s, x, y); + s->text_x[0] = MIN(s->text_x[0], x); + s->text_x[1] = MAX(s->text_x[1], x); + s->text_y[0] = MIN(s->text_y[0], y); + s->text_y[1] = MAX(s->text_y[1], y); y1 = (s->y_base + y) % s->total_height; y2 = y1 - s->y_displayed; From 4c946b7f97e09e625d8c359f06f6b3e1ce937e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:49 +0400 Subject: [PATCH 15/52] ui/vc: pass VCCharDev to VC-specific functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even though they actually use more of QemuConsole at this point, it makes it clearer those functions are only used from the chardev implementation. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-16-marcandre.lureau@redhat.com> --- ui/console.c | 70 +++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/ui/console.c b/ui/console.c index 405aedfcbd..f9ed012e1c 100644 --- a/ui/console.c +++ b/ui/console.c @@ -440,8 +440,9 @@ static void invalidate_xy(QemuConsole *s, int x, int y) s->update_y1 = (y + 1) * FONT_HEIGHT; } -static void update_xy(QemuConsole *s, int x, int y) +static void vc_update_xy(VCChardev *vc, int x, int y) { + QemuConsole *s = vc->console; TextCell *c; int y1, y2; @@ -555,8 +556,9 @@ static void console_scroll(QemuConsole *s, int ydelta) console_refresh(s); } -static void console_put_lf(QemuConsole *s) +static void vc_put_lf(VCChardev *vc) { + QemuConsole *s = vc->console; TextCell *c; int x, y1; @@ -603,8 +605,9 @@ static void console_put_lf(QemuConsole *s) * NOTE: I know this code is not very efficient (checking every color for it * self) but it is more readable and better maintainable. */ -static void console_handle_escape(QemuConsole *s) +static void vc_handle_escape(VCChardev *vc) { + QemuConsole *s = vc->console; int i; for (i=0; inb_esc_params; i++) { @@ -696,8 +699,9 @@ static void console_handle_escape(QemuConsole *s) } } -static void console_clear_xy(QemuConsole *s, int x, int y) +static void vc_clear_xy(VCChardev *vc, int x, int y) { + QemuConsole *s = vc->console; int y1 = (s->y_base + y) % s->total_height; if (x >= s->width) { x = s->width - 1; @@ -705,37 +709,40 @@ static void console_clear_xy(QemuConsole *s, int x, int y) TextCell *c = &s->cells[y1 * s->width + x]; c->ch = ' '; c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; - update_xy(s, x, y); + vc_update_xy(vc, x, y); } -static void console_put_one(QemuConsole *s, int ch) +static void vc_put_one(VCChardev *vc, int ch) { + QemuConsole *s = vc->console; TextCell *c; int y1; if (s->x >= s->width) { /* line wrap */ s->x = 0; - console_put_lf(s); + vc_put_lf(vc); } y1 = (s->y_base + s->y) % s->total_height; c = &s->cells[y1 * s->width + s->x]; c->ch = ch; c->t_attrib = s->t_attrib; - update_xy(s, s->x, s->y); + vc_update_xy(vc, s->x, s->y); s->x++; } -static void console_respond_str(QemuConsole *s, const char *buf) +static void vc_respond_str(VCChardev *vc, const char *buf) { while (*buf) { - console_put_one(s, *buf); + vc_put_one(vc, *buf); buf++; } } /* set cursor, checking bounds */ -static void set_cursor(QemuConsole *s, int x, int y) +static void vc_set_cursor(VCChardev *vc, int x, int y) { + QemuConsole *s = vc->console; + if (x < 0) { x = 0; } @@ -753,8 +760,9 @@ static void set_cursor(QemuConsole *s, int x, int y) s->y = y; } -static void console_putchar(QemuConsole *s, int ch) +static void vc_putchar(VCChardev *vc, int ch) { + QemuConsole *s = vc->console; int i; int x, y; char response[40]; @@ -766,7 +774,7 @@ static void console_putchar(QemuConsole *s, int ch) s->x = 0; break; case '\n': /* newline */ - console_put_lf(s); + vc_put_lf(vc); break; case '\b': /* backspace */ if (s->x > 0) @@ -775,7 +783,7 @@ static void console_putchar(QemuConsole *s, int ch) case '\t': /* tabspace */ if (s->x + (8 - (s->x % 8)) > s->width) { s->x = 0; - console_put_lf(s); + vc_put_lf(vc); } else { s->x = s->x + (8 - (s->x % 8)); } @@ -793,7 +801,7 @@ static void console_putchar(QemuConsole *s, int ch) s->state = TTY_STATE_ESC; break; default: - console_put_one(s, ch); + vc_put_one(vc, ch); break; } break; @@ -831,37 +839,37 @@ static void console_putchar(QemuConsole *s, int ch) if (s->esc_params[0] == 0) { s->esc_params[0] = 1; } - set_cursor(s, s->x, s->y - s->esc_params[0]); + vc_set_cursor(vc, s->x, s->y - s->esc_params[0]); break; case 'B': /* move cursor down */ if (s->esc_params[0] == 0) { s->esc_params[0] = 1; } - set_cursor(s, s->x, s->y + s->esc_params[0]); + vc_set_cursor(vc, s->x, s->y + s->esc_params[0]); break; case 'C': /* move cursor right */ if (s->esc_params[0] == 0) { s->esc_params[0] = 1; } - set_cursor(s, s->x + s->esc_params[0], s->y); + vc_set_cursor(vc, s->x + s->esc_params[0], s->y); break; case 'D': /* move cursor left */ if (s->esc_params[0] == 0) { s->esc_params[0] = 1; } - set_cursor(s, s->x - s->esc_params[0], s->y); + vc_set_cursor(vc, s->x - s->esc_params[0], s->y); break; case 'G': /* move cursor to column */ - set_cursor(s, s->esc_params[0] - 1, s->y); + vc_set_cursor(vc, s->esc_params[0] - 1, s->y); break; case 'f': case 'H': /* move cursor to row, column */ - set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1); + vc_set_cursor(vc, s->esc_params[1] - 1, s->esc_params[0] - 1); break; case 'J': switch (s->esc_params[0]) { @@ -872,7 +880,7 @@ static void console_putchar(QemuConsole *s, int ch) if (y == s->y && x < s->x) { continue; } - console_clear_xy(s, x, y); + vc_clear_xy(vc, x, y); } } break; @@ -883,7 +891,7 @@ static void console_putchar(QemuConsole *s, int ch) if (y == s->y && x > s->x) { break; } - console_clear_xy(s, x, y); + vc_clear_xy(vc, x, y); } } break; @@ -891,7 +899,7 @@ static void console_putchar(QemuConsole *s, int ch) /* clear entire screen */ for (y = 0; y <= s->height; y++) { for (x = 0; x < s->width; x++) { - console_clear_xy(s, x, y); + vc_clear_xy(vc, x, y); } } break; @@ -902,38 +910,38 @@ static void console_putchar(QemuConsole *s, int ch) case 0: /* clear to eol */ for(x = s->x; x < s->width; x++) { - console_clear_xy(s, x, s->y); + vc_clear_xy(vc, x, s->y); } break; case 1: /* clear from beginning of line */ for (x = 0; x <= s->x && x < s->width; x++) { - console_clear_xy(s, x, s->y); + vc_clear_xy(vc, x, s->y); } break; case 2: /* clear entire line */ for(x = 0; x < s->width; x++) { - console_clear_xy(s, x, s->y); + vc_clear_xy(vc, x, s->y); } break; } break; case 'm': - console_handle_escape(s); + vc_handle_escape(vc); break; case 'n': switch (s->esc_params[0]) { case 5: /* report console status (always succeed)*/ - console_respond_str(s, "\033[0n"); + vc_respond_str(vc, "\033[0n"); break; case 6: /* report cursor position */ sprintf(response, "\033[%d;%dR", (s->y_base + s->y) % s->total_height + 1, s->x + 1); - console_respond_str(s, response); + vc_respond_str(vc, response); break; } break; @@ -1072,7 +1080,7 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) s->update_y1 = 0; console_show_cursor(s, 0); for(i = 0; i < len; i++) { - console_putchar(s, buf[i]); + vc_putchar(drv, buf[i]); } console_show_cursor(s, 1); if (s->update_x0 < s->update_x1) { From 6505fd8d2390e57c6a2e84f9c07b9e408ad7da76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:50 +0400 Subject: [PATCH 16/52] ui/vc: move VCCharDev specific fields out of QemuConsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-17-marcandre.lureau@redhat.com> --- ui/console.c | 147 +++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 74 deletions(-) diff --git a/ui/console.c b/ui/console.c index f9ed012e1c..1336a1be6c 100644 --- a/ui/console.c +++ b/ui/console.c @@ -106,10 +106,8 @@ struct QemuConsole { int total_height; int backscroll_height; int x, y; - int x_saved, y_saved; int y_displayed; int y_base; - TextAttributes t_attrib; /* currently active text attributes */ TextCell *cells; int text_x[2], text_y[2], cursor_invalidate; int echo; @@ -119,10 +117,6 @@ struct QemuConsole { int update_x1; int update_y1; - enum TTYState state; - int esc_params[MAX_ESC_PARAMS]; - int nb_esc_params; - Chardev *chr; /* fifo for key pressed */ Fifo8 out_fifo; @@ -134,6 +128,12 @@ struct QemuConsole { struct VCChardev { Chardev parent; QemuConsole *console; + + enum TTYState state; + int esc_params[MAX_ESC_PARAMS]; + int nb_esc_params; + TextAttributes t_attrib; /* currently active text attributes */ + int x_saved, y_saved; }; typedef struct VCChardev VCChardev; @@ -607,93 +607,92 @@ static void vc_put_lf(VCChardev *vc) */ static void vc_handle_escape(VCChardev *vc) { - QemuConsole *s = vc->console; int i; - for (i=0; inb_esc_params; i++) { - switch (s->esc_params[i]) { + for (i = 0; i < vc->nb_esc_params; i++) { + switch (vc->esc_params[i]) { case 0: /* reset all console attributes to default */ - s->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + vc->t_attrib = TEXT_ATTRIBUTES_DEFAULT; break; case 1: - s->t_attrib.bold = 1; + vc->t_attrib.bold = 1; break; case 4: - s->t_attrib.uline = 1; + vc->t_attrib.uline = 1; break; case 5: - s->t_attrib.blink = 1; + vc->t_attrib.blink = 1; break; case 7: - s->t_attrib.invers = 1; + vc->t_attrib.invers = 1; break; case 8: - s->t_attrib.unvisible = 1; + vc->t_attrib.unvisible = 1; break; case 22: - s->t_attrib.bold = 0; + vc->t_attrib.bold = 0; break; case 24: - s->t_attrib.uline = 0; + vc->t_attrib.uline = 0; break; case 25: - s->t_attrib.blink = 0; + vc->t_attrib.blink = 0; break; case 27: - s->t_attrib.invers = 0; + vc->t_attrib.invers = 0; break; case 28: - s->t_attrib.unvisible = 0; + vc->t_attrib.unvisible = 0; break; /* set foreground color */ case 30: - s->t_attrib.fgcol = QEMU_COLOR_BLACK; + vc->t_attrib.fgcol = QEMU_COLOR_BLACK; break; case 31: - s->t_attrib.fgcol = QEMU_COLOR_RED; + vc->t_attrib.fgcol = QEMU_COLOR_RED; break; case 32: - s->t_attrib.fgcol = QEMU_COLOR_GREEN; + vc->t_attrib.fgcol = QEMU_COLOR_GREEN; break; case 33: - s->t_attrib.fgcol = QEMU_COLOR_YELLOW; + vc->t_attrib.fgcol = QEMU_COLOR_YELLOW; break; case 34: - s->t_attrib.fgcol = QEMU_COLOR_BLUE; + vc->t_attrib.fgcol = QEMU_COLOR_BLUE; break; case 35: - s->t_attrib.fgcol = QEMU_COLOR_MAGENTA; + vc->t_attrib.fgcol = QEMU_COLOR_MAGENTA; break; case 36: - s->t_attrib.fgcol = QEMU_COLOR_CYAN; + vc->t_attrib.fgcol = QEMU_COLOR_CYAN; break; case 37: - s->t_attrib.fgcol = QEMU_COLOR_WHITE; + vc->t_attrib.fgcol = QEMU_COLOR_WHITE; break; /* set background color */ case 40: - s->t_attrib.bgcol = QEMU_COLOR_BLACK; + vc->t_attrib.bgcol = QEMU_COLOR_BLACK; break; case 41: - s->t_attrib.bgcol = QEMU_COLOR_RED; + vc->t_attrib.bgcol = QEMU_COLOR_RED; break; case 42: - s->t_attrib.bgcol = QEMU_COLOR_GREEN; + vc->t_attrib.bgcol = QEMU_COLOR_GREEN; break; case 43: - s->t_attrib.bgcol = QEMU_COLOR_YELLOW; + vc->t_attrib.bgcol = QEMU_COLOR_YELLOW; break; case 44: - s->t_attrib.bgcol = QEMU_COLOR_BLUE; + vc->t_attrib.bgcol = QEMU_COLOR_BLUE; break; case 45: - s->t_attrib.bgcol = QEMU_COLOR_MAGENTA; + vc->t_attrib.bgcol = QEMU_COLOR_MAGENTA; break; case 46: - s->t_attrib.bgcol = QEMU_COLOR_CYAN; + vc->t_attrib.bgcol = QEMU_COLOR_CYAN; break; case 47: - s->t_attrib.bgcol = QEMU_COLOR_WHITE; + vc->t_attrib.bgcol = QEMU_COLOR_WHITE; break; } } @@ -725,7 +724,7 @@ static void vc_put_one(VCChardev *vc, int ch) y1 = (s->y_base + s->y) % s->total_height; c = &s->cells[y1 * s->width + s->x]; c->ch = ch; - c->t_attrib = s->t_attrib; + c->t_attrib = vc->t_attrib; vc_update_xy(vc, s->x, s->y); s->x++; } @@ -767,7 +766,7 @@ static void vc_putchar(VCChardev *vc, int ch) int x, y; char response[40]; - switch(s->state) { + switch(vc->state) { case TTY_STATE_NORM: switch(ch) { case '\r': /* carriage return */ @@ -798,7 +797,7 @@ static void vc_putchar(VCChardev *vc, int ch) /* SO (shift out), character set 1 (ignored) */ break; case 27: /* esc (introducing an escape sequence) */ - s->state = TTY_STATE_ESC; + vc->state = TTY_STATE_ESC; break; default: vc_put_one(vc, ch); @@ -808,71 +807,71 @@ static void vc_putchar(VCChardev *vc, int ch) case TTY_STATE_ESC: /* check if it is a terminal escape sequence */ if (ch == '[') { for(i=0;iesc_params[i] = 0; - s->nb_esc_params = 0; - s->state = TTY_STATE_CSI; + vc->esc_params[i] = 0; + vc->nb_esc_params = 0; + vc->state = TTY_STATE_CSI; } else { - s->state = TTY_STATE_NORM; + vc->state = TTY_STATE_NORM; } break; case TTY_STATE_CSI: /* handle escape sequence parameters */ if (ch >= '0' && ch <= '9') { - if (s->nb_esc_params < MAX_ESC_PARAMS) { - int *param = &s->esc_params[s->nb_esc_params]; + if (vc->nb_esc_params < MAX_ESC_PARAMS) { + int *param = &vc->esc_params[vc->nb_esc_params]; int digit = (ch - '0'); *param = (*param <= (INT_MAX - digit) / 10) ? *param * 10 + digit : INT_MAX; } } else { - if (s->nb_esc_params < MAX_ESC_PARAMS) - s->nb_esc_params++; + if (vc->nb_esc_params < MAX_ESC_PARAMS) + vc->nb_esc_params++; if (ch == ';' || ch == '?') { break; } - trace_console_putchar_csi(s->esc_params[0], s->esc_params[1], - ch, s->nb_esc_params); - s->state = TTY_STATE_NORM; + trace_console_putchar_csi(vc->esc_params[0], vc->esc_params[1], + ch, vc->nb_esc_params); + vc->state = TTY_STATE_NORM; switch(ch) { case 'A': /* move cursor up */ - if (s->esc_params[0] == 0) { - s->esc_params[0] = 1; + if (vc->esc_params[0] == 0) { + vc->esc_params[0] = 1; } - vc_set_cursor(vc, s->x, s->y - s->esc_params[0]); + vc_set_cursor(vc, s->x, s->y - vc->esc_params[0]); break; case 'B': /* move cursor down */ - if (s->esc_params[0] == 0) { - s->esc_params[0] = 1; + if (vc->esc_params[0] == 0) { + vc->esc_params[0] = 1; } - vc_set_cursor(vc, s->x, s->y + s->esc_params[0]); + vc_set_cursor(vc, s->x, s->y + vc->esc_params[0]); break; case 'C': /* move cursor right */ - if (s->esc_params[0] == 0) { - s->esc_params[0] = 1; + if (vc->esc_params[0] == 0) { + vc->esc_params[0] = 1; } - vc_set_cursor(vc, s->x + s->esc_params[0], s->y); + vc_set_cursor(vc, s->x + vc->esc_params[0], s->y); break; case 'D': /* move cursor left */ - if (s->esc_params[0] == 0) { - s->esc_params[0] = 1; + if (vc->esc_params[0] == 0) { + vc->esc_params[0] = 1; } - vc_set_cursor(vc, s->x - s->esc_params[0], s->y); + vc_set_cursor(vc, s->x - vc->esc_params[0], s->y); break; case 'G': /* move cursor to column */ - vc_set_cursor(vc, s->esc_params[0] - 1, s->y); + vc_set_cursor(vc, vc->esc_params[0] - 1, s->y); break; case 'f': case 'H': /* move cursor to row, column */ - vc_set_cursor(vc, s->esc_params[1] - 1, s->esc_params[0] - 1); + vc_set_cursor(vc, vc->esc_params[1] - 1, vc->esc_params[0] - 1); break; case 'J': - switch (s->esc_params[0]) { + switch (vc->esc_params[0]) { case 0: /* clear to end of screen */ for (y = s->y; y < s->height; y++) { @@ -906,7 +905,7 @@ static void vc_putchar(VCChardev *vc, int ch) } break; case 'K': - switch (s->esc_params[0]) { + switch (vc->esc_params[0]) { case 0: /* clear to eol */ for(x = s->x; x < s->width; x++) { @@ -931,7 +930,7 @@ static void vc_putchar(VCChardev *vc, int ch) vc_handle_escape(vc); break; case 'n': - switch (s->esc_params[0]) { + switch (vc->esc_params[0]) { case 5: /* report console status (always succeed)*/ vc_respond_str(vc, "\033[0n"); @@ -947,13 +946,13 @@ static void vc_putchar(VCChardev *vc, int ch) break; case 's': /* save cursor position */ - s->x_saved = s->x; - s->y_saved = s->y; + vc->x_saved = s->x; + vc->y_saved = s->y; break; case 'u': /* restore cursor position */ - s->x = s->x_saved; - s->y = s->y_saved; + s->x = vc->x_saved; + s->y = vc->y_saved; break; default: trace_console_putchar_unhandled(ch); @@ -2427,17 +2426,17 @@ static void text_console_do_init(Chardev *chr) s->hw = s; /* set current text attributes to default */ - s->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; text_console_resize(s); if (chr->label) { char *msg; - s->t_attrib.bgcol = QEMU_COLOR_BLUE; + drv->t_attrib.bgcol = QEMU_COLOR_BLUE; msg = g_strdup_printf("%s console\r\n", chr->label); qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true); g_free(msg); - s->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; } qemu_chr_be_event(chr, CHR_EVENT_OPENED); From e265917c77710ef721e4c333bccfecf030c7776c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:51 +0400 Subject: [PATCH 17/52] ui/console: use OBJECT_DEFINE_TYPE for QemuConsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following patch will move some object initialization to the corresponding handlers. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-18-marcandre.lureau@redhat.com> --- ui/console.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/ui/console.c b/ui/console.c index 1336a1be6c..14fb38c661 100644 --- a/ui/console.c +++ b/ui/console.c @@ -125,6 +125,8 @@ struct QemuConsole { QTAILQ_ENTRY(QemuConsole) next; }; +OBJECT_DEFINE_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT) + struct VCChardev { Chardev parent; QemuConsole *console; @@ -1313,6 +1315,21 @@ static QemuConsole *new_console(console_type_t console_type, uint32_t head) return s; } +static void +qemu_console_finalize(Object *obj) +{ +} + +static void +qemu_console_class_init(ObjectClass *oc, void *data) +{ +} + +static void +qemu_console_init(Object *obj) +{ +} + #ifdef WIN32 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, HANDLE h, uint32_t offset) @@ -2646,13 +2663,6 @@ void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) } } -static const TypeInfo qemu_console_info = { - .name = TYPE_QEMU_CONSOLE, - .parent = TYPE_OBJECT, - .instance_size = sizeof(QemuConsole), - .class_size = sizeof(QemuConsoleClass), -}; - static void char_vc_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -2678,10 +2688,3 @@ void qemu_console_early_init(void) type_register(&char_vc_type_info); } } - -static void register_types(void) -{ - type_register_static(&qemu_console_info); -} - -type_init(register_types); From 098d57e7c0aa347f08f0738e8bd55b9a7faed8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:52 +0400 Subject: [PATCH 18/52] ui/console: change new_console() to use object initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Object construction should be done in respective object instance and class handlers. Introduce qemu_console_register() to split out the registration logic. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-19-marcandre.lureau@redhat.com> --- ui/console.c | 92 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/ui/console.c b/ui/console.c index 14fb38c661..bdecfe7306 100644 --- a/ui/console.c +++ b/ui/console.c @@ -27,6 +27,7 @@ #include "hw/qdev-core.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" +#include "qapi/visitor.h" #include "qemu/coroutine.h" #include "qemu/fifo8.h" #include "qemu/error-report.h" @@ -1253,39 +1254,24 @@ static void text_console_update(void *opaque, console_ch_t *chardata) } } -static QemuConsole *new_console(console_type_t console_type, uint32_t head) +static void +qemu_console_register(QemuConsole *c, console_type_t console_type) { - DisplayState *ds = get_alloc_displaystate(); - Object *obj; - QemuConsole *s; int i; - obj = object_new(TYPE_QEMU_CONSOLE); - s = QEMU_CONSOLE(obj); - qemu_co_queue_init(&s->dump_queue); - s->head = head; - object_property_add_link(obj, "device", TYPE_DEVICE, - (Object **)&s->device, - object_property_allow_set_link, - OBJ_PROP_LINK_STRONG); - object_property_add_uint32_ptr(obj, "head", &s->head, - OBJ_PROP_FLAG_READ); - if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && - (console_type == GRAPHIC_CONSOLE))) { - active_console = s; + (console_type == GRAPHIC_CONSOLE))) { + active_console = c; } - s->ds = ds; - s->console_type = console_type; - s->window_id = -1; + c->console_type = console_type; if (QTAILQ_EMPTY(&consoles)) { - s->index = 0; - QTAILQ_INSERT_TAIL(&consoles, s, next); + c->index = 0; + QTAILQ_INSERT_TAIL(&consoles, c, next); } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) { QemuConsole *last = QTAILQ_LAST(&consoles); - s->index = last->index + 1; - QTAILQ_INSERT_TAIL(&consoles, s, next); + c->index = last->index + 1; + QTAILQ_INSERT_TAIL(&consoles, c, next); } else { /* * HACK: Put graphical consoles before text consoles. @@ -1293,41 +1279,75 @@ static QemuConsole *new_console(console_type_t console_type, uint32_t head) * Only do that for coldplugged devices. After initial device * initialization we will not renumber the consoles any more. */ - QemuConsole *c = QTAILQ_FIRST(&consoles); + QemuConsole *it = QTAILQ_FIRST(&consoles); - while (QTAILQ_NEXT(c, next) != NULL && - c->console_type == GRAPHIC_CONSOLE) { - c = QTAILQ_NEXT(c, next); + while (QTAILQ_NEXT(it, next) != NULL && + it->console_type == GRAPHIC_CONSOLE) { + it = QTAILQ_NEXT(it, next); } - if (c->console_type == GRAPHIC_CONSOLE) { + if (it->console_type == GRAPHIC_CONSOLE) { /* have no text consoles */ - s->index = c->index + 1; - QTAILQ_INSERT_AFTER(&consoles, c, s, next); + c->index = it->index + 1; + QTAILQ_INSERT_AFTER(&consoles, it, c, next); } else { - s->index = c->index; - QTAILQ_INSERT_BEFORE(c, s, next); + c->index = it->index; + QTAILQ_INSERT_BEFORE(it, c, next); /* renumber text consoles */ - for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) { - c->index = i; + for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) { + it->index = i; } } } - return s; } static void qemu_console_finalize(Object *obj) { + /* TODO: should unregister from consoles and free itself */ +} + +static void +qemu_console_prop_get_head(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QemuConsole *c = QEMU_CONSOLE(obj); + + visit_type_uint32(v, name, &c->head, errp); } static void qemu_console_class_init(ObjectClass *oc, void *data) { + object_class_property_add_link(oc, "device", TYPE_DEVICE, + offsetof(QemuConsole, device), + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + object_class_property_add(oc, "head", "uint32", + qemu_console_prop_get_head, + NULL, NULL, NULL); } static void qemu_console_init(Object *obj) { + QemuConsole *c = QEMU_CONSOLE(obj); + DisplayState *ds = get_alloc_displaystate(); + + qemu_co_queue_init(&c->dump_queue); + c->ds = ds; + c->window_id = -1; +} + +static QemuConsole *new_console(console_type_t console_type, + uint32_t head) +{ + QemuConsole *c = QEMU_CONSOLE(object_new(TYPE_QEMU_CONSOLE)); + + c->head = head; + /* TODO: move to console_init() once there is a type hierarchy */ + qemu_console_register(c, console_type); + + return c; } #ifdef WIN32 From b208f745a8af27344c7c8401560b312a4f4bd539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:53 +0400 Subject: [PATCH 19/52] ui/console: introduce different console objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Boilerplate code to introduce different object types for the different console types. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-20-marcandre.lureau@redhat.com> --- ui/console.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/ui/console.c b/ui/console.c index bdecfe7306..4ca5064cc9 100644 --- a/ui/console.c +++ b/ui/console.c @@ -128,6 +128,45 @@ struct QemuConsole { OBJECT_DEFINE_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT) +typedef struct QemuGraphicConsole { + QemuConsole parent; +} QemuGraphicConsole; + +typedef QemuConsoleClass QemuGraphicConsoleClass; + +#define TYPE_QEMU_GRAPHIC_CONSOLE "qemu-graphic-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuGraphicConsole, QEMU_GRAPHIC_CONSOLE) +OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOLE, QEMU_CONSOLE) + +#define QEMU_IS_GRAPHIC_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_GRAPHIC_CONSOLE) + +typedef struct QemuTextConsole { + QemuConsole parent; +} QemuTextConsole; + +typedef QemuConsoleClass QemuTextConsoleClass; + +#define TYPE_QEMU_TEXT_CONSOLE "qemu-text-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuTextConsole, QEMU_TEXT_CONSOLE) +OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE) + +#define QEMU_IS_TEXT_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_TEXT_CONSOLE) + +typedef struct QemuFixedTextConsole { + QemuTextConsole parent; +} QemuFixedTextConsole; + +typedef QemuTextConsoleClass QemuFixedTextConsoleClass; + +#define TYPE_QEMU_FIXED_TEXT_CONSOLE "qemu-fixed-text-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuFixedTextConsole, QEMU_FIXED_TEXT_CONSOLE) +OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE) + +#define QEMU_IS_FIXED_TEXT_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_FIXED_TEXT_CONSOLE) + struct VCChardev { Chardev parent; QemuConsole *console; @@ -1338,6 +1377,51 @@ qemu_console_init(Object *obj) c->window_id = -1; } +static void +qemu_graphic_console_finalize(Object *obj) +{ +} + +static void +qemu_graphic_console_class_init(ObjectClass *oc, void *data) +{ +} + +static void +qemu_graphic_console_init(Object *obj) +{ +} + +static void +qemu_text_console_finalize(Object *obj) +{ +} + +static void +qemu_text_console_class_init(ObjectClass *oc, void *data) +{ +} + +static void +qemu_text_console_init(Object *obj) +{ +} + +static void +qemu_fixed_text_console_finalize(Object *obj) +{ +} + +static void +qemu_fixed_text_console_class_init(ObjectClass *oc, void *data) +{ +} + +static void +qemu_fixed_text_console_init(Object *obj) +{ +} + static QemuConsole *new_console(console_type_t console_type, uint32_t head) { From c105d60f7fe912cca558ce5ff5680bfd0c1300fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:54 +0400 Subject: [PATCH 20/52] ui/console: instantiate a specific console type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow to move code/data to the specific console types. Replace console_type_t with object type check. QemuConsole can be abstract. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-21-marcandre.lureau@redhat.com> --- ui/console.c | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/ui/console.c b/ui/console.c index 4ca5064cc9..dd8e8db525 100644 --- a/ui/console.c +++ b/ui/console.c @@ -71,17 +71,10 @@ enum TTYState { TTY_STATE_CSI, }; -typedef enum { - GRAPHIC_CONSOLE, - TEXT_CONSOLE, - TEXT_CONSOLE_FIXED_SIZE -} console_type_t; - struct QemuConsole { Object parent; int index; - console_type_t console_type; DisplayState *ds; DisplaySurface *surface; DisplayScanout scanout; @@ -126,7 +119,7 @@ struct QemuConsole { QTAILQ_ENTRY(QemuConsole) next; }; -OBJECT_DEFINE_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT) +OBJECT_DEFINE_ABSTRACT_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT) typedef struct QemuGraphicConsole { QemuConsole parent; @@ -1156,7 +1149,7 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym) int c; uint32_t num_free; - if (!s || (s->console_type == GRAPHIC_CONSOLE)) + if (!s || QEMU_IS_GRAPHIC_CONSOLE(s)) return; switch(keysym) { @@ -1258,7 +1251,7 @@ static void text_console_invalidate(void *opaque) { QemuConsole *s = (QemuConsole *) opaque; - if (s->console_type == TEXT_CONSOLE) { + if (QEMU_IS_TEXT_CONSOLE(s) && !QEMU_IS_FIXED_TEXT_CONSOLE(s)) { text_console_resize(s); } console_refresh(s); @@ -1294,20 +1287,19 @@ static void text_console_update(void *opaque, console_ch_t *chardata) } static void -qemu_console_register(QemuConsole *c, console_type_t console_type) +qemu_console_register(QemuConsole *c) { int i; - if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && - (console_type == GRAPHIC_CONSOLE))) { + if (!active_console || (!QEMU_IS_GRAPHIC_CONSOLE(active_console) && + QEMU_IS_GRAPHIC_CONSOLE(c))) { active_console = c; } - c->console_type = console_type; if (QTAILQ_EMPTY(&consoles)) { c->index = 0; QTAILQ_INSERT_TAIL(&consoles, c, next); - } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) { + } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) { QemuConsole *last = QTAILQ_LAST(&consoles); c->index = last->index + 1; QTAILQ_INSERT_TAIL(&consoles, c, next); @@ -1320,11 +1312,10 @@ qemu_console_register(QemuConsole *c, console_type_t console_type) */ QemuConsole *it = QTAILQ_FIRST(&consoles); - while (QTAILQ_NEXT(it, next) != NULL && - it->console_type == GRAPHIC_CONSOLE) { + while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) { it = QTAILQ_NEXT(it, next); } - if (it->console_type == GRAPHIC_CONSOLE) { + if (QEMU_IS_GRAPHIC_CONSOLE(it)) { /* have no text consoles */ c->index = it->index + 1; QTAILQ_INSERT_AFTER(&consoles, it, c, next); @@ -1422,14 +1413,14 @@ qemu_fixed_text_console_init(Object *obj) { } -static QemuConsole *new_console(console_type_t console_type, +static QemuConsole *new_console(const char *typename, uint32_t head) { - QemuConsole *c = QEMU_CONSOLE(object_new(TYPE_QEMU_CONSOLE)); + QemuConsole *c = QEMU_CONSOLE(object_new(typename)); c->head = head; /* TODO: move to console_init() once there is a type hierarchy */ - qemu_console_register(c, console_type); + qemu_console_register(c); return c; } @@ -2211,7 +2202,7 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, height = qemu_console_get_height(s, 0); } else { trace_console_gfx_new(); - s = new_console(GRAPHIC_CONSOLE, head); + s = new_console(TYPE_QEMU_GRAPHIC_CONSOLE, head); s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, dpy_set_ui_info_timer, s); } @@ -2345,7 +2336,7 @@ bool qemu_console_is_graphic(QemuConsole *con) if (con == NULL) { con = active_console; } - return con && (con->console_type == GRAPHIC_CONSOLE); + return con && QEMU_IS_GRAPHIC_CONSOLE(con); } bool qemu_console_is_fixedsize(QemuConsole *con) @@ -2353,7 +2344,7 @@ bool qemu_console_is_fixedsize(QemuConsole *con) if (con == NULL) { con = active_console; } - return con && (con->console_type != TEXT_CONSOLE); + return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con)); } bool qemu_console_is_gl_blocked(QemuConsole *con) @@ -2389,7 +2380,7 @@ bool qemu_console_is_multihead(DeviceState *dev) char *qemu_console_get_label(QemuConsole *con) { - if (con->console_type == GRAPHIC_CONSOLE) { + if (QEMU_IS_GRAPHIC_CONSOLE(con)) { if (con->device) { DeviceState *dev; bool multihead; @@ -2588,9 +2579,9 @@ static void vc_chr_open(Chardev *chr, trace_console_txt_new(width, height); if (width == 0 || height == 0) { - s = new_console(TEXT_CONSOLE, 0); + s = new_console(TYPE_QEMU_TEXT_CONSOLE, 0); } else { - s = new_console(TEXT_CONSOLE_FIXED_SIZE, 0); + s = new_console(TYPE_QEMU_FIXED_TEXT_CONSOLE, 0); s->scanout.kind = SCANOUT_SURFACE; s->surface = qemu_create_displaysurface(width, height); } @@ -2610,7 +2601,7 @@ void qemu_console_resize(QemuConsole *s, int width, int height) { DisplaySurface *surface = qemu_console_surface(s); - assert(s->console_type == GRAPHIC_CONSOLE); + assert(QEMU_IS_GRAPHIC_CONSOLE(s)); if ((s->scanout.kind != SCANOUT_SURFACE || (surface && surface->flags & QEMU_ALLOCATED_FLAG)) && From ba0ec5c2931cd6efafc92bde3bd8fc3f99594fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:55 +0400 Subject: [PATCH 21/52] ui/console: register the console from qemu_console_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-22-marcandre.lureau@redhat.com> --- ui/console.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/console.c b/ui/console.c index dd8e8db525..02a24eaf5d 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1366,6 +1366,7 @@ qemu_console_init(Object *obj) qemu_co_queue_init(&c->dump_queue); c->ds = ds; c->window_id = -1; + qemu_console_register(c); } static void @@ -1419,8 +1420,6 @@ static QemuConsole *new_console(const char *typename, QemuConsole *c = QEMU_CONSOLE(object_new(typename)); c->head = head; - /* TODO: move to console_init() once there is a type hierarchy */ - qemu_console_register(c); return c; } From 34b7751574ebac7e19bfdb3ed0f91550c5ed171b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:56 +0400 Subject: [PATCH 22/52] ui/console: remove new_console() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The constructor helper isn't of much used now. "head" is only specified for graphic console (and default to 0), and we are going to move it to QemuGraphicConsole next. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-23-marcandre.lureau@redhat.com> --- ui/console.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/ui/console.c b/ui/console.c index 02a24eaf5d..e0e4f980d7 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1414,16 +1414,6 @@ qemu_fixed_text_console_init(Object *obj) { } -static QemuConsole *new_console(const char *typename, - uint32_t head) -{ - QemuConsole *c = QEMU_CONSOLE(object_new(typename)); - - c->head = head; - - return c; -} - #ifdef WIN32 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, HANDLE h, uint32_t offset) @@ -2163,7 +2153,7 @@ DisplayState *init_displaystate(void) QemuConsole *con; QTAILQ_FOREACH(con, &consoles, next) { - /* Hook up into the qom tree here (not in new_console()), once + /* Hook up into the qom tree here (not in object_new()), once * all QemuConsoles are created and the order / numbering * doesn't change any more */ name = g_strdup_printf("console[%d]", con->index); @@ -2201,7 +2191,8 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, height = qemu_console_get_height(s, 0); } else { trace_console_gfx_new(); - s = new_console(TYPE_QEMU_GRAPHIC_CONSOLE, head); + s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE); + s->head = head; s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, dpy_set_ui_info_timer, s); } @@ -2578,9 +2569,9 @@ static void vc_chr_open(Chardev *chr, trace_console_txt_new(width, height); if (width == 0 || height == 0) { - s = new_console(TYPE_QEMU_TEXT_CONSOLE, 0); + s = (QemuConsole *)object_new(TYPE_QEMU_TEXT_CONSOLE); } else { - s = new_console(TYPE_QEMU_FIXED_TEXT_CONSOLE, 0); + s = (QemuConsole *)object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE); s->scanout.kind = SCANOUT_SURFACE; s->surface = qemu_create_displaysurface(width, height); } From f9411aaebd99e1efb04f0d32f01b37467e43b6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:57 +0400 Subject: [PATCH 23/52] ui/console: specialize console_lookup_unused() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit graphics_console_init() is expected to return a graphic console. The function doesn't need to be exported. We are going to specialize further QemuGraphicConsole & QemuTextConsole. The two will not be interchangeable anymore. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-24-marcandre.lureau@redhat.com> --- include/ui/console.h | 1 - ui/console.c | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index 1b08b0f8ad..465f0f93a0 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -484,7 +484,6 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index); QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head); QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, uint32_t head, Error **errp); -QemuConsole *qemu_console_lookup_unused(void); QEMUCursor *qemu_console_get_cursor(QemuConsole *con); bool qemu_console_is_visible(QemuConsole *con); bool qemu_console_is_graphic(QemuConsole *con); diff --git a/ui/console.c b/ui/console.c index e0e4f980d7..08bed58b80 100644 --- a/ui/console.c +++ b/ui/console.c @@ -196,6 +196,7 @@ static void text_console_update_cursor(void *opaque); static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl); static bool console_compatible_with(QemuConsole *con, DisplayChangeListener *dcl, Error **errp); +static QemuConsole *qemu_graphic_console_lookup_unused(void); static void gui_update(void *opaque) { @@ -2184,7 +2185,7 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, QemuConsole *s; DisplaySurface *surface; - s = qemu_console_lookup_unused(); + s = qemu_graphic_console_lookup_unused(); if (s) { trace_console_gfx_reuse(s->index); width = qemu_console_get_width(s, 0); @@ -2289,13 +2290,13 @@ QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, return con; } -QemuConsole *qemu_console_lookup_unused(void) +static QemuConsole *qemu_graphic_console_lookup_unused(void) { QemuConsole *con; Object *obj; QTAILQ_FOREACH(con, &consoles, next) { - if (con->hw_ops != &unused_ops) { + if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) { continue; } obj = object_property_get_link(OBJECT(con), From 7fa4b8041b870951642515e0954d274ec4d599b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:58 +0400 Subject: [PATCH 24/52] ui/console: update the head from unused QemuConsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When recycling unused QemuConsole, we should still set the associated head number for correct information and lookups. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-25-marcandre.lureau@redhat.com> --- ui/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/console.c b/ui/console.c index 08bed58b80..a9a922b6e3 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2193,10 +2193,10 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, } else { trace_console_gfx_new(); s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE); - s->head = head; s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, dpy_set_ui_info_timer, s); } + s->head = head; graphic_console_set_hwops(s, hw_ops, opaque); if (dev) { object_property_set_link(OBJECT(s), "device", OBJECT(dev), From cfde05d15bbad620f87592edc2882611acbacc53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:37:59 +0400 Subject: [PATCH 25/52] ui/console: allocate ui_timer in QemuConsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although at this point only QemuGraphicConsole have hw_ops that implements ui_info() callback, it makes sense to keep the code in the base QemuConsole, to simplify conditions for the caller. As of now, the code didn't reach a NULL timer because dpy_set_ui_info() checks if dpy_ui_info_supported() (hw_ops->ui_info != NULL), which is false for text_console_ops. This is a bit fragile, let simply allocate and free the timer in the base class. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-26-marcandre.lureau@redhat.com> --- ui/console.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/console.c b/ui/console.c index a9a922b6e3..8c4a2c83fa 100644 --- a/ui/console.c +++ b/ui/console.c @@ -197,6 +197,7 @@ static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl); static bool console_compatible_with(QemuConsole *con, DisplayChangeListener *dcl, Error **errp); static QemuConsole *qemu_graphic_console_lookup_unused(void); +static void dpy_set_ui_info_timer(void *opaque); static void gui_update(void *opaque) { @@ -1334,6 +1335,9 @@ qemu_console_register(QemuConsole *c) static void qemu_console_finalize(Object *obj) { + QemuConsole *c = QEMU_CONSOLE(obj); + + g_clear_pointer(&c->ui_timer, timer_free); /* TODO: should unregister from consoles and free itself */ } @@ -1367,6 +1371,8 @@ qemu_console_init(Object *obj) qemu_co_queue_init(&c->dump_queue); c->ds = ds; c->window_id = -1; + c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, + dpy_set_ui_info_timer, c); qemu_console_register(c); } @@ -2193,8 +2199,6 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, } else { trace_console_gfx_new(); s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE); - s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, - dpy_set_ui_info_timer, s); } s->head = head; graphic_console_set_hwops(s, hw_ops, opaque); From b97a76d0355f8fc3856de9ebd4f6929b51ba26fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:00 +0400 Subject: [PATCH 26/52] ui/vc: move cursor_timer initialization to QemuTextConsole class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timer is only relevant when a text console exists. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-27-marcandre.lureau@redhat.com> --- ui/console.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/console.c b/ui/console.c index 8c4a2c83fa..ffa68c3a22 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1399,6 +1399,10 @@ qemu_text_console_finalize(Object *obj) static void qemu_text_console_class_init(ObjectClass *oc, void *data) { + if (!cursor_timer) { + cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, + text_console_update_cursor, NULL); + } } static void @@ -2144,8 +2148,6 @@ static DisplayState *get_alloc_displaystate(void) { if (!display_state) { display_state = g_new0(DisplayState, 1); - cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, - text_console_update_cursor, NULL); } return display_state; } From 463c6b19c75313734e6e1b624d6b89dd8eb62516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:01 +0400 Subject: [PATCH 27/52] ui/console: free more QemuConsole resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code path is probably not executed at this point, since console aren't being released. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-28-marcandre.lureau@redhat.com> --- ui/console.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/console.c b/ui/console.c index ffa68c3a22..3cd4c74eec 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1337,8 +1337,11 @@ qemu_console_finalize(Object *obj) { QemuConsole *c = QEMU_CONSOLE(obj); + /* TODO: check this code path, and unregister from consoles */ + g_clear_pointer(&c->device, object_unref); + g_clear_pointer(&c->surface, qemu_free_displaysurface); + g_clear_pointer(&c->gl_unblock_timer, timer_free); g_clear_pointer(&c->ui_timer, timer_free); - /* TODO: should unregister from consoles and free itself */ } static void From b2bb9cc43dbb942a5333a6271629fd6094771bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:02 +0400 Subject: [PATCH 28/52] ui/vc: move text fields to QemuTextConsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we can instantiate the specific console with its own fields. Pass the most appropriate type to the various functions, and cast up to QEMU_CONSOLE as necessary. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-29-marcandre.lureau@redhat.com> --- ui/console.c | 177 ++++++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 86 deletions(-) diff --git a/ui/console.c b/ui/console.c index 3cd4c74eec..335d7ba841 100644 --- a/ui/console.c +++ b/ui/console.c @@ -94,26 +94,6 @@ struct QemuConsole { const GraphicHwOps *hw_ops; void *hw; - /* Text console state */ - int width; - int height; - int total_height; - int backscroll_height; - int x, y; - int y_displayed; - int y_base; - TextCell *cells; - int text_x[2], text_y[2], cursor_invalidate; - int echo; - - int update_x0; - int update_y0; - int update_x1; - int update_y1; - - Chardev *chr; - /* fifo for key pressed */ - Fifo8 out_fifo; CoQueue dump_queue; QTAILQ_ENTRY(QemuConsole) next; @@ -136,6 +116,26 @@ OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOL typedef struct QemuTextConsole { QemuConsole parent; + + int width; + int height; + int total_height; + int backscroll_height; + int x, y; + int y_displayed; + int y_base; + TextCell *cells; + int text_x[2], text_y[2], cursor_invalidate; + int echo; + + int update_x0; + int update_y0; + int update_x1; + int update_y1; + + Chardev *chr; + /* fifo for key pressed */ + Fifo8 out_fifo; } QemuTextConsole; typedef QemuConsoleClass QemuTextConsoleClass; @@ -162,7 +162,7 @@ OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEX struct VCChardev { Chardev parent; - QemuConsole *console; + QemuTextConsole *console; enum TTYState state; int esc_params[MAX_ESC_PARAMS]; @@ -428,43 +428,44 @@ static void vga_putcharxy(QemuConsole *s, int x, int y, int ch, &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT); } -static void text_console_resize(QemuConsole *s) +static void text_console_resize(QemuTextConsole *t) { + QemuConsole *s = QEMU_CONSOLE(t); TextCell *cells, *c, *c1; int w1, x, y, last_width; assert(s->scanout.kind == SCANOUT_SURFACE); - last_width = s->width; - s->width = surface_width(s->surface) / FONT_WIDTH; - s->height = surface_height(s->surface) / FONT_HEIGHT; + last_width = t->width; + t->width = surface_width(s->surface) / FONT_WIDTH; + t->height = surface_height(s->surface) / FONT_HEIGHT; w1 = last_width; - if (s->width < w1) - w1 = s->width; + if (t->width < w1) + w1 = t->width; - cells = g_new(TextCell, s->width * s->total_height + 1); - for(y = 0; y < s->total_height; y++) { - c = &cells[y * s->width]; + cells = g_new(TextCell, t->width * t->total_height + 1); + for(y = 0; y < t->total_height; y++) { + c = &cells[y * t->width]; if (w1 > 0) { - c1 = &s->cells[y * last_width]; + c1 = &t->cells[y * last_width]; for(x = 0; x < w1; x++) { *c++ = *c1++; } } - for(x = w1; x < s->width; x++) { + for(x = w1; x < t->width; x++) { c->ch = ' '; c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; c++; } } - g_free(s->cells); - s->cells = cells; + g_free(t->cells); + t->cells = cells; } -static void invalidate_xy(QemuConsole *s, int x, int y) +static void invalidate_xy(QemuTextConsole *s, int x, int y) { - if (!qemu_console_is_visible(s)) { + if (!qemu_console_is_visible(QEMU_CONSOLE(s))) { return; } if (s->update_x0 > x * FONT_WIDTH) @@ -479,7 +480,7 @@ static void invalidate_xy(QemuConsole *s, int x, int y) static void vc_update_xy(VCChardev *vc, int x, int y) { - QemuConsole *s = vc->console; + QemuTextConsole *s = vc->console; TextCell *c; int y1, y2; @@ -498,13 +499,13 @@ static void vc_update_xy(VCChardev *vc, int x, int y) x = s->width - 1; } c = &s->cells[y1 * s->width + x]; - vga_putcharxy(s, x, y2, c->ch, + vga_putcharxy(QEMU_CONSOLE(s), x, y2, c->ch, &(c->t_attrib)); invalidate_xy(s, x, y2); } } -static void console_show_cursor(QemuConsole *s, int show) +static void console_show_cursor(QemuTextConsole *s, int show) { TextCell *c; int y, y1; @@ -525,17 +526,17 @@ static void console_show_cursor(QemuConsole *s, int show) if (show && cursor_visible_phase) { TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT; t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ - vga_putcharxy(s, x, y, c->ch, &t_attrib); + vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &t_attrib); } else { - vga_putcharxy(s, x, y, c->ch, &(c->t_attrib)); + vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &(c->t_attrib)); } invalidate_xy(s, x, y); } } -static void console_refresh(QemuConsole *s) +static void console_refresh(QemuTextConsole *s) { - DisplaySurface *surface = qemu_console_surface(s); + DisplaySurface *surface = qemu_console_surface(QEMU_CONSOLE(s)); TextCell *c; int x, y, y1; @@ -545,13 +546,13 @@ static void console_refresh(QemuConsole *s) s->text_y[1] = s->height - 1; s->cursor_invalidate = 1; - vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), + vga_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface), color_table_rgb[0][QEMU_COLOR_BLACK]); y1 = s->y_displayed; for (y = 0; y < s->height; y++) { c = s->cells + y1 * s->width; for (x = 0; x < s->width; x++) { - vga_putcharxy(s, x, y, c->ch, + vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &(c->t_attrib)); c++; } @@ -560,11 +561,11 @@ static void console_refresh(QemuConsole *s) } } console_show_cursor(s, 1); - dpy_gfx_update(s, 0, 0, + dpy_gfx_update(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface)); } -static void console_scroll(QemuConsole *s, int ydelta) +static void console_scroll(QemuTextConsole *s, int ydelta) { int i, y1; @@ -595,7 +596,7 @@ static void console_scroll(QemuConsole *s, int ydelta) static void vc_put_lf(VCChardev *vc) { - QemuConsole *s = vc->console; + QemuTextConsole *s = vc->console; TextCell *c; int x, y1; @@ -624,10 +625,10 @@ static void vc_put_lf(VCChardev *vc) s->text_x[1] = s->width - 1; s->text_y[1] = s->height - 1; - vga_bitblt(s, 0, FONT_HEIGHT, 0, 0, + vga_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0, s->width * FONT_WIDTH, (s->height - 1) * FONT_HEIGHT); - vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT, + vga_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT, s->width * FONT_WIDTH, FONT_HEIGHT, color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]); s->update_x0 = 0; @@ -737,7 +738,7 @@ static void vc_handle_escape(VCChardev *vc) static void vc_clear_xy(VCChardev *vc, int x, int y) { - QemuConsole *s = vc->console; + QemuTextConsole *s = vc->console; int y1 = (s->y_base + y) % s->total_height; if (x >= s->width) { x = s->width - 1; @@ -750,7 +751,7 @@ static void vc_clear_xy(VCChardev *vc, int x, int y) static void vc_put_one(VCChardev *vc, int ch) { - QemuConsole *s = vc->console; + QemuTextConsole *s = vc->console; TextCell *c; int y1; if (s->x >= s->width) { @@ -777,7 +778,7 @@ static void vc_respond_str(VCChardev *vc, const char *buf) /* set cursor, checking bounds */ static void vc_set_cursor(VCChardev *vc, int x, int y) { - QemuConsole *s = vc->console; + QemuTextConsole *s = vc->console; if (x < 0) { x = 0; @@ -798,7 +799,7 @@ static void vc_set_cursor(VCChardev *vc, int x, int y) static void vc_putchar(VCChardev *vc, int ch) { - QemuConsole *s = vc->console; + QemuTextConsole *s = vc->console; int i; int x, y; char response[40]; @@ -1095,8 +1096,11 @@ void console_select(unsigned int index) } displaychangelistener_display_console(dcl, s, NULL); } - dpy_text_resize(s, s->width, s->height); - text_console_update_cursor(NULL); + + if (QEMU_IS_TEXT_CONSOLE(s)) { + dpy_text_resize(s, QEMU_TEXT_CONSOLE(s)->width, QEMU_TEXT_CONSOLE(s)->height); + text_console_update_cursor(NULL); + } } } @@ -1107,7 +1111,7 @@ DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV, static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) { VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s = drv->console; + QemuTextConsole *s = drv->console; int i; s->update_x0 = s->width * FONT_WIDTH; @@ -1120,14 +1124,14 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) } console_show_cursor(s, 1); if (s->update_x0 < s->update_x1) { - dpy_gfx_update(s, s->update_x0, s->update_y0, + dpy_gfx_update(QEMU_CONSOLE(s), s->update_x0, s->update_y0, s->update_x1 - s->update_x0, s->update_y1 - s->update_y0); } return len; } -static void kbd_send_chars(QemuConsole *s) +static void kbd_send_chars(QemuTextConsole *s) { uint32_t len, avail; @@ -1145,13 +1149,14 @@ static void kbd_send_chars(QemuConsole *s) } /* called when an ascii key is pressed */ -void kbd_put_keysym_console(QemuConsole *s, int keysym) +void kbd_put_keysym_console(QemuConsole *con, int keysym) { + QemuTextConsole *s = (QemuTextConsole *)object_dynamic_cast(OBJECT(con), TYPE_QEMU_TEXT_CONSOLE); uint8_t buf[16], *q; int c; uint32_t num_free; - if (!s || QEMU_IS_GRAPHIC_CONSOLE(s)) + if (!s) return; switch(keysym) { @@ -1251,17 +1256,17 @@ void kbd_put_keysym(int keysym) static void text_console_invalidate(void *opaque) { - QemuConsole *s = (QemuConsole *) opaque; + QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque); - if (QEMU_IS_TEXT_CONSOLE(s) && !QEMU_IS_FIXED_TEXT_CONSOLE(s)) { - text_console_resize(s); + if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) { + text_console_resize(QEMU_TEXT_CONSOLE(s)); } console_refresh(s); } static void text_console_update(void *opaque, console_ch_t *chardata) { - QemuConsole *s = (QemuConsole *) opaque; + QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque); int i, j, src; if (s->text_x[0] <= s->text_x[1]) { @@ -1275,7 +1280,7 @@ static void text_console_update(void *opaque, console_ch_t *chardata) s->cells[src].t_attrib.bgcol, s->cells[src].t_attrib.bold)); } - dpy_text_update(s, s->text_x[0], s->text_y[0], + dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0], s->text_x[1] - s->text_x[0], i - s->text_y[0]); s->text_x[0] = s->width; s->text_y[0] = s->height; @@ -1283,7 +1288,7 @@ static void text_console_update(void *opaque, console_ch_t *chardata) s->text_y[1] = 0; } if (s->cursor_invalidate) { - dpy_text_cursor(s, s->x, s->y); + dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y); s->cursor_invalidate = 0; } } @@ -2399,12 +2404,14 @@ char *qemu_console_get_label(QemuConsole *con) } } return g_strdup("VGA"); - } else { - if (con->chr && con->chr->label) { - return g_strdup(con->chr->label); + } else if (QEMU_IS_TEXT_CONSOLE(con)) { + QemuTextConsole *c = QEMU_TEXT_CONSOLE(con); + if (c->chr && c->chr->label) { + return g_strdup(c->chr->label); } - return g_strdup_printf("vc%d", con->index); } + + return g_strdup_printf("vc%d", con->index); } int qemu_console_get_index(QemuConsole *con) @@ -2466,17 +2473,15 @@ int qemu_console_get_height(QemuConsole *con, int fallback) static void vc_chr_accept_input(Chardev *chr) { VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s = drv->console; - kbd_send_chars(s); + kbd_send_chars(drv->console); } static void vc_chr_set_echo(Chardev *chr, bool echo) { VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s = drv->console; - s->echo = echo; + drv->console->echo = echo; } static void text_console_update_cursor_timer(void) @@ -2514,7 +2519,7 @@ static const GraphicHwOps text_console_ops = { static void text_console_do_init(Chardev *chr) { VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s = drv->console; + QemuTextConsole *s = drv->console; int g_width = 80 * FONT_WIDTH; int g_height = 24 * FONT_HEIGHT; @@ -2525,17 +2530,17 @@ static void text_console_do_init(Chardev *chr) s->total_height = DEFAULT_BACKSCROLL; s->x = 0; s->y = 0; - if (s->scanout.kind != SCANOUT_SURFACE) { + if (QEMU_CONSOLE(s)->scanout.kind != SCANOUT_SURFACE) { if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) { g_width = qemu_console_get_width(active_console, g_width); g_height = qemu_console_get_height(active_console, g_height); } - s->surface = qemu_create_displaysurface(g_width, g_height); - s->scanout.kind = SCANOUT_SURFACE; + QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(g_width, g_height); + QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; } - s->hw_ops = &text_console_ops; - s->hw = s; + QEMU_CONSOLE(s)->hw_ops = &text_console_ops; + QEMU_CONSOLE(s)->hw = s; /* set current text attributes to default */ drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; @@ -2561,7 +2566,7 @@ static void vc_chr_open(Chardev *chr, { ChardevVC *vc = backend->u.vc.data; VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s; + QemuTextConsole *s; unsigned width = 0; unsigned height = 0; @@ -2579,11 +2584,11 @@ static void vc_chr_open(Chardev *chr, trace_console_txt_new(width, height); if (width == 0 || height == 0) { - s = (QemuConsole *)object_new(TYPE_QEMU_TEXT_CONSOLE); + s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE)); } else { - s = (QemuConsole *)object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE); - s->scanout.kind = SCANOUT_SURFACE; - s->surface = qemu_create_displaysurface(width, height); + s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE)); + QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; + QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(width, height); } s->chr = chr; From 58d5870845c61cea1e7df287b86c2607b2bf48a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:03 +0400 Subject: [PATCH 29/52] ui/console: move graphic fields to QemuGraphicConsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move fields specific to graphic console to the console subclass. qemu_console_get_head() is adapated to accomodate QemuTextConsole, and always returns 0. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-30-marcandre.lureau@redhat.com> --- ui/console.c | 110 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 46 deletions(-) diff --git a/ui/console.c b/ui/console.c index 335d7ba841..9d884ba02f 100644 --- a/ui/console.c +++ b/ui/console.c @@ -83,17 +83,10 @@ struct QemuConsole { int gl_block; QEMUTimer *gl_unblock_timer; int window_id; - - /* Graphic console state. */ - Object *device; - uint32_t head; QemuUIInfo ui_info; QEMUTimer *ui_timer; - QEMUCursor *cursor; - int cursor_x, cursor_y, cursor_on; const GraphicHwOps *hw_ops; void *hw; - CoQueue dump_queue; QTAILQ_ENTRY(QemuConsole) next; @@ -103,6 +96,12 @@ OBJECT_DEFINE_ABSTRACT_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT) typedef struct QemuGraphicConsole { QemuConsole parent; + + Object *device; + uint32_t head; + + QEMUCursor *cursor; + int cursor_x, cursor_y, cursor_on; } QemuGraphicConsole; typedef QemuConsoleClass QemuGraphicConsoleClass; @@ -1343,31 +1342,14 @@ qemu_console_finalize(Object *obj) QemuConsole *c = QEMU_CONSOLE(obj); /* TODO: check this code path, and unregister from consoles */ - g_clear_pointer(&c->device, object_unref); g_clear_pointer(&c->surface, qemu_free_displaysurface); g_clear_pointer(&c->gl_unblock_timer, timer_free); g_clear_pointer(&c->ui_timer, timer_free); } -static void -qemu_console_prop_get_head(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QemuConsole *c = QEMU_CONSOLE(obj); - - visit_type_uint32(v, name, &c->head, errp); -} - static void qemu_console_class_init(ObjectClass *oc, void *data) { - object_class_property_add_link(oc, "device", TYPE_DEVICE, - offsetof(QemuConsole, device), - object_property_allow_set_link, - OBJ_PROP_LINK_STRONG); - object_class_property_add(oc, "head", "uint32", - qemu_console_prop_get_head, - NULL, NULL, NULL); } static void @@ -1387,11 +1369,30 @@ qemu_console_init(Object *obj) static void qemu_graphic_console_finalize(Object *obj) { + QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj); + + g_clear_pointer(&c->device, object_unref); +} + +static void +qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj); + + visit_type_uint32(v, name, &c->head, errp); } static void qemu_graphic_console_class_init(ObjectClass *oc, void *data) { + object_class_property_add_link(oc, "device", TYPE_DEVICE, + offsetof(QemuGraphicConsole, device), + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + object_class_property_add(oc, "head", "uint32", + qemu_graphic_console_prop_get_head, + NULL, NULL, NULL); } static void @@ -1676,6 +1677,16 @@ void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) con->gl = gl; } +static void +dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con) +{ + if (con && con->cursor && dcl->ops->dpy_cursor_define) { + dcl->ops->dpy_cursor_define(dcl, con->cursor); + } + if (con && dcl->ops->dpy_mouse_set) { + dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on); + } +} void register_displaychangelistener(DisplayChangeListener *dcl) { QemuConsole *con; @@ -1693,11 +1704,8 @@ void register_displaychangelistener(DisplayChangeListener *dcl) con = active_console; } displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL); - if (con && con->cursor && dcl->ops->dpy_cursor_define) { - dcl->ops->dpy_cursor_define(dcl, con->cursor); - } - if (con && dcl->ops->dpy_mouse_set) { - dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on); + if (QEMU_IS_GRAPHIC_CONSOLE(con)) { + dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(con)); } text_console_update_cursor(NULL); } @@ -1728,8 +1736,9 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl) static void dpy_set_ui_info_timer(void *opaque) { QemuConsole *con = opaque; + uint32_t head = qemu_console_get_head(con); - con->hw_ops->ui_info(con->hw, con->head, &con->ui_info); + con->hw_ops->ui_info(con->hw, head, &con->ui_info); } bool dpy_ui_info_supported(QemuConsole *con) @@ -1939,19 +1948,20 @@ void dpy_text_resize(QemuConsole *con, int w, int h) } } -void dpy_mouse_set(QemuConsole *con, int x, int y, int on) +void dpy_mouse_set(QemuConsole *c, int x, int y, int on) { - DisplayState *s = con->ds; + QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); + DisplayState *s = c->ds; DisplayChangeListener *dcl; con->cursor_x = x; con->cursor_y = y; con->cursor_on = on; - if (!qemu_console_is_visible(con)) { + if (!qemu_console_is_visible(c)) { return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (c != (dcl->con ? dcl->con : active_console)) { continue; } if (dcl->ops->dpy_mouse_set) { @@ -1960,18 +1970,19 @@ void dpy_mouse_set(QemuConsole *con, int x, int y, int on) } } -void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) +void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor) { - DisplayState *s = con->ds; + QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); + DisplayState *s = c->ds; DisplayChangeListener *dcl; cursor_unref(con->cursor); con->cursor = cursor_ref(cursor); - if (!qemu_console_is_visible(con)) { + if (!qemu_console_is_visible(c)) { return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (c != (dcl->con ? dcl->con : active_console)) { continue; } if (dcl->ops->dpy_cursor_define) { @@ -2210,7 +2221,7 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, trace_console_gfx_new(); s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE); } - s->head = head; + QEMU_GRAPHIC_CONSOLE(s)->head = head; graphic_console_set_hwops(s, hw_ops, opaque); if (dev) { object_property_set_link(OBJECT(s), "device", OBJECT(dev), @@ -2328,7 +2339,7 @@ QEMUCursor *qemu_console_get_cursor(QemuConsole *con) if (con == NULL) { con = active_console; } - return con ? con->cursor : NULL; + return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL; } bool qemu_console_is_visible(QemuConsole *con) @@ -2386,21 +2397,22 @@ bool qemu_console_is_multihead(DeviceState *dev) char *qemu_console_get_label(QemuConsole *con) { if (QEMU_IS_GRAPHIC_CONSOLE(con)) { - if (con->device) { + QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con); + if (c->device) { DeviceState *dev; bool multihead; - dev = DEVICE(con->device); + dev = DEVICE(c->device); multihead = qemu_console_is_multihead(dev); if (multihead) { return g_strdup_printf("%s.%d", dev->id ? dev->id : - object_get_typename(con->device), - con->head); + object_get_typename(c->device), + c->head); } else { return g_strdup_printf("%s", dev->id ? dev->id : - object_get_typename(con->device)); + object_get_typename(c->device)); } } return g_strdup("VGA"); @@ -2427,7 +2439,13 @@ uint32_t qemu_console_get_head(QemuConsole *con) if (con == NULL) { con = active_console; } - return con ? con->head : -1; + if (con == NULL) { + return -1; + } + if (QEMU_IS_GRAPHIC_CONSOLE(con)) { + return QEMU_GRAPHIC_CONSOLE(con)->head; + } + return 0; } int qemu_console_get_width(QemuConsole *con, int fallback) From 98ee9dab81b2bc75d6ccf86681053ed80f9fc9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:04 +0400 Subject: [PATCH 30/52] ui/vc: fold text_console_do_init() in vc_chr_open() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20230830093843.3531473-31-marcandre.lureau@redhat.com> --- ui/console.c | 84 +++++++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 51 deletions(-) diff --git a/ui/console.c b/ui/console.c index 9d884ba02f..22505d093f 100644 --- a/ui/console.c +++ b/ui/console.c @@ -187,7 +187,6 @@ static QTAILQ_HEAD(, QemuConsole) consoles = static bool cursor_visible_phase; static QEMUTimer *cursor_timer; -static void text_console_do_init(Chardev *chr); static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); @@ -2534,20 +2533,46 @@ static const GraphicHwOps text_console_ops = { .text_update = text_console_update, }; -static void text_console_do_init(Chardev *chr) +static void vc_chr_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { + ChardevVC *vc = backend->u.vc.data; VCChardev *drv = VC_CHARDEV(chr); - QemuTextConsole *s = drv->console; + QemuTextConsole *s; + unsigned width = 0; + unsigned height = 0; int g_width = 80 * FONT_WIDTH; int g_height = 24 * FONT_HEIGHT; + if (vc->has_width) { + width = vc->width; + } else if (vc->has_cols) { + width = vc->cols * FONT_WIDTH; + } + + if (vc->has_height) { + height = vc->height; + } else if (vc->has_rows) { + height = vc->rows * FONT_HEIGHT; + } + + trace_console_txt_new(width, height); + if (width == 0 || height == 0) { + s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE)); + } else { + s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE)); + QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; + QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(width, height); + } + + s->chr = chr; + drv->console = s; + fifo8_create(&s->out_fifo, 16); - s->y_displayed = 0; - s->y_base = 0; s->total_height = DEFAULT_BACKSCROLL; - s->x = 0; - s->y = 0; if (QEMU_CONSOLE(s)->scanout.kind != SCANOUT_SURFACE) { if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) { g_width = qemu_console_get_width(active_console, g_width); @@ -2574,50 +2599,7 @@ static void text_console_do_init(Chardev *chr) drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; } - qemu_chr_be_event(chr, CHR_EVENT_OPENED); -} - -static void vc_chr_open(Chardev *chr, - ChardevBackend *backend, - bool *be_opened, - Error **errp) -{ - ChardevVC *vc = backend->u.vc.data; - VCChardev *drv = VC_CHARDEV(chr); - QemuTextConsole *s; - unsigned width = 0; - unsigned height = 0; - - if (vc->has_width) { - width = vc->width; - } else if (vc->has_cols) { - width = vc->cols * FONT_WIDTH; - } - - if (vc->has_height) { - height = vc->height; - } else if (vc->has_rows) { - height = vc->rows * FONT_HEIGHT; - } - - trace_console_txt_new(width, height); - if (width == 0 || height == 0) { - s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE)); - } else { - s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE)); - QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; - QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(width, height); - } - - s->chr = chr; - drv->console = s; - - text_console_do_init(chr); - - /* console/chardev init sometimes completes elsewhere in a 2nd - * stage, so defer OPENED events until they are fully initialized - */ - *be_opened = false; + *be_opened = true; } void qemu_console_resize(QemuConsole *s, int width, int height) From 60cb14b4f9d94d750640f42e0f18a1710b8d6c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:05 +0400 Subject: [PATCH 31/52] ui/vc: move some text console initialization to qom handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-32-marcandre.lureau@redhat.com> --- ui/console.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ui/console.c b/ui/console.c index 22505d093f..5d521ba79d 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1413,9 +1413,20 @@ qemu_text_console_class_init(ObjectClass *oc, void *data) } } +static const GraphicHwOps text_console_ops = { + .invalidate = text_console_invalidate, + .text_update = text_console_update, +}; + static void qemu_text_console_init(Object *obj) { + QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj); + + fifo8_create(&c->out_fifo, 16); + c->total_height = DEFAULT_BACKSCROLL; + QEMU_CONSOLE(c)->hw_ops = &text_console_ops; + QEMU_CONSOLE(c)->hw = c; } static void @@ -2528,11 +2539,6 @@ static void text_console_update_cursor(void *opaque) } } -static const GraphicHwOps text_console_ops = { - .invalidate = text_console_invalidate, - .text_update = text_console_update, -}; - static void vc_chr_open(Chardev *chr, ChardevBackend *backend, bool *be_opened, @@ -2570,9 +2576,6 @@ static void vc_chr_open(Chardev *chr, s->chr = chr; drv->console = s; - fifo8_create(&s->out_fifo, 16); - - s->total_height = DEFAULT_BACKSCROLL; if (QEMU_CONSOLE(s)->scanout.kind != SCANOUT_SURFACE) { if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) { g_width = qemu_console_get_width(active_console, g_width); @@ -2582,9 +2585,6 @@ static void vc_chr_open(Chardev *chr, QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; } - QEMU_CONSOLE(s)->hw_ops = &text_console_ops; - QEMU_CONSOLE(s)->hw = s; - /* set current text attributes to default */ drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; text_console_resize(s); From 5e5a30b7d17f207a85af167ba3efdeff2b1f61de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:06 +0400 Subject: [PATCH 32/52] ui/console: simplify getting active_console size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can get the active console dimension regardless of its kind, by simply giving NULL as argument. It will fallback with the given value when the dimensions aren't known. This will also allow to move the code in a separate unit more easily. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-33-marcandre.lureau@redhat.com> --- ui/console.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ui/console.c b/ui/console.c index 5d521ba79d..70e11f924d 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2577,10 +2577,8 @@ static void vc_chr_open(Chardev *chr, drv->console = s; if (QEMU_CONSOLE(s)->scanout.kind != SCANOUT_SURFACE) { - if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) { - g_width = qemu_console_get_width(active_console, g_width); - g_height = qemu_console_get_height(active_console, g_height); - } + g_width = qemu_console_get_width(NULL, g_width); + g_height = qemu_console_get_height(NULL, g_height); QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(g_width, g_height); QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; } From 6ce7b1fa8844db668f0a3c0b37b78b08d331a16a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:07 +0400 Subject: [PATCH 33/52] ui/console: remove need for g_width/g_height MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-34-marcandre.lureau@redhat.com> --- ui/console.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ui/console.c b/ui/console.c index 70e11f924d..a3fd1c5059 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2549,8 +2549,6 @@ static void vc_chr_open(Chardev *chr, QemuTextConsole *s; unsigned width = 0; unsigned height = 0; - int g_width = 80 * FONT_WIDTH; - int g_height = 24 * FONT_HEIGHT; if (vc->has_width) { width = vc->width; @@ -2567,6 +2565,8 @@ static void vc_chr_open(Chardev *chr, trace_console_txt_new(width, height); if (width == 0 || height == 0) { s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE)); + width = qemu_console_get_width(NULL, 80 * FONT_WIDTH); + height = qemu_console_get_height(NULL, 24 * FONT_HEIGHT); } else { s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE)); QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; @@ -2577,9 +2577,7 @@ static void vc_chr_open(Chardev *chr, drv->console = s; if (QEMU_CONSOLE(s)->scanout.kind != SCANOUT_SURFACE) { - g_width = qemu_console_get_width(NULL, g_width); - g_height = qemu_console_get_height(NULL, g_height); - QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(g_width, g_height); + QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(width, height); QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; } From 742a6896ea1b83894e68b2dc455b63cea498bafc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:08 +0400 Subject: [PATCH 34/52] ui/vc: use common text console surface creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-35-marcandre.lureau@redhat.com> --- ui/console.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ui/console.c b/ui/console.c index a3fd1c5059..3d884956b7 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2569,18 +2569,13 @@ static void vc_chr_open(Chardev *chr, height = qemu_console_get_height(NULL, 24 * FONT_HEIGHT); } else { s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE)); - QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; - QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(width, height); } + dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height)); + s->chr = chr; drv->console = s; - if (QEMU_CONSOLE(s)->scanout.kind != SCANOUT_SURFACE) { - QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(width, height); - QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE; - } - /* set current text attributes to default */ drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; text_console_resize(s); From 8c6381d84668ff9b6324bf00b91107cbcaf7505f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:09 +0400 Subject: [PATCH 35/52] ui/console: declare console types in console.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to split the console.c unit next, and implement separately. But we need to check the underlying type in various places. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-36-marcandre.lureau@redhat.com> --- include/ui/console.h | 25 +++++++++++++++++++++---- ui/console.c | 15 --------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index 465f0f93a0..0f7f50deaf 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -12,6 +12,27 @@ # include "ui/shader.h" #endif +#define TYPE_QEMU_CONSOLE "qemu-console" +OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE) + +#define TYPE_QEMU_GRAPHIC_CONSOLE "qemu-graphic-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuGraphicConsole, QEMU_GRAPHIC_CONSOLE) + +#define TYPE_QEMU_TEXT_CONSOLE "qemu-text-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuTextConsole, QEMU_TEXT_CONSOLE) + +#define TYPE_QEMU_FIXED_TEXT_CONSOLE "qemu-fixed-text-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuFixedTextConsole, QEMU_FIXED_TEXT_CONSOLE) + +#define QEMU_IS_GRAPHIC_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_GRAPHIC_CONSOLE) + +#define QEMU_IS_TEXT_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_TEXT_CONSOLE) + +#define QEMU_IS_FIXED_TEXT_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_FIXED_TEXT_CONSOLE) + /* keyboard/mouse support */ #define MOUSE_EVENT_LBUTTON 0x01 @@ -112,10 +133,6 @@ void console_handle_touch_event(QemuConsole *con, Error **errp); /* consoles */ -#define TYPE_QEMU_CONSOLE "qemu-console" -OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE) - - struct QemuConsoleClass { ObjectClass parent_class; }; diff --git a/ui/console.c b/ui/console.c index 3d884956b7..ed9e7137b8 100644 --- a/ui/console.c +++ b/ui/console.c @@ -106,13 +106,8 @@ typedef struct QemuGraphicConsole { typedef QemuConsoleClass QemuGraphicConsoleClass; -#define TYPE_QEMU_GRAPHIC_CONSOLE "qemu-graphic-console" -OBJECT_DECLARE_SIMPLE_TYPE(QemuGraphicConsole, QEMU_GRAPHIC_CONSOLE) OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOLE, QEMU_CONSOLE) -#define QEMU_IS_GRAPHIC_CONSOLE(c) \ - object_dynamic_cast(OBJECT(c), TYPE_QEMU_GRAPHIC_CONSOLE) - typedef struct QemuTextConsole { QemuConsole parent; @@ -139,26 +134,16 @@ typedef struct QemuTextConsole { typedef QemuConsoleClass QemuTextConsoleClass; -#define TYPE_QEMU_TEXT_CONSOLE "qemu-text-console" -OBJECT_DECLARE_SIMPLE_TYPE(QemuTextConsole, QEMU_TEXT_CONSOLE) OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE) -#define QEMU_IS_TEXT_CONSOLE(c) \ - object_dynamic_cast(OBJECT(c), TYPE_QEMU_TEXT_CONSOLE) - typedef struct QemuFixedTextConsole { QemuTextConsole parent; } QemuFixedTextConsole; typedef QemuTextConsoleClass QemuFixedTextConsoleClass; -#define TYPE_QEMU_FIXED_TEXT_CONSOLE "qemu-fixed-text-console" -OBJECT_DECLARE_SIMPLE_TYPE(QemuFixedTextConsole, QEMU_FIXED_TEXT_CONSOLE) OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE) -#define QEMU_IS_FIXED_TEXT_CONSOLE(c) \ - object_dynamic_cast(OBJECT(c), TYPE_QEMU_FIXED_TEXT_CONSOLE) - struct VCChardev { Chardev parent; QemuTextConsole *console; From 1ece6777fe1770f8a26f6887be96b21edfd0e442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:10 +0400 Subject: [PATCH 36/52] ui/console: use QEMU_PIXMAN_COLOR helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU_RGB macro is actually defining a pixman color. Make this explicit in the macro name. Move it to qemu-pixman.h so it can be used elsewhere, as done in the following patch. Finally, define QEMU_PIXMAN_COLOR_{BLACK,GRAY}, to avoid need to look up the VGA color table from the QemuConsole placeholder surface rendering. Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230830093843.3531473-37-marcandre.lureau@redhat.com> --- include/ui/qemu-pixman.h | 6 ++++++ ui/console.c | 39 ++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index ce4518e4de..51f8709327 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -47,6 +47,12 @@ # define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8 #endif +#define QEMU_PIXMAN_COLOR(r, g, b) \ + { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff } + +#define QEMU_PIXMAN_COLOR_BLACK QEMU_PIXMAN_COLOR(0x00, 0x00, 0x00) +#define QEMU_PIXMAN_COLOR_GRAY QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0xaa) + /* -------------------------------------------------------------------- */ typedef struct PixelFormat { diff --git a/ui/console.c b/ui/console.c index ed9e7137b8..88e37eaff3 100644 --- a/ui/console.c +++ b/ui/console.c @@ -363,29 +363,26 @@ static void vga_bitblt(QemuConsole *con, #include "vgafont.h" -#define QEMU_RGB(r, g, b) \ - { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff } - static const pixman_color_t color_table_rgb[2][8] = { { /* dark */ - [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ - [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ - [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */ - [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ - [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */ - [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ - [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ - [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ + [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK, + [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa), /* blue */ + [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00), /* green */ + [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa), /* cyan */ + [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00), /* red */ + [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa), /* magenta */ + [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00), /* yellow */ + [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR_GRAY, }, { /* bright */ - [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ - [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */ - [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */ - [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ - [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */ - [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ - [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ - [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */ + [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK, + [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff), /* blue */ + [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00), /* green */ + [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff), /* cyan */ + [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00), /* red */ + [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff), /* magenta */ + [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00), /* yellow */ + [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff), /* white */ } }; @@ -1520,8 +1517,8 @@ DisplaySurface *qemu_create_placeholder_surface(int w, int h, const char *msg) { DisplaySurface *surface = qemu_create_displaysurface(w, h); - pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK]; - pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE]; + pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK; + pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY; pixman_image_t *glyph; int len, x, y, i; From b704a8ce0c17f2f9f50a62cbe9053ef587c35db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:11 +0400 Subject: [PATCH 37/52] ui/console: rename vga_ functions with qemu_console_ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are not specific to VGA. Let's use the object type name as prefix instead, to avoid confusion. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230830093843.3531473-38-marcandre.lureau@redhat.com> --- ui/console.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ui/console.c b/ui/console.c index 88e37eaff3..a157a5b31c 100644 --- a/ui/console.c +++ b/ui/console.c @@ -331,9 +331,8 @@ void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata) } } -static void vga_fill_rect(QemuConsole *con, - int posx, int posy, int width, int height, - pixman_color_t color) +static void qemu_console_fill_rect(QemuConsole *con, int posx, int posy, + int width, int height, pixman_color_t color) { DisplaySurface *surface = qemu_console_surface(con); pixman_rectangle16_t rect = { @@ -345,8 +344,8 @@ static void vga_fill_rect(QemuConsole *con, } /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ -static void vga_bitblt(QemuConsole *con, - int xs, int ys, int xd, int yd, int w, int h) +static void qemu_console_bitblt(QemuConsole *con, + int xs, int ys, int xd, int yd, int w, int h) { DisplaySurface *surface = qemu_console_surface(con); @@ -526,8 +525,8 @@ static void console_refresh(QemuTextConsole *s) s->text_y[1] = s->height - 1; s->cursor_invalidate = 1; - vga_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface), - color_table_rgb[0][QEMU_COLOR_BLACK]); + qemu_console_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface), + color_table_rgb[0][QEMU_COLOR_BLACK]); y1 = s->y_displayed; for (y = 0; y < s->height; y++) { c = s->cells + y1 * s->width; @@ -605,12 +604,12 @@ static void vc_put_lf(VCChardev *vc) s->text_x[1] = s->width - 1; s->text_y[1] = s->height - 1; - vga_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0, - s->width * FONT_WIDTH, - (s->height - 1) * FONT_HEIGHT); - vga_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT, - s->width * FONT_WIDTH, FONT_HEIGHT, - color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]); + qemu_console_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0, + s->width * FONT_WIDTH, + (s->height - 1) * FONT_HEIGHT); + qemu_console_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT, + s->width * FONT_WIDTH, FONT_HEIGHT, + color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]); s->update_x0 = 0; s->update_y0 = 0; s->update_x1 = s->width * FONT_WIDTH; From 0a1642e7ccdadf6c3da670369eeceec410dce058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:12 +0400 Subject: [PATCH 38/52] ui/console: assert(surface) where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QemuTextConsole code paths assume a surface is being used as scanout, let's make this more explicit. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-39-marcandre.lureau@redhat.com> --- ui/console.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/console.c b/ui/console.c index a157a5b31c..04ec2d2488 100644 --- a/ui/console.c +++ b/ui/console.c @@ -339,6 +339,7 @@ static void qemu_console_fill_rect(QemuConsole *con, int posx, int posy, .x = posx, .y = posy, .width = width, .height = height }; + assert(surface); pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image, &color, 1, &rect); } @@ -349,6 +350,7 @@ static void qemu_console_bitblt(QemuConsole *con, { DisplaySurface *surface = qemu_console_surface(con); + assert(surface); pixman_image_composite(PIXMAN_OP_SRC, surface->image, NULL, surface->image, xs, ys, 0, 0, xd, yd, w, h); @@ -392,6 +394,7 @@ static void vga_putcharxy(QemuConsole *s, int x, int y, int ch, DisplaySurface *surface = qemu_console_surface(s); pixman_color_t fgcol, bgcol; + assert(surface); if (t_attrib->invers) { bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; @@ -519,6 +522,7 @@ static void console_refresh(QemuTextConsole *s) TextCell *c; int x, y, y1; + assert(surface); s->text_x[0] = 0; s->text_y[0] = 0; s->text_x[1] = s->width - 1; From 3f9c21325c4c2005a852744db1016c479d60cb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:13 +0400 Subject: [PATCH 39/52] ui/console: fold text_console_update_cursor_timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230830093843.3531473-40-marcandre.lureau@redhat.com> --- ui/console.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/ui/console.c b/ui/console.c index 04ec2d2488..0a48ce9159 100644 --- a/ui/console.c +++ b/ui/console.c @@ -174,7 +174,6 @@ static QEMUTimer *cursor_timer; static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); -static void text_console_update_cursor_timer(void); static void text_console_update_cursor(void *opaque); static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl); static bool console_compatible_with(QemuConsole *con, @@ -2497,12 +2496,6 @@ static void vc_chr_set_echo(Chardev *chr, bool echo) drv->console->echo = echo; } -static void text_console_update_cursor_timer(void) -{ - timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - + CONSOLE_CURSOR_PERIOD / 2); -} - static void text_console_update_cursor(void *opaque) { QemuConsole *s; @@ -2520,7 +2513,8 @@ static void text_console_update_cursor(void *opaque) } if (count) { - text_console_update_cursor_timer(); + timer_mod(cursor_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2); } } From 9cb737b77d9cc43a9bed305cbb105928a3dda54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:14 +0400 Subject: [PATCH 40/52] ui/vc: skip text console resize when possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is called on invalidate, on each cursor blink. Avoid the extra copy when the console size didn't change. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-41-marcandre.lureau@redhat.com> --- ui/console.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ui/console.c b/ui/console.c index 0a48ce9159..4ee3b77568 100644 --- a/ui/console.c +++ b/ui/console.c @@ -413,13 +413,19 @@ static void text_console_resize(QemuTextConsole *t) { QemuConsole *s = QEMU_CONSOLE(t); TextCell *cells, *c, *c1; - int w1, x, y, last_width; + int w1, x, y, last_width, w, h; assert(s->scanout.kind == SCANOUT_SURFACE); + w = surface_width(s->surface) / FONT_WIDTH; + h = surface_height(s->surface) / FONT_HEIGHT; + if (w == t->width && h == t->height) { + return; + } + last_width = t->width; - t->width = surface_width(s->surface) / FONT_WIDTH; - t->height = surface_height(s->surface) / FONT_HEIGHT; + t->width = w; + t->height = h; w1 = last_width; if (t->width < w1) From 893fe23e7dc675d650a4da710efe62a53c2341ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:15 +0400 Subject: [PATCH 41/52] ui/console: minor stylistic changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-42-marcandre.lureau@redhat.com> --- ui/console.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ui/console.c b/ui/console.c index 4ee3b77568..b1d375ecb4 100644 --- a/ui/console.c +++ b/ui/console.c @@ -427,20 +427,18 @@ static void text_console_resize(QemuTextConsole *t) t->width = w; t->height = h; - w1 = last_width; - if (t->width < w1) - w1 = t->width; + w1 = MIN(t->width, last_width); cells = g_new(TextCell, t->width * t->total_height + 1); - for(y = 0; y < t->total_height; y++) { + for (y = 0; y < t->total_height; y++) { c = &cells[y * t->width]; if (w1 > 0) { c1 = &t->cells[y * last_width]; - for(x = 0; x < w1; x++) { + for (x = 0; x < w1; x++) { *c++ = *c1++; } } - for(x = w1; x < t->width; x++) { + for (x = w1; x < t->width; x++) { c->ch = ' '; c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; c++; From 322dae4bc817fe288a103427f53de2a945daca27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:16 +0400 Subject: [PATCH 42/52] ui/vc: move text console invalidate in helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow to split the VC code in a separate unit more easily. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-43-marcandre.lureau@redhat.com> --- include/ui/console.h | 1 + ui/console.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index 0f7f50deaf..91d8bbc9dc 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -521,6 +521,7 @@ void console_select(unsigned int index); void qemu_console_resize(QemuConsole *con, int width, int height); DisplaySurface *qemu_console_surface(QemuConsole *con); void coroutine_fn qemu_console_co_wait_update(QemuConsole *con); +int qemu_invalidate_text_consoles(void); /* console-gl.c */ #ifdef CONFIG_OPENGL diff --git a/ui/console.c b/ui/console.c index b1d375ecb4..ba9da8c1b3 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2500,13 +2500,11 @@ static void vc_chr_set_echo(Chardev *chr, bool echo) drv->console->echo = echo; } -static void text_console_update_cursor(void *opaque) +int qemu_invalidate_text_consoles(void) { QemuConsole *s; int count = 0; - cursor_visible_phase = !cursor_visible_phase; - QTAILQ_FOREACH(s, &consoles, next) { if (qemu_console_is_graphic(s) || !qemu_console_is_visible(s)) { @@ -2516,7 +2514,14 @@ static void text_console_update_cursor(void *opaque) graphic_hw_invalidate(s); } - if (count) { + return count; +} + +static void text_console_update_cursor(void *opaque) +{ + cursor_visible_phase = !cursor_visible_phase; + + if (qemu_invalidate_text_consoles()) { timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2); } From 32aa1f8dee3b2e8a4606bc2836a022f1ff5e7f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:17 +0400 Subject: [PATCH 43/52] ui/vc: do not parse VC-specific options in Spice and GTK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 6f974c843c ("gtk: overwrite the console.c char driver"), I shared the VC console parse handler with GTK. And later on in commit d8aec9d9 ("display: add -display spice-app launching a Spice client"), I also used it to handle spice-app VC. This is not necessary, the VC console options (width/height/cols/rows) are specific, and unused by tty-level GTK/Spice VC. This is not a breaking change, as those options are still being parsed by QAPI ChardevVC. Adjust the documentation about it. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-44-marcandre.lureau@redhat.com> --- include/chardev/char.h | 3 --- qapi/char.json | 4 ++++ ui/console.c | 4 ++-- ui/gtk.c | 1 - ui/spice-app.c | 7 ++++++- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/chardev/char.h b/include/chardev/char.h index 44cd82e405..01df55f9e8 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -320,7 +320,4 @@ GSource *qemu_chr_timeout_add_ms(Chardev *chr, guint ms, void suspend_mux_open(void); void resume_mux_open(void); -/* console.c */ -void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp); - #endif diff --git a/qapi/char.json b/qapi/char.json index 52aaff25eb..c1bab7b855 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -390,6 +390,10 @@ # # @rows: console height, in chars # +# Note: the options are only effective when the VNC or SDL graphical +# display backend is active. They are ignored with the GTK, Spice, VNC +# and D-Bus display backends. +# # Since: 1.5 ## { 'struct': 'ChardevVC', diff --git a/ui/console.c b/ui/console.c index ba9da8c1b3..e2b0b9ce06 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2708,7 +2708,7 @@ void qemu_display_help(void) } } -void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) +static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp) { int val; ChardevVC *vc; @@ -2746,7 +2746,7 @@ static void char_vc_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); - cc->parse = qemu_chr_parse_vc; + cc->parse = vc_chr_parse; cc->open = vc_chr_open; cc->chr_write = vc_chr_write; cc->chr_accept_input = vc_chr_accept_input; diff --git a/ui/gtk.c b/ui/gtk.c index 8ba41c8f13..ef98bb0648 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1860,7 +1860,6 @@ static void char_gd_vc_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); - cc->parse = qemu_chr_parse_vc; cc->open = gd_vc_open; cc->chr_write = gd_vc_chr_write; cc->chr_accept_input = gd_vc_chr_accept_input; diff --git a/ui/spice-app.c b/ui/spice-app.c index ad7f0551ad..405fb7f9f5 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -96,6 +96,11 @@ static void vc_chr_set_echo(Chardev *chr, bool echo) /* TODO: set echo for frontends QMP and qtest */ } +static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp) +{ + /* fqdn is dealt with in vc_chr_open() */ +} + static void char_vc_class_init(ObjectClass *oc, void *data) { VCChardevClass *vc = CHARDEV_VC_CLASS(oc); @@ -103,7 +108,7 @@ static void char_vc_class_init(ObjectClass *oc, void *data) vc->parent_open = cc->open; - cc->parse = qemu_chr_parse_vc; + cc->parse = vc_chr_parse; cc->open = vc_chr_open; cc->chr_set_echo = vc_chr_set_echo; } From 9db018ac56119ee8e0a87a1a340276e4c8d86392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 30 Aug 2023 13:38:18 +0400 Subject: [PATCH 44/52] ui/vc: change the argument for QemuTextConsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those functions are specifc to text/vc console, make that explicit from the argument type. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé Message-Id: <20230830093843.3531473-45-marcandre.lureau@redhat.com> --- include/ui/console.h | 6 +++--- ui/console.c | 14 ++++++-------- ui/gtk.c | 2 +- ui/sdl2-input.c | 7 ++++--- ui/sdl2.c | 5 ++--- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index 91d8bbc9dc..1ccd432b4d 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -112,9 +112,9 @@ bool qemu_mouse_set(int index, Error **errp); #define QEMU_KEY_CTRL_PAGEUP 0xe406 #define QEMU_KEY_CTRL_PAGEDOWN 0xe407 -void kbd_put_keysym_console(QemuConsole *s, int keysym); -bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl); -void kbd_put_string_console(QemuConsole *s, const char *str, int len); +void kbd_put_keysym_console(QemuTextConsole *s, int keysym); +bool kbd_put_qcode_console(QemuTextConsole *s, int qcode, bool ctrl); +void kbd_put_string_console(QemuTextConsole *s, const char *str, int len); void kbd_put_keysym(int keysym); /* Touch devices */ diff --git a/ui/console.c b/ui/console.c index e2b0b9ce06..e4d61794bb 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1135,16 +1135,12 @@ static void kbd_send_chars(QemuTextConsole *s) } /* called when an ascii key is pressed */ -void kbd_put_keysym_console(QemuConsole *con, int keysym) +void kbd_put_keysym_console(QemuTextConsole *s, int keysym) { - QemuTextConsole *s = (QemuTextConsole *)object_dynamic_cast(OBJECT(con), TYPE_QEMU_TEXT_CONSOLE); uint8_t buf[16], *q; int c; uint32_t num_free; - if (!s) - return; - switch(keysym) { case QEMU_KEY_CTRL_UP: console_scroll(s, -1); @@ -1214,7 +1210,7 @@ static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = { [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN, }; -bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl) +bool kbd_put_qcode_console(QemuTextConsole *s, int qcode, bool ctrl) { int keysym; @@ -1226,7 +1222,7 @@ bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl) return true; } -void kbd_put_string_console(QemuConsole *s, const char *str, int len) +void kbd_put_string_console(QemuTextConsole *s, const char *str, int len) { int i; @@ -1237,7 +1233,9 @@ void kbd_put_string_console(QemuConsole *s, const char *str, int len) void kbd_put_keysym(int keysym) { - kbd_put_keysym_console(active_console, keysym); + if (QEMU_IS_TEXT_CONSOLE(active_console)) { + kbd_put_keysym_console(QEMU_TEXT_CONSOLE(active_console), keysym); + } } static void text_console_invalidate(void *opaque) diff --git a/ui/gtk.c b/ui/gtk.c index ef98bb0648..c34c133550 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1187,7 +1187,7 @@ static gboolean gd_text_key_down(GtkWidget *widget, GdkEventKey *key, void *opaque) { VirtualConsole *vc = opaque; - QemuConsole *con = vc->gfx.dcl.con; + QemuTextConsole *con = QEMU_TEXT_CONSOLE(vc->gfx.dcl.con); if (key->keyval == GDK_KEY_Delete) { kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false); diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c index f068382209..a318cc5867 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -43,15 +43,16 @@ void sdl2_process_key(struct sdl2_console *scon, ev->type == SDL_KEYDOWN ? "down" : "up"); qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN); - if (!qemu_console_is_graphic(con)) { + 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: - kbd_put_keysym_console(con, '\n'); + kbd_put_keysym_console(s, '\n'); break; default: - kbd_put_qcode_console(con, qcode, ctrl); + kbd_put_qcode_console(s, qcode, ctrl); break; } } diff --git a/ui/sdl2.c b/ui/sdl2.c index 0d91b555e3..16b515fcf9 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -483,10 +483,9 @@ static void handle_textinput(SDL_Event *ev) return; } - if (qemu_console_is_graphic(con)) { - return; + if (QEMU_IS_TEXT_CONSOLE(con)) { + kbd_put_string_console(QEMU_TEXT_CONSOLE(con), ev->text.text, strlen(ev->text.text)); } - kbd_put_string_console(con, ev->text.text, strlen(ev->text.text)); } static void handle_mousemotion(SDL_Event *ev) From 1663ffb9157e3dc17d14741f6cd6c48bfffde9d0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Aug 2023 16:10:55 +0100 Subject: [PATCH 45/52] ui/spice-display: Avoid dynamic stack allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use an autofree heap allocation instead of a variable-length array on the stack in qemu_spice_create_update(). The codebase has very few VLAs, and if we can get rid of them all we can make the compiler error on new additions. This is a defensive measure against security bugs where an on-stack dynamic allocation isn't correctly size-checked (e.g. CVE-2021-3527). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20230818151057.1541189-2-peter.maydell@linaro.org> --- ui/spice-display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/spice-display.c b/ui/spice-display.c index 3f3f8013d8..0e2fbfb17c 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -189,7 +189,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) { static const int blksize = 32; int blocks = DIV_ROUND_UP(surface_width(ssd->ds), blksize); - int dirty_top[blocks]; + g_autofree int *dirty_top = NULL; int y, yoff1, yoff2, x, xoff, blk, bw; int bpp = surface_bytes_per_pixel(ssd->ds); uint8_t *guest, *mirror; @@ -198,6 +198,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) return; }; + dirty_top = g_new(int, blocks); for (blk = 0; blk < blocks; blk++) { dirty_top[blk] = -1; } From e12acaf75d1ffadfd527180dac798368716a0001 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Aug 2023 16:10:56 +0100 Subject: [PATCH 46/52] ui/vnc-enc-hextile: Use static rather than dynamic length stack array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the send_hextile_tile_* function we create a variable length array data[]. In fact we know that the client_pf.bytes_per_pixel is at most 4 (enforced by set_pixel_format()), so we can make the array a compile-time fixed length of 1536 bytes. The codebase has very few VLAs, and if we can get rid of them all we can make the compiler error on new additions. This is a defensive measure against security bugs where an on-stack dynamic allocation isn't correctly size-checked (e.g. CVE-2021-3527). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé [ Marc-André - rename BPP to MAX_BYTES_PER_PIXEL ] Signed-off-by: Marc-André Lureau Message-Id: <20230818151057.1541189-3-peter.maydell@linaro.org> --- ui/vnc-enc-hextile-template.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/vnc-enc-hextile-template.h b/ui/vnc-enc-hextile-template.h index 0c56262aff..8ee92086ac 100644 --- a/ui/vnc-enc-hextile-template.h +++ b/ui/vnc-enc-hextile-template.h @@ -7,6 +7,8 @@ #define NAME BPP #endif +#define MAX_BYTES_PER_PIXEL 4 + static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, int x, int y, int w, int h, void *last_bg_, @@ -25,10 +27,13 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, int bg_count = 0; int fg_count = 0; int flags = 0; - uint8_t data[(vs->client_pf.bytes_per_pixel + 2) * 16 * 16]; + uint8_t data[(MAX_BYTES_PER_PIXEL + 2) * 16 * 16]; int n_data = 0; int n_subtiles = 0; + /* Enforced by set_pixel_format() */ + assert(vs->client_pf.bytes_per_pixel <= MAX_BYTES_PER_PIXEL); + for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { switch (n_colors) { @@ -205,6 +210,7 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, } } +#undef MAX_BYTES_PER_PIXEL #undef NAME #undef pixel_t #undef CONCAT_I From dd0439e1496ad326dcaa7dc67f91f2e6f6c4930b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 18 Aug 2023 16:10:57 +0100 Subject: [PATCH 47/52] ui/vnc-enc-tight: Avoid dynamic stack allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use autofree heap allocation instead of variable-length array on the stack. The codebase has very few VLAs, and if we can get rid of them all we can make the compiler error on new additions. This is a defensive measure against security bugs where an on-stack dynamic allocation isn't correctly size-checked (e.g. CVE-2021-3527). Signed-off-by: Philippe Mathieu-Daudé [PMM: expanded commit message] Signed-off-by: Peter Maydell Reviewed-by: Francisco Iglesias Reviewed-by: Marc-André Lureau Message-Id: <20230818151057.1541189-4-peter.maydell@linaro.org> --- ui/vnc-enc-tight.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index ee853dcfcb..41f559eb83 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -1097,13 +1097,13 @@ static int send_palette_rect(VncState *vs, int x, int y, switch (vs->client_pf.bytes_per_pixel) { case 4: { - size_t old_offset, offset; - uint32_t header[palette_size(palette)]; + 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 }; old_offset = vs->output.offset; palette_iter(palette, write_palette, &priv); - vnc_write(vs, header, sizeof(header)); + vnc_write(vs, header, palette_sz * sizeof(uint32_t)); if (vs->tight->pixel24) { tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset); @@ -1115,11 +1115,12 @@ static int send_palette_rect(VncState *vs, int x, int y, } case 2: { - uint16_t header[palette_size(palette)]; + 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 }; palette_iter(palette, write_palette, &priv); - vnc_write(vs, header, sizeof(header)); + vnc_write(vs, header, palette_sz * sizeof(uint16_t)); tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, palette); break; } From cb6ccdc9ca705cd8c3ef50e51c16a3732c2fa734 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Fri, 1 Sep 2023 14:45:07 +0200 Subject: [PATCH 48/52] ui/dbus: Properly dispose touch/mouse dbus objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 142ca628a7 ("ui: add a D-Bus display backend") Fixes: de9f844ce2 ("ui/dbus: Expose a touch device interface") Signed-off-by: Bilal Elmoussaoui Reviewed-by: Marc-André Lureau Message-Id: <20230901124507.94087-1-belmouss@redhat.com> --- ui/dbus-console.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/dbus-console.c b/ui/dbus-console.c index e19774f985..36f7349585 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -150,6 +150,8 @@ dbus_display_console_dispose(GObject *object) DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); unregister_displaychangelistener(&ddc->dcl); + g_clear_object(&ddc->iface_touch); + g_clear_object(&ddc->iface_mouse); g_clear_object(&ddc->iface_kbd); g_clear_object(&ddc->iface); g_clear_pointer(&ddc->listeners, g_hash_table_unref); From 7007e98c4ba443ce5d42acf851daaa1835b18e83 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Mon, 14 Aug 2023 14:58:02 +0200 Subject: [PATCH 49/52] ui/dbus: implement damage regions for GL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, when using `-display dbus,gl=on` all updates to the client become "full scanout" updates, meaning there is no way for the client to limit damage regions to the display server. Instead of using an "update count", this patch tracks the damage region and propagates it to the client. This was less of an issue when clients were using GtkGLArea for rendering, as you'd be doing full-surface redraw. To be efficient, the client needs both a DMA-BUF and the damage region to be updated. Co-authored-by: Christian Hergert Signed-off-by: Bilal Elmoussaoui Reviewed-by: Marc-André Lureau Message-Id: <20230814125802.102160-1-belmouss@redhat.com> --- ui/dbus-listener.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 30917271ab..36548a7f52 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -26,6 +26,9 @@ #include "qapi/error.h" #include "sysemu/sysemu.h" #include "dbus.h" +#ifdef CONFIG_OPENGL +#include +#endif #ifdef G_OS_UNIX #include #endif @@ -59,12 +62,15 @@ struct _DBusDisplayListener { QemuDBusDisplay1Listener *proxy; +#ifdef CONFIG_OPENGL + /* Keep track of the damage region */ + pixman_region32_t gl_damage; +#endif + DisplayChangeListener dcl; DisplaySurface *ds; enum share_kind ds_share; - int gl_updates; - bool ds_mapped; bool can_share_map; @@ -539,11 +545,16 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl) return; } - if (ddl->gl_updates) { - dbus_call_update_gl(dcl, 0, 0, - surface_width(ddl->ds), surface_height(ddl->ds)); - ddl->gl_updates = 0; + int n_rects = pixman_region32_n_rects(&ddl->gl_damage); + + for (int i = 0; i < n_rects; i++) { + pixman_box32_t *box; + box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i; + /* TODO: Add a UpdateList call to send multiple updates at once */ + dbus_call_update_gl(dcl, box->x1, box->y1, + box->x2 - box->x1, box->y2 - box->y1); } + pixman_region32_clear(&ddl->gl_damage); } #endif /* OPENGL */ @@ -558,7 +569,10 @@ static void dbus_gl_gfx_update(DisplayChangeListener *dcl, { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - ddl->gl_updates++; + pixman_region32_t rect_region; + pixman_region32_init_rect(&rect_region, x, y, w, h); + pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region); + pixman_region32_fini(&rect_region); } #endif @@ -738,6 +752,7 @@ dbus_display_listener_dispose(GObject *object) g_clear_object(&ddl->d3d11_proxy); g_clear_pointer(&ddl->peer_process, CloseHandle); #ifdef CONFIG_OPENGL + pixman_region32_fini(&ddl->gl_damage); egl_fb_destroy(&ddl->fb); #endif #endif @@ -772,6 +787,9 @@ dbus_display_listener_class_init(DBusDisplayListenerClass *klass) static void dbus_display_listener_init(DBusDisplayListener *ddl) { +#ifdef CONFIG_OPENGL + pixman_region32_init(&ddl->gl_damage); +#endif } const char * From 1b4fd51656556646b1a0842e596cb606d73e26cf Mon Sep 17 00:00:00 2001 From: Guoyi Tu Date: Thu, 17 Aug 2023 22:12:52 +0800 Subject: [PATCH 50/52] ui/vdagent: call vdagent_disconnect() when agent connection is lost MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when the agent connection is lost, the input handler of the mouse doesn't deactivate, which results in unresponsive mouse events in VNC windows. To fix this issue, call vdagent_disconnect() to reset the state each time the frontend disconncect Signed-off-by: Guoyi Tu Signed-off-by: dengpengcheng Reviewed-by: Marc-André Lureau Message-Id: <71fd5a58fd09f10cdb35f167b2edb5669300116e.1692281173.git.tugy@chinatelecom.cn> --- ui/vdagent.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/vdagent.c b/ui/vdagent.c index 8a651492f0..4b9a1fb7c5 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -870,8 +870,11 @@ static void vdagent_disconnect(VDAgentChardev *vd) static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) { + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr); + if (!fe_open) { trace_vdagent_close(); + vdagent_disconnect(vd); /* To reset_serial, we CLOSED our side. Make sure the other end knows we * are ready again. */ qemu_chr_be_event(chr, CHR_EVENT_OPENED); From 878490937c6273f27191e3a195c7a60fa68819b8 Mon Sep 17 00:00:00 2001 From: Guoyi Tu Date: Thu, 17 Aug 2023 22:12:53 +0800 Subject: [PATCH 51/52] ui/vdagent: Unregister input handler of mouse during finalization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Input handler resource should be released when VDAgentChardev object finalize Signed-off-by: Guoyi Tu Signed-off-by: dengpengcheng Reviewed-by: Marc-André Lureau Message-Id: --- ui/vdagent.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/vdagent.c b/ui/vdagent.c index 4b9a1fb7c5..00d36a8677 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -926,6 +926,9 @@ static void vdagent_chr_fini(Object *obj) migrate_del_blocker(vd->migration_blocker); vdagent_disconnect(vd); + if (vd->mouse_hs) { + qemu_input_handler_unregister(vd->mouse_hs); + } buffer_free(&vd->outbuf); error_free(vd->migration_blocker); } From e38f4e976dd40c985bfe84230a627de9a108c9d3 Mon Sep 17 00:00:00 2001 From: Dmitry Frolov Date: Fri, 25 Aug 2023 14:58:19 +0300 Subject: [PATCH 52/52] ui/gtk: fix leaks found wtih fuzzing It is true, that there is no problem during runtime from the first sight, because the memory is lost just before qemu exits. Nevertheless, this change is necessary, because AddressSanitizer is not able to recognize this situation and produces crash-report (which is false-positive in fact). Lots of False-Positive warnings are davaluing problems, found with fuzzing, and thus the whole methodology of dynamic analysis. This patch eliminates such False-Positive reports, and makes every problem, found with fuzzing, more valuable. Fixes: 060ab76356 ("gtk: don't exit early in case gtk init fails") Signed-off-by: Dmitry Frolov Reviewed-by: Michael Tokarev Message-Id: <20230825115818.1091936-1-frolov@swemel.ru> --- ui/gtk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/gtk.c b/ui/gtk.c index c34c133550..a14d56168d 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -2359,7 +2359,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) { VirtualConsole *vc; - GtkDisplayState *s = g_malloc0(sizeof(*s)); + GtkDisplayState *s; GdkDisplay *window_display; GtkIconTheme *theme; char *dir; @@ -2369,6 +2369,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) exit(1); } assert(opts->type == DISPLAY_TYPE_GTK); + s = g_malloc0(sizeof(*s)); s->opts = opts; theme = gtk_icon_theme_get_default();