From 15280e85d730002f10c0829ed2fac9bd141b63e9 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 16 Jun 2021 23:19:10 +0900 Subject: [PATCH 1/5] ui/cocoa: Set UI information Signed-off-by: Akihiko Odaki Message-Id: <20210616141910.54188-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- ui/cocoa.m | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/ui/cocoa.m b/ui/cocoa.m index 37e1fb52eb..530c506489 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -518,6 +518,43 @@ QemuCocoaView *cocoaView; } } +- (void) updateUIInfo +{ + NSSize frameSize; + QemuUIInfo info; + + if (!qemu_console_is_graphic(dcl.con)) { + return; + } + + if ([self window]) { + NSDictionary *description = [[[self window] screen] deviceDescription]; + CGDirectDisplayID display = [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]; + NSSize screenSize = [[[self window] screen] frame].size; + CGSize screenPhysicalSize = CGDisplayScreenSize(display); + + frameSize = isFullscreen ? screenSize : [self frame].size; + info.width_mm = frameSize.width / screenSize.width * screenPhysicalSize.width; + info.height_mm = frameSize.height / screenSize.height * screenPhysicalSize.height; + } else { + frameSize = [self frame].size; + info.width_mm = 0; + info.height_mm = 0; + } + + info.xoff = 0; + info.yoff = 0; + info.width = frameSize.width; + info.height = frameSize.height; + + dpy_set_ui_info(dcl.con, &info); +} + +- (void)viewDidMoveToWindow +{ + [self updateUIInfo]; +} + - (void) switchSurface:(pixman_image_t *)image { COCOA_DEBUG("QemuCocoaView: switchSurface\n"); @@ -1172,6 +1209,16 @@ QemuCocoaView *cocoaView; return [self verifyQuit]; } +- (void)windowDidChangeScreen:(NSNotification *)notification +{ + [cocoaView updateUIInfo]; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + [cocoaView updateUIInfo]; +} + /* Called when the user clicks on a window's close button */ - (BOOL)windowShouldClose:(id)sender { @@ -1836,6 +1883,8 @@ static void cocoa_switch(DisplayChangeListener *dcl, COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); + [cocoaView updateUIInfo]; + // The DisplaySurface will be freed as soon as this callback returns. // We take a reference to the underlying pixman image here so it does // not disappear from under our feet; the switchSurface method will From 7e3e20d89129614f4a7b2451fe321cc6ccca3b76 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 16 Jun 2021 23:19:54 +0900 Subject: [PATCH 2/5] ui/cocoa: Add clipboard support Signed-off-by: Akihiko Odaki Message-Id: <20210616141954.54291-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- include/ui/clipboard.h | 2 +- ui/clipboard.c | 2 +- ui/cocoa.m | 109 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h index e5bcb365ed..b45b984c9f 100644 --- a/include/ui/clipboard.h +++ b/include/ui/clipboard.h @@ -187,7 +187,7 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer, QemuClipboardInfo *info, QemuClipboardType type, uint32_t size, - void *data, + const void *data, bool update); #endif /* QEMU_CLIPBOARD_H */ diff --git a/ui/clipboard.c b/ui/clipboard.c index abf2b98f1f..3525b30178 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -73,7 +73,7 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer, QemuClipboardInfo *info, QemuClipboardType type, uint32_t size, - void *data, + const void *data, bool update) { if (!info || diff --git a/ui/cocoa.m b/ui/cocoa.m index 530c506489..9f72844b07 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -28,6 +28,7 @@ #include #include "qemu-common.h" +#include "ui/clipboard.h" #include "ui/console.h" #include "ui/input.h" #include "ui/kbd-state.h" @@ -105,6 +106,10 @@ static QemuSemaphore display_init_sem; static QemuSemaphore app_started_sem; static bool allow_events; +static NSInteger cbchangecount = -1; +static QemuClipboardInfo *cbinfo; +static QemuEvent cbevent; + // Utility functions to run specified code block with iothread lock held typedef void (^CodeBlock)(void); typedef bool (^BoolCodeBlock)(void); @@ -1758,6 +1763,93 @@ static void addRemovableDevicesMenuItems(void) qapi_free_BlockInfoList(pointerToFree); } +@interface QemuCocoaPasteboardTypeOwner : NSObject +@end + +@implementation QemuCocoaPasteboardTypeOwner + +- (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSPasteboardType)type +{ + if (type != NSPasteboardTypeString) { + return; + } + + with_iothread_lock(^{ + QemuClipboardInfo *info = qemu_clipboard_info_ref(cbinfo); + qemu_event_reset(&cbevent); + qemu_clipboard_request(info, QEMU_CLIPBOARD_TYPE_TEXT); + + while (info == cbinfo && + info->types[QEMU_CLIPBOARD_TYPE_TEXT].available && + info->types[QEMU_CLIPBOARD_TYPE_TEXT].data == NULL) { + qemu_mutex_unlock_iothread(); + qemu_event_wait(&cbevent); + qemu_mutex_lock_iothread(); + } + + if (info == cbinfo) { + NSData *data = [[NSData alloc] initWithBytes:info->types[QEMU_CLIPBOARD_TYPE_TEXT].data + length:info->types[QEMU_CLIPBOARD_TYPE_TEXT].size]; + [sender setData:data forType:NSPasteboardTypeString]; + [data release]; + } + + qemu_clipboard_info_unref(info); + }); +} + +@end + +static QemuCocoaPasteboardTypeOwner *cbowner; + +static void cocoa_clipboard_notify(Notifier *notifier, void *data); +static void cocoa_clipboard_request(QemuClipboardInfo *info, + QemuClipboardType type); + +static QemuClipboardPeer cbpeer = { + .name = "cocoa", + .update = { .notify = cocoa_clipboard_notify }, + .request = cocoa_clipboard_request +}; + +static void cocoa_clipboard_notify(Notifier *notifier, void *data) +{ + QemuClipboardInfo *info = data; + + if (info->owner == &cbpeer || info->selection != QEMU_CLIPBOARD_SELECTION_CLIPBOARD) { + return; + } + + if (info != cbinfo) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + qemu_clipboard_info_unref(cbinfo); + cbinfo = qemu_clipboard_info_ref(info); + cbchangecount = [[NSPasteboard generalPasteboard] declareTypes:@[NSPasteboardTypeString] owner:cbowner]; + [pool release]; + } + + qemu_event_set(&cbevent); +} + +static void cocoa_clipboard_request(QemuClipboardInfo *info, + QemuClipboardType type) +{ + NSData *text; + + switch (type) { + case QEMU_CLIPBOARD_TYPE_TEXT: + text = [[NSPasteboard generalPasteboard] dataForType:NSPasteboardTypeString]; + if (text) { + qemu_clipboard_set_data(&cbpeer, info, type, + [text length], [text bytes], true); + [text release]; + } + break; + default: + break; + } +} + /* * The startup process for the OSX/Cocoa UI is complicated, because * OSX insists that the UI runs on the initial main thread, and so we @@ -1792,6 +1884,7 @@ static void *call_qemu_main(void *opaque) COCOA_DEBUG("Second thread: calling qemu_main()\n"); status = qemu_main(gArgc, gArgv, *_NSGetEnviron()); COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n"); + [cbowner release]; exit(status); } @@ -1914,6 +2007,18 @@ static void cocoa_refresh(DisplayChangeListener *dcl) [cocoaView setAbsoluteEnabled:YES]; }); } + + if (cbchangecount != [[NSPasteboard generalPasteboard] changeCount]) { + qemu_clipboard_info_unref(cbinfo); + cbinfo = qemu_clipboard_info_new(&cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD); + if ([[NSPasteboard generalPasteboard] availableTypeFromArray:@[NSPasteboardTypeString]]) { + cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; + } + qemu_clipboard_update(cbinfo); + cbchangecount = [[NSPasteboard generalPasteboard] changeCount]; + qemu_event_set(&cbevent); + } + [pool release]; } @@ -1939,6 +2044,10 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) // register vga output callbacks register_displaychangelistener(&dcl); + + qemu_event_init(&cbevent, false); + cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init]; + qemu_clipboard_peer_register(&cbpeer); } static QemuDisplay qemu_display_cocoa = { From d7696ff884e35c6dacf83a7cbe3355e3b0a90125 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 17 Jun 2021 11:31:13 +0900 Subject: [PATCH 3/5] input: Add lang1 and lang2 to QKeyCode lang1 and lang2 represents the keys with the same names in the keyboard/keypad usage page (0x07) included in the "HID Usage Tables for Universal Serial Bus (USB)" version 1.22. Although the keys are described as "Hangul/English toggle key" and "Hanja conversion key" in the specification, the meaning depends on the variety of the keyboard, and it will be used as the representations of Kana and Eisu keys on Japanese Macs in qemu_input_map_osx_to_qcode, which is used by ui/gtk. Signed-off-by: Akihiko Odaki Message-Id: <20210617023113.2441-2-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- qapi/ui.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qapi/ui.json b/qapi/ui.json index 1052ca9c38..90b44c5c5c 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -786,6 +786,9 @@ # @muhenkan: since 2.12 # @katakanahiragana: since 2.12 # +# @lang1: since 6.1 +# @lang2: since 6.1 +# # 'sysrq' was mistakenly added to hack around the fact that # the ps2 driver was not generating correct scancodes sequences # when 'alt+print' was pressed. This flaw is now fixed and the @@ -818,7 +821,8 @@ 'audionext', 'audioprev', 'audiostop', 'audioplay', 'audiomute', 'volumeup', 'volumedown', 'mediaselect', 'mail', 'calculator', 'computer', - 'ac_home', 'ac_back', 'ac_forward', 'ac_refresh', 'ac_bookmarks' ] } + 'ac_home', 'ac_back', 'ac_forward', 'ac_refresh', 'ac_bookmarks', + 'lang1', 'lang2' ] } ## # @KeyValue: From ddc717581c2ea45c38423d24f2157572c73b8e75 Mon Sep 17 00:00:00 2001 From: Ahmed Abouzied Date: Tue, 1 Jun 2021 19:41:18 +0200 Subject: [PATCH 4/5] Add display suboptions to man pages Updates man pages with the suboptions for the `-display`. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/128 Buglink: https://bugs.launchpad.net/qemu/+bug/1620660 Signed-off-by: Ahmed Abouzied Message-Id: <20210601174117.661-1-email@aabouzied.com> Signed-off-by: Gerd Hoffmann --- qemu-options.hx | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index 14258784b3..ba3ca9da1d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1819,11 +1819,22 @@ SRST old style -sdl/-curses/... options. Use ``-display help`` to list the available display types. Valid values for type are - ``sdl`` + ``spice-app[,gl=on|off]`` + Start QEMU as a Spice server and launch the default Spice client + application. The Spice server will redirect the serial consoles + and QEMU monitors. (Since 4.0) + + ``sdl[,window-close=on|off][,gl=on|core|es|off]`` + Display video output via SDL (usually in a separate graphics window; see the SDL documentation for other possibilities). - ``curses`` + ``gtk[,grab-on-hover=on|off][,gl=on|off]`` + Display video output in a GTK window. This interface provides + drop-down menus and other UI elements to configure and control + the VM during runtime. + + ``curses [,charset=]`` Display video output via curses. For graphics device models which support a text mode, QEMU can display this output using a curses/ncurses interface. Nothing is displayed when the graphics @@ -1834,6 +1845,11 @@ SRST ``charset=CP850`` for IBM CP850 encoding. The default is ``CP437``. + ``egl-headless[,rendernode]`` + Offload all OpenGL operations to a local DRI device. For any + graphical display, this display needs to be paired with either + VNC or SPICE displays. + ``none`` Do not display video output. The guest will still see an emulated graphics card, but its output will not be displayed to @@ -1842,23 +1858,8 @@ SRST also changes the destination of the serial and parallel port data. - ``gtk`` - Display video output in a GTK window. This interface provides - drop-down menus and other UI elements to configure and control - the VM during runtime. - ``vnc`` - Start a VNC server on display - ``egl-headless`` - Offload all OpenGL operations to a local DRI device. For any - graphical display, this display needs to be paired with either - VNC or SPICE displays. - - ``spice-app`` - Start QEMU as a Spice server and launch the default Spice client - application. The Spice server will redirect the serial consoles - and QEMU monitors. (Since 4.0) ERST DEF("nographic", 0, QEMU_OPTION_nographic, From 66c2207fd28a6025792fbb75151ee848b911dc35 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 15 Jun 2021 11:04:39 +0200 Subject: [PATCH 5/5] ui: Make the DisplayType enum entries conditional Libvirt's "domcapabilities" command has a way to state whether certain graphic frontends are available in QEMU or not. Originally, libvirt looked at the "--help" output of the QEMU binary to determine whether SDL was available or not (by looking for the "-sdl" parameter in the help text), but since libvirt stopped doing this analysis of the help text, the detection of SDL is currently broken, see: https://bugzilla.redhat.com/show_bug.cgi?id=1790902 QEMU should provide a way via the QMP interface instead. A simple way, without introducing additional commands, is to make the DisplayType enum entries conditional, so that the enum only contains the entries if the corresponding CONFIG_xxx switches have been set. This of course only gives an indication which possibilities have been enabled during compile-time of QEMU (and does not take into account whether modules are later available or not for example - for this we'd need a separate command), but anyway, this should already be good enough for the above bug ticket, and it's a good idea anyway to make the QMP interface conditional here, so let's simply do it. Signed-off-by: Thomas Huth Message-Id: <20210615090439.70926-1-thuth@redhat.com> Signed-off-by: Gerd Hoffmann --- qapi/ui.json | 23 +++++++++++++++++------ softmmu/vl.c | 20 +++++++++++++++++--- ui/console.c | 8 +++++++- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index 90b44c5c5c..fd9677d48e 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1130,9 +1130,16 @@ # ## { 'enum' : 'DisplayType', - 'data' : [ 'default', 'none', 'gtk', 'sdl', - 'egl-headless', 'curses', 'cocoa', - 'spice-app'] } + 'data' : [ + { 'name': 'default' }, + { 'name': 'none' }, + { 'name': 'gtk', 'if': 'defined(CONFIG_GTK)' }, + { 'name': 'sdl', 'if': 'defined(CONFIG_SDL)' }, + { 'name': 'egl-headless', + 'if': 'defined(CONFIG_OPENGL) && defined(CONFIG_GBM)' }, + { 'name': 'curses', 'if': 'defined(CONFIG_CURSES)' }, + { 'name': 'cocoa', 'if': 'defined(CONFIG_COCOA)' }, + { 'name': 'spice-app', 'if': 'defined(CONFIG_SPICE)'} ] } ## # @DisplayOptions: @@ -1156,9 +1163,13 @@ '*show-cursor' : 'bool', '*gl' : 'DisplayGLMode' }, 'discriminator' : 'type', - 'data' : { 'gtk' : 'DisplayGTK', - 'curses' : 'DisplayCurses', - 'egl-headless' : 'DisplayEGLHeadless'} } + 'data' : { + 'gtk': { 'type': 'DisplayGTK', 'if': 'defined(CONFIG_GTK)' }, + 'curses': { 'type': 'DisplayCurses', 'if': 'defined(CONFIG_CURSES)' }, + 'egl-headless': { 'type': 'DisplayEGLHeadless', + 'if': 'defined(CONFIG_OPENGL) && defined(CONFIG_GBM)' } + } +} ## # @query-display-options: diff --git a/softmmu/vl.c b/softmmu/vl.c index feb4d201f3..d99e2cbdbf 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -1068,6 +1068,7 @@ static void parse_display(const char *p) * Not clear yet what happens to them long-term. Should * replaced by something better or deprecated and dropped. */ +#if defined(CONFIG_SDL) dpy.type = DISPLAY_TYPE_SDL; while (*opts) { const char *nextopt; @@ -1131,6 +1132,10 @@ static void parse_display(const char *p) } opts = nextopt; } +#else + error_report("SDL display supported is not available in this binary"); + exit(1); +#endif } else if (strstart(p, "vnc", &opts)) { /* * vnc isn't a (local) DisplayType but a protocol for remote @@ -1867,13 +1872,22 @@ static void qemu_apply_machine_options(void) static void qemu_create_early_backends(void) { MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); +#if defined(CONFIG_SDL) + const bool use_sdl = (dpy.type == DISPLAY_TYPE_SDL); +#else + const bool use_sdl = false; +#endif +#if defined(CONFIG_GTK) + const bool use_gtk = (dpy.type == DISPLAY_TYPE_GTK); +#else + const bool use_gtk = false; +#endif - if ((alt_grab || ctrl_grab) && dpy.type != DISPLAY_TYPE_SDL) { + if ((alt_grab || ctrl_grab) && !use_sdl) { error_report("-alt-grab and -ctrl-grab are only valid " "for SDL, ignoring option"); } - if (dpy.has_window_close && - (dpy.type != DISPLAY_TYPE_GTK && dpy.type != DISPLAY_TYPE_SDL)) { + if (dpy.has_window_close && !use_gtk && !use_sdl) { error_report("-no-quit is only valid for GTK and SDL, " "ignoring option"); } diff --git a/ui/console.c b/ui/console.c index 2de5f4105b..1103b65314 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2370,13 +2370,19 @@ void qemu_display_register(QemuDisplay *ui) bool qemu_display_find_default(DisplayOptions *opts) { static DisplayType prio[] = { +#if defined(CONFIG_GTK) DISPLAY_TYPE_GTK, +#endif +#if defined(CONFIG_SDL) DISPLAY_TYPE_SDL, +#endif +#if defined(CONFIG_COCOA) DISPLAY_TYPE_COCOA +#endif }; int i; - for (i = 0; i < ARRAY_SIZE(prio); i++) { + for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { if (dpys[prio[i]] == NULL) { ui_module_load_one(DisplayType_str(prio[i])); }