From: Gustavo Noronha Silva <gustavo@noronha.eti.br>
On Mac OS X the Option key maps to Alt and Command to Super/Meta. This change
swaps them around so that Alt is the key closer to the space bar and Meta/Super
is between Control and Alt, like on non-Mac keyboards.
It is a cocoa display option, enabled by default.
Signed-off-by: Gustavo Noronha Silva <gustavo@noronha.eti.br>
---
qapi/ui.json | 7 +++++-
qemu-options.hx | 1 +
ui/cocoa.m | 66 +++++++++++++++++++++++++++++++++++++++++--------
3 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/qapi/ui.json b/qapi/ui.json
index 77bc00fd0d..02db684251 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1098,9 +1098,14 @@
# a global grab on key events. (default: off)
# See https://support.apple.com/en-in/guide/mac-help/mh32356/mac
#
+# @swap-option-command: Swaps the Option and Command keys so that their key codes
+# match their position on non-Mac keyboards and you can use
+# Meta/Super and Alt where you expect them. (default: on)
+#
##
{ 'struct' : 'DisplayCocoa',
- 'data' : { '*full-grab' : 'bool' } }
+ 'data' : { '*full-grab' : 'bool',
+ '*swap-option-command' : 'bool' } }
##
# @DisplayType:
diff --git a/qemu-options.hx b/qemu-options.hx
index a77505241f..d6137eedac 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1785,6 +1785,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
#endif
#if defined(CONFIG_COCOA)
"-display cocoa[,full_grab=on|off]\n"
+ " [,swap_option_command=on|off]\n"
#endif
#if defined(CONFIG_OPENGL)
"-display egl-headless[,rendernode=<file>]\n"
diff --git a/ui/cocoa.m b/ui/cocoa.m
index f1e4449082..879e568a9d 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -73,6 +73,7 @@
int width;
int height;
bool full_grab;
+ bool swap_option_command;
} QEMUScreen;
static void cocoa_update(DisplayChangeListener *dcl,
@@ -327,6 +328,7 @@ - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
- (BOOL) isMouseGrabbed;
- (BOOL) isAbsoluteEnabled;
- (BOOL) isFullGrabEnabled;
+- (BOOL) isSwapOptionCommandEnabled;
- (float) cdx;
- (float) cdy;
- (QEMUScreen) gscreen;
@@ -648,6 +650,13 @@ - (void) setFullGrab:(id)sender to:(BOOL)value
screen.full_grab = value;
}
+- (void) setSwapOptionCommand:(id)sender
+{
+ COCOA_DEBUG("QemuCocoaView: setSwapOptionCommand\n");
+
+ screen.swap_option_command = true;
+}
+
- (void) toggleKey: (int)keycode {
qkbd_state_key_event(kbd, keycode, !qkbd_state_key_get(kbd, keycode));
}
@@ -797,12 +806,22 @@ - (bool) handleEventLocked:(NSEvent *)event
qkbd_state_key_event(kbd, Q_KEY_CODE_CTRL_R, false);
}
if (!(modifiers & NSEventModifierFlagOption)) {
- qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
- qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
+ if ([self isSwapOptionCommandEnabled]) {
+ qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
+ qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
+ } else {
+ qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
+ qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
+ }
}
if (!(modifiers & NSEventModifierFlagCommand)) {
- qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
- qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
+ if ([self isSwapOptionCommandEnabled]) {
+ qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
+ qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
+ } else {
+ qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
+ qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
+ }
}
switch ([event type]) {
@@ -834,13 +853,21 @@ - (bool) handleEventLocked:(NSEvent *)event
case kVK_Option:
if (!!(modifiers & NSEventModifierFlagOption)) {
- [self toggleKey:Q_KEY_CODE_ALT];
+ if ([self isSwapOptionCommandEnabled]) {
+ [self toggleKey:Q_KEY_CODE_META_L];
+ } else {
+ [self toggleKey:Q_KEY_CODE_ALT];
+ }
}
break;
case kVK_RightOption:
if (!!(modifiers & NSEventModifierFlagOption)) {
- [self toggleKey:Q_KEY_CODE_ALT_R];
+ if ([self isSwapOptionCommandEnabled]) {
+ [self toggleKey:Q_KEY_CODE_META_R];
+ } else {
+ [self toggleKey:Q_KEY_CODE_ALT_R];
+ }
}
break;
@@ -848,15 +875,21 @@ - (bool) handleEventLocked:(NSEvent *)event
case kVK_Command:
if (isMouseGrabbed &&
!!(modifiers & NSEventModifierFlagCommand)) {
- [self toggleKey:Q_KEY_CODE_META_L];
- }
+ if ([self isSwapOptionCommandEnabled]) {
+ [self toggleKey:Q_KEY_CODE_ALT];
+ } else {
+ [self toggleKey:Q_KEY_CODE_META_L];
+ } }
break;
case kVK_RightCommand:
if (isMouseGrabbed &&
!!(modifiers & NSEventModifierFlagCommand)) {
- [self toggleKey:Q_KEY_CODE_META_R];
- }
+ if ([self isSwapOptionCommandEnabled]) {
+ [self toggleKey:Q_KEY_CODE_ALT_R];
+ } else {
+ [self toggleKey:Q_KEY_CODE_META_R];
+ } }
break;
}
break;
@@ -1085,6 +1118,7 @@ - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {
- (BOOL) isMouseGrabbed {return isMouseGrabbed;}
- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
- (BOOL) isFullGrabEnabled {return screen.full_grab;}
+- (BOOL) isSwapOptionCommandEnabled {return screen.swap_option_command;}
- (float) cdx {return cdx;}
- (float) cdy {return cdy;}
- (QEMUScreen) gscreen {return screen;}
@@ -1271,6 +1305,13 @@ - (void) setFullGrab:(id)sender to:(BOOL)value
[cocoaView setFullGrab:sender to:value];
}
+- (void) setSwapOptionCommand:(id)sender
+{
+ COCOA_DEBUG("QemuCocoaAppController: setSwapOptionCommand\n");
+
+ [cocoaView setSwapOptionCommand:sender];
+}
+
/* Tries to find then open the specified filename */
- (void) openDocumentation: (NSString *) filename
{
@@ -1953,6 +1994,11 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
[[controller delegate] setFullGrab: nil to:opts->u.cocoa.full_grab];
});
}
+ if (!opts->u.cocoa.has_swap_option_command || opts->u.cocoa.swap_option_command) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[controller delegate] setSwapOptionCommand: nil];
+ });
+ }
if (opts->has_show_cursor && opts->show_cursor) {
cursor_hide = 0;
};
--
2.24.3 (Apple Git-128)
gustavo@noronha.eti.br writes:
> From: Gustavo Noronha Silva <gustavo@noronha.eti.br>
>
> On Mac OS X the Option key maps to Alt and Command to Super/Meta. This change
> swaps them around so that Alt is the key closer to the space bar and Meta/Super
> is between Control and Alt, like on non-Mac keyboards.
>
> It is a cocoa display option, enabled by default.
>
> Signed-off-by: Gustavo Noronha Silva <gustavo@noronha.eti.br>
> ---
> qapi/ui.json | 7 +++++-
> qemu-options.hx | 1 +
> ui/cocoa.m | 66 +++++++++++++++++++++++++++++++++++++++++--------
> 3 files changed, 63 insertions(+), 11 deletions(-)
>
> diff --git a/qapi/ui.json b/qapi/ui.json
> index 77bc00fd0d..02db684251 100644
> --- a/qapi/ui.json
> +++ b/qapi/ui.json
> @@ -1098,9 +1098,14 @@
##
# @DisplayCocoa:
#
# Cocoa display options.
#
# @full-grab: Capture all key presses, including system combos. This
# requires accessibility permissions, since it performs
> # a global grab on key events. (default: off)
> # See https://support.apple.com/en-in/guide/mac-help/mh32356/mac
> #
> +# @swap-option-command: Swaps the Option and Command keys so that their key codes
Please use imperative mood consistently: "Swap", like "Capture" above.
> +# match their position on non-Mac keyboards and you can use
> +# Meta/Super and Alt where you expect them. (default: on)
> +#
Drop the blank comment line, and break your lines a bit earlier.
> ##
Like this:
##
# @DisplayCocoa:
#
# Cocoa display options.
#
# @full-grab: Capture all key presses, including system combos. This
# requires accessibility permissions, since it performs
# a global grab on key events. (default: off)
# See https://support.apple.com/en-in/guide/mac-help/mh32356/mac
#
# @swap-option-command: Swap the Option and Command keys so that their key
# codes match their position on non-Mac keyboards and
# you can use Meta/Super and Alt where you expect
# them. (default: on)
##
> { 'struct' : 'DisplayCocoa',
> - 'data' : { '*full-grab' : 'bool' } }
> + 'data' : { '*full-grab' : 'bool',
> + '*swap-option-command' : 'bool' } }
>
> ##
> # @DisplayType:
With the doc comment tidied up, QAPI schema
Acked-by: Markus Armbruster <armbru@redhat.com>
> @@ -797,12 +806,22 @@ - (bool) handleEventLocked:(NSEvent *)event
> qkbd_state_key_event(kbd, Q_KEY_CODE_CTRL_R, false);
> }
> if (!(modifiers & NSEventModifierFlagOption)) {
> - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
> - qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
> + if ([self isSwapOptionCommandEnabled]) {
> + qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
> + qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
> + } else {
> + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
> + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
> + }
> }
> if (!(modifiers & NSEventModifierFlagCommand)) {
> - qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
> - qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
> + if ([self isSwapOptionCommandEnabled]) {
> + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT, false);
> + qkbd_state_key_event(kbd, Q_KEY_CODE_ALT_R, false);
> + } else {
> + qkbd_state_key_event(kbd, Q_KEY_CODE_META_L, false);
> + qkbd_state_key_event(kbd, Q_KEY_CODE_META_R, false);
> + }
> }
Wouldn't it be easier to swap the bits in the modifiers variable once
instead of having lots of isSwapOptionCommandEnabled checks in the code?
take care,
Gerd
Hey again, On Fri, Apr 30, 2021, at 5:04 AM, 'Gerd Hoffmann ' wrote: > Wouldn't it be easier to swap the bits in the modifiers variable once > instead of having lots of isSwapOptionCommandEnabled checks in the code? Good point, since a local variable is used it's definitely easier. I'll do the change and test, will be in v2. Cheers, Gustavo
© 2016 - 2026 Red Hat, Inc.