From 3af167185229afae754f86e1b86d6dd9a5acfb70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 16 Feb 2021 13:20:55 +0400 Subject: [PATCH 1/8] spice: flush on GL update before notifying client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the introduction of spice/virgl support in commit 474114b7 ("spice: add opengl/virgl/dmabuf support"), the drawing isn't being flushed before notifying the client. This results in outdated/sluggish drawing on client side, in particular when using the Linux console. Signed-off-by: Marc-André Lureau Message-Id: <20210216092056.2301293-1-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- ui/spice-display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/spice-display.c b/ui/spice-display.c index 6f32b66a6e..d562c64084 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1087,6 +1087,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); qemu_spice_gl_block(ssd, true); + glFlush(); cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); } From f4f2805ef8938565973864a7e5fd1207e074460e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 16 Feb 2021 13:20:56 +0400 Subject: [PATCH 2/8] spice: flush drawing before notifying client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This solves the client having slow/outdated VGA/2D console. It's a regression introduced when the code was switched to render it via opengl in commit 4423184376d ("spice/gl: render DisplaySurface via opengl") Signed-off-by: Marc-André Lureau Message-Id: <20210216092056.2301293-2-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- ui/spice-display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/spice-display.c b/ui/spice-display.c index d562c64084..ad93b953a9 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -846,6 +846,7 @@ static void spice_gl_refresh(DisplayChangeListener *dcl) graphic_hw_update(dcl->con); if (ssd->gl_updates && ssd->have_surface) { qemu_spice_gl_block(ssd, true); + glFlush(); cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); spice_qxl_gl_draw_async(&ssd->qxl, 0, 0, surface_width(ssd->ds), From 708b72557ff510493ab3653c7a1ebfe983009469 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 12 Feb 2021 09:04:04 +0900 Subject: [PATCH 3/8] ui/cocoa: Support unique keys of JIS keyboards Signed-off-by: Akihiko Odaki Message-Id: <20210212000404.28413-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- ui/cocoa.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui/cocoa.m b/ui/cocoa.m index 13fba8103e..78fcfeaf04 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -240,6 +240,13 @@ const int mac_to_qkeycode_map[] = { [kVK_F14] = Q_KEY_CODE_SCROLL_LOCK, [kVK_F15] = Q_KEY_CODE_PAUSE, + // JIS keyboards only + [kVK_JIS_Yen] = Q_KEY_CODE_YEN, + [kVK_JIS_Underscore] = Q_KEY_CODE_RO, + [kVK_JIS_KeypadComma] = Q_KEY_CODE_KP_COMMA, + [kVK_JIS_Eisu] = Q_KEY_CODE_MUHENKAN, + [kVK_JIS_Kana] = Q_KEY_CODE_HENKAN, + /* * The eject and volume keys can't be used here because they are handled at * a lower level than what an Application can see. From c0ff29d1889040a61a7120731ac16e0c1da39149 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 12 Feb 2021 09:06:29 +0900 Subject: [PATCH 4/8] ui/cocoa: Do not copy members of pixman image The old CocoaView had an idea of synchronizing the host window configuration and the guest screen configuration. Here, the guest screen actually means pixman image given ui/cocoa display implementation. However, [CocoaView -drawRect:] directly interacts with the pixman image buffer in reality. There is no such distinction of "host" and "guest." This change removes the "host" configuration and let drawRect consistently have the direct reference to pixman image. It allows to get rid of the error-prone "sync" and reduce code size a bit. Signed-off-by: Akihiko Odaki Message-Id: <20210212000629.28551-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- ui/cocoa.m | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index 78fcfeaf04..eab4bfe7c8 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -70,8 +70,6 @@ typedef struct { int width; int height; - int bitsPerComponent; - int bitsPerPixel; } QEMUScreen; NSWindow *normalWindow, *about_window; @@ -291,7 +289,6 @@ static void handleAnyDeviceErrors(Error * err) QEMUScreen screen; NSWindow *fullScreenWindow; float cx,cy,cw,ch,cdx,cdy; - CGDataProviderRef dataProviderRef; pixman_image_t *pixman_image; BOOL modifiers_state[256]; BOOL isMouseGrabbed; @@ -338,8 +335,6 @@ QemuCocoaView *cocoaView; self = [super initWithFrame:frameRect]; if (self) { - screen.bitsPerComponent = 8; - screen.bitsPerPixel = 32; screen.width = frameRect.size.width; screen.height = frameRect.size.height; @@ -351,8 +346,7 @@ QemuCocoaView *cocoaView; { COCOA_DEBUG("QemuCocoaView: dealloc\n"); - if (dataProviderRef) { - CGDataProviderRelease(dataProviderRef); + if (pixman_image) { pixman_image_unref(pixman_image); } @@ -431,18 +425,28 @@ QemuCocoaView *cocoaView; CGContextSetShouldAntialias (viewContextRef, NO); // draw screen bitmap directly to Core Graphics context - if (!dataProviderRef) { + if (!pixman_image) { // Draw request before any guest device has set up a framebuffer: // just draw an opaque black rectangle CGContextSetRGBFillColor(viewContextRef, 0, 0, 0, 1.0); CGContextFillRect(viewContextRef, NSRectToCGRect(rect)); } else { + int w = pixman_image_get_width(pixman_image); + int h = pixman_image_get_height(pixman_image); + int bitsPerPixel = PIXMAN_FORMAT_BPP(pixman_image_get_format(pixman_image)); + int bitsPerComponent = DIV_ROUND_UP(bitsPerPixel, 8) * 2; + CGDataProviderRef dataProviderRef = CGDataProviderCreateWithData( + NULL, + pixman_image_get_data(pixman_image), + w * 4 * h, + NULL + ); CGImageRef imageRef = CGImageCreate( - screen.width, //width - screen.height, //height - screen.bitsPerComponent, //bitsPerComponent - screen.bitsPerPixel, //bitsPerPixel - (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow + w, //width + h, //height + bitsPerComponent, //bitsPerComponent + bitsPerPixel, //bitsPerPixel + (w * (bitsPerComponent/2)), //bytesPerRow #ifdef __LITTLE_ENDIAN__ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4 kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, @@ -465,7 +469,7 @@ QemuCocoaView *cocoaView; [self getRectsBeingDrawn:&rectList count:&rectCount]; for (i = 0; i < rectCount; i++) { clipRect.origin.x = rectList[i].origin.x / cdx; - clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy; + clipRect.origin.y = (float)h - (rectList[i].origin.y + rectList[i].size.height) / cdy; clipRect.size.width = rectList[i].size.width / cdx; clipRect.size.height = rectList[i].size.height / cdy; clipImageRef = CGImageCreateWithImageInRect( @@ -476,6 +480,7 @@ QemuCocoaView *cocoaView; CGImageRelease (clipImageRef); } CGImageRelease (imageRef); + CGDataProviderRelease(dataProviderRef); } } @@ -518,7 +523,6 @@ QemuCocoaView *cocoaView; int w = pixman_image_get_width(image); int h = pixman_image_get_height(image); - pixman_format_code_t image_format = pixman_image_get_format(image); /* cdx == 0 means this is our very first surface, in which case we need * to recalculate the content dimensions even if it happens to be the size * of the initial empty window. @@ -536,17 +540,11 @@ QemuCocoaView *cocoaView; } // update screenBuffer - if (dataProviderRef) { - CGDataProviderRelease(dataProviderRef); + if (pixman_image) { pixman_image_unref(pixman_image); } - //sync host window color space with guests - screen.bitsPerPixel = PIXMAN_FORMAT_BPP(image_format); - screen.bitsPerComponent = DIV_ROUND_UP(screen.bitsPerPixel, 8) * 2; - pixman_image = image; - dataProviderRef = CGDataProviderCreateWithData(NULL, pixman_image_get_data(image), w * 4 * h, NULL); // update windows if (isFullscreen) { From 122e4ef6dea14a078a860ca253852e18ddebb8e2 Mon Sep 17 00:00:00 2001 From: Bruce Rogers Date: Fri, 12 Feb 2021 20:23:18 -0700 Subject: [PATCH 5/8] spice-app: avoid crash when core spice module doesn't loaded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When qemu is built with modules, but a given module doesn't load qemu should handle that gracefully. When ui-spice-core.so isn't able to be loaded and qemu is invoked with -display spice-app or -spice, qemu will dereference a null pointer. With this change we check the pointer before dereferencing and error out in a normal way. Signed-off-by: Bruce Rogers Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210213032318.346093-1-brogers@suse.com> Signed-off-by: Gerd Hoffmann --- ui/spice-app.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/spice-app.c b/ui/spice-app.c index 026124ef56..4325ac2d9c 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -129,6 +129,7 @@ static void spice_app_atexit(void) static void spice_app_display_early_init(DisplayOptions *opts) { QemuOpts *qopts; + QemuOptsList *list; GError *err = NULL; if (opts->has_full_screen) { @@ -159,11 +160,16 @@ static void spice_app_display_early_init(DisplayOptions *opts) exit(1); } } + list = qemu_find_opts("spice"); + if (list == NULL) { + error_report("spice-app missing spice support"); + exit(1); + } type_register(&char_vc_type_info); sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL); - qopts = qemu_opts_create(qemu_find_opts("spice"), NULL, 0, &error_abort); + qopts = qemu_opts_create(list, NULL, 0, &error_abort); qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort); qemu_opt_set(qopts, "unix", "on", &error_abort); qemu_opt_set(qopts, "addr", sock_path, &error_abort); From 4295f8365cbe8b583d1065b046e9d086b4bc54c9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 12 Feb 2021 09:07:06 +0900 Subject: [PATCH 6/8] ui/cocoa: Interpret left button down as is when command is pressed Old Macs were not equipped with mice with an ability to perform "right clicks" and ui/cocoa interpreted left button down with left command key pressed as right button down as a workaround. The workaround has an obvious downside: you cannot tell the guest that the left button is down while the left command key is pressed. Today, Macs has trackpads, Apple Mice, or Magic Mice. They are capable to emulate right clicks with gestures, which also allows to perform right clicks on "BootCamp" OSes like Windows. By removing the workaround, we overcome its downside, and provide a behavior consistent with BootCamp. Signed-off-by: Akihiko Odaki Message-Id: <20210212000706.28616-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- ui/cocoa.m | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index eab4bfe7c8..13f19bece1 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -835,11 +835,7 @@ QemuCocoaView *cocoaView; mouse_event = true; break; case NSEventTypeLeftMouseDown: - if ([event modifierFlags] & NSEventModifierFlagCommand) { - buttons |= MOUSE_EVENT_RBUTTON; - } else { - buttons |= MOUSE_EVENT_LBUTTON; - } + buttons |= MOUSE_EVENT_LBUTTON; mouse_event = true; break; case NSEventTypeRightMouseDown: @@ -851,11 +847,7 @@ QemuCocoaView *cocoaView; mouse_event = true; break; case NSEventTypeLeftMouseDragged: - if ([event modifierFlags] & NSEventModifierFlagCommand) { - buttons |= MOUSE_EVENT_RBUTTON; - } else { - buttons |= MOUSE_EVENT_LBUTTON; - } + buttons |= MOUSE_EVENT_LBUTTON; mouse_event = true; break; case NSEventTypeRightMouseDragged: From cc7859c370b0e12f176aab3d0034ff7fa8f5a973 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 19 Feb 2021 17:44:19 +0900 Subject: [PATCH 7/8] ui/cocoa: Statically allocate dcl There is no need of dynamic allocation as dcl is a small singleton. Static allocation reduces code size and makes hacking with ui/cocoa a bit easier. Signed-off-by: Akihiko Odaki Message-Id: <20210219084419.90181-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- ui/cocoa.m | 65 ++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index 13f19bece1..0ef5fdf3b7 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -72,8 +72,24 @@ typedef struct { int height; } QEMUScreen; +static void cocoa_update(DisplayChangeListener *dcl, + int x, int y, int w, int h); + +static void cocoa_switch(DisplayChangeListener *dcl, + DisplaySurface *surface); + +static void cocoa_refresh(DisplayChangeListener *dcl); + NSWindow *normalWindow, *about_window; -static DisplayChangeListener *dcl; +static const DisplayChangeListenerOps dcl_ops = { + .dpy_name = "cocoa", + .dpy_gfx_update = cocoa_update, + .dpy_gfx_switch = cocoa_switch, + .dpy_refresh = cocoa_refresh, +}; +static DisplayChangeListener dcl = { + .ops = &dcl_ops, +}; static int last_buttons; static int cursor_hide = 1; @@ -607,15 +623,15 @@ QemuCocoaView *cocoaView; // Toggle the stored state. modifiers_state[keycode] = !modifiers_state[keycode]; // Send a keyup or keydown depending on the state. - qemu_input_event_send_key_qcode(dcl->con, keycode, modifiers_state[keycode]); + qemu_input_event_send_key_qcode(dcl.con, keycode, modifiers_state[keycode]); } - (void) toggleStatefulModifier: (int)keycode { // Toggle the stored state. modifiers_state[keycode] = !modifiers_state[keycode]; // Generate keydown and keyup. - qemu_input_event_send_key_qcode(dcl->con, keycode, true); - qemu_input_event_send_key_qcode(dcl->con, keycode, false); + qemu_input_event_send_key_qcode(dcl.con, keycode, true); + qemu_input_event_send_key_qcode(dcl.con, keycode, false); } // Does the work of sending input to the monitor @@ -799,7 +815,7 @@ QemuCocoaView *cocoaView; } if (qemu_console_is_graphic(NULL)) { - qemu_input_event_send_key_qcode(dcl->con, keycode, true); + qemu_input_event_send_key_qcode(dcl.con, keycode, true); } else { [self handleMonitorInput: event]; } @@ -814,7 +830,7 @@ QemuCocoaView *cocoaView; } if (qemu_console_is_graphic(NULL)) { - qemu_input_event_send_key_qcode(dcl->con, keycode, false); + qemu_input_event_send_key_qcode(dcl.con, keycode, false); } break; case NSEventTypeMouseMoved: @@ -892,9 +908,9 @@ QemuCocoaView *cocoaView; /* Determine if this is a scroll up or scroll down event */ buttons = ([event deltaY] > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; - qemu_input_queue_btn(dcl->con, buttons, true); + qemu_input_queue_btn(dcl.con, buttons, true); qemu_input_event_sync(); - qemu_input_queue_btn(dcl->con, buttons, false); + qemu_input_queue_btn(dcl.con, buttons, false); qemu_input_event_sync(); } /* @@ -922,7 +938,7 @@ QemuCocoaView *cocoaView; [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON }; - qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons); + qemu_input_update_buttons(dcl.con, bmap, last_buttons, buttons); last_buttons = buttons; } if (isMouseGrabbed) { @@ -932,12 +948,12 @@ QemuCocoaView *cocoaView; * clicks in the titlebar. */ if ([self screenContainsPoint:p]) { - qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, 0, screen.width); - qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, screen.height - p.y, 0, screen.height); + qemu_input_queue_abs(dcl.con, INPUT_AXIS_X, p.x, 0, screen.width); + qemu_input_queue_abs(dcl.con, INPUT_AXIS_Y, screen.height - p.y, 0, screen.height); } } else { - qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]); - qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]); + qemu_input_queue_rel(dcl.con, INPUT_AXIS_X, (int)[event deltaX]); + qemu_input_queue_rel(dcl.con, INPUT_AXIS_Y, (int)[event deltaY]); } } else { return false; @@ -1006,7 +1022,7 @@ QemuCocoaView *cocoaView; for (index = 0; index < max_index; index++) { if (modifiers_state[index]) { modifiers_state[index] = 0; - qemu_input_event_send_key_qcode(dcl->con, index, false); + qemu_input_event_send_key_qcode(dcl.con, index, false); } } }); @@ -1833,19 +1849,6 @@ static void cocoa_refresh(DisplayChangeListener *dcl) [pool release]; } -static void cocoa_cleanup(void) -{ - COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); - g_free(dcl); -} - -static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "cocoa", - .dpy_gfx_update = cocoa_update, - .dpy_gfx_switch = cocoa_switch, - .dpy_refresh = cocoa_refresh, -}; - static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); @@ -1866,14 +1869,8 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) cursor_hide = 0; } - dcl = g_malloc0(sizeof(DisplayChangeListener)); - // register vga output callbacks - dcl->ops = &dcl_ops; - register_displaychangelistener(dcl); - - // register cleanup function - atexit(cocoa_cleanup); + register_displaychangelistener(&dcl); } static QemuDisplay qemu_display_cocoa = { From 075e7a5b7f3c640823fce76c8dab503c42f0d7f6 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 19 Feb 2021 18:47:02 +0900 Subject: [PATCH 8/8] ui/console: Remove dpy_gl_ctx_get_current It is not used, and it is unlikely that a new use case will emerge anytime soon because the scope of OpenGL contexts are limited due to the nature of the frontend, VirGL, processing simple commands from the guest. Remove the function and ease implementing a new OpenGL backend a little. Signed-off-by: Akihiko Odaki Message-Id: <20210219094702.90789-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- include/ui/gtk.h | 1 - ui/gtk-gl-area.c | 5 ----- 2 files changed, 6 deletions(-) diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 3c1cd98db8..5ae0ad60a6 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -147,7 +147,6 @@ void gd_gl_area_scanout_disable(DisplayChangeListener *dcl); void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void gtk_gl_area_init(void); -QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl); int gd_gl_area_make_current(DisplayChangeListener *dcl, QEMUGLContext ctx); diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index e7ca73c7b1..4e8ee88b9b 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -239,11 +239,6 @@ void gtk_gl_area_init(void) display_opengl = 1; } -QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl) -{ - return gdk_gl_context_get_current(); -} - int gd_gl_area_make_current(DisplayChangeListener *dcl, QEMUGLContext ctx) {