mirror of https://github.com/xemu-project/xemu.git
ui: better cocoa integration (ui info + clipboard).
ui: add lang1+lang2 keys, fixes, doc updates. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmDUQyQACgkQTLbY7tPo cTiBVRAAqFkg8yKkLQnIYy8CEZGwwDzUxbtxSMQNqnWRVSrz0HVLvmTViMDy25VP lMc+6Eo4l3rx+pyrubc39WtqtRp8uZwCwMnOXstt6VSayiiP61KkYahmSzz2PztM AHYcfMbPbARqKXo2c7z4GrrcoXmUpuAEkez7c6mLl17+2S+IQNUp45Wk0p6rZO6J I2gh0CaG4/e5981Hxwai4FkRm+WxV9PcBPJU3ac5tOrdt16CC0HWE8+KmCs2RA1L JxRuvBgXZH0pHCKnAPT2jOQOq/LLHddbCiaSgiWTtqMEXqF5WSVn+NDkrrOnkfI3 hsDlHiqMRWBtxiK3slCmFU0GSPz3mijy3PZHx+0RSaLSwbp1EHOERSxDX/n/n6jj 2Y8sQV5NVmo5YtxrgXdw8fVexaS5C6Gp1mHTThgzepsTAHEgY8vmmLr3M1GXwE4M yaD+4hVnDP0tCXP6nVYsYdrE+fgY14JE5EvQWqJ5v23tiudrQ2Ol+mcOiyhLE/aF 2B1HJFQ5J0h2rxSxIIr0IEwWiAE3ohqvUMfNao9o6+ICFDPVbH3pJnl1N1NGQzbl JAcvlkpGskNp5pNmQcgj2b9xT7S/jCR8qlBVQwAefixSPWuscEqNSv7yBRKTaZ/f kOlVXqU4go0F7FGe9c5alezRRZGCpQz9Wc9DFFsJX0yUogojFVU= =skkB -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/ui-20210624-pull-request' into staging ui: better cocoa integration (ui info + clipboard). ui: add lang1+lang2 keys, fixes, doc updates. # gpg: Signature made Thu 24 Jun 2021 09:32:36 BST # gpg: using RSA key A0328CFFB93A17A79901FE7D4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full] # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full] # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full] # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * remotes/kraxel/tags/ui-20210624-pull-request: ui: Make the DisplayType enum entries conditional Add display suboptions to man pages input: Add lang1 and lang2 to QKeyCode ui/cocoa: Add clipboard support ui/cocoa: Set UI information Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
e0da9171e0
|
@ -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 */
|
||||
|
|
29
qapi/ui.json
29
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:
|
||||
|
@ -1126,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:
|
||||
|
@ -1152,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:
|
||||
|
|
|
@ -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=<encoding>]``
|
||||
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<file>]``
|
||||
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 <arg>
|
||||
|
||||
``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,
|
||||
|
|
20
softmmu/vl.c
20
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");
|
||||
}
|
||||
|
|
|
@ -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 ||
|
||||
|
|
158
ui/cocoa.m
158
ui/cocoa.m
|
@ -28,6 +28,7 @@
|
|||
#include <crt_externs.h>
|
||||
|
||||
#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);
|
||||
|
@ -518,6 +523,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 +1214,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
|
||||
{
|
||||
|
@ -1711,6 +1763,93 @@ static void addRemovableDevicesMenuItems(void)
|
|||
qapi_free_BlockInfoList(pointerToFree);
|
||||
}
|
||||
|
||||
@interface QemuCocoaPasteboardTypeOwner : NSObject<NSPasteboardTypeOwner>
|
||||
@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
|
||||
|
@ -1745,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);
|
||||
}
|
||||
|
||||
|
@ -1836,6 +1976,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
|
||||
|
@ -1865,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];
|
||||
}
|
||||
|
||||
|
@ -1890,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 = {
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue