aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkihiko Odaki <akihiko.odaki@gmail.com>2021-03-10 23:46:02 +0900
committerGerd Hoffmann <kraxel@redhat.com>2021-03-11 10:06:44 +0100
commit6d73bb643aa725348aabe6a885ac5fb0b7f70252 (patch)
treeb450eebba94917bd80712d59acad50267a806ab1
parenta0f973f931680ae8bd847054aca7f658abb9c18f (diff)
ui/cocoa: Clear modifiers whenever possible
ui/cocoa does not receive NSEventTypeFlagsChanged when it is not active, and the modifier state can be desynchronized in such a situation. [NSEvent -modifierFlags] tells whether a modifier is *not* pressed, so check it whenever receiving an event and clear the modifier if it is not pressed. Note that [NSEvent -modifierFlags] does not tell if a certain modifier *is* pressed because the documented mask for [NSEvent -modifierFlags] generalizes left shift and right shift, for example. CapsLock is the only exception. The pressed state is synchronized only with NSEventTypeFlagsChanged. This change also removes modifier keys from keycode map. If they are input with NSEventTypeKeyDown or NSEventTypeKeyUp, it leads to desynchronization. Although such a situation is not observed, they are removed just in case. Moreover, QKbdState is introduced for automatic key state tracking. Thanks to Konstantin Nazarov for testing and finding a bug in this change: https://gist.github.com/akihikodaki/87df4149e7ca87f18dc56807ec5a1bc5#gistcomment-3659419 Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com> Message-Id: <20210310144602.58528-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-rw-r--r--ui/cocoa.m148
1 files changed, 76 insertions, 72 deletions
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 4753bb2f88..f71c59711f 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -30,6 +30,7 @@
#include "qemu-common.h"
#include "ui/console.h"
#include "ui/input.h"
+#include "ui/kbd-state.h"
#include "sysemu/sysemu.h"
#include "sysemu/runstate.h"
#include "sysemu/cpu-throttle.h"
@@ -190,14 +191,6 @@ const int mac_to_qkeycode_map[] = {
[kVK_ANSI_Comma] = Q_KEY_CODE_COMMA,
[kVK_ANSI_Period] = Q_KEY_CODE_DOT,
[kVK_ANSI_Slash] = Q_KEY_CODE_SLASH,
- [kVK_Shift] = Q_KEY_CODE_SHIFT,
- [kVK_RightShift] = Q_KEY_CODE_SHIFT_R,
- [kVK_Control] = Q_KEY_CODE_CTRL,
- [kVK_RightControl] = Q_KEY_CODE_CTRL_R,
- [kVK_Option] = Q_KEY_CODE_ALT,
- [kVK_RightOption] = Q_KEY_CODE_ALT_R,
- [kVK_Command] = Q_KEY_CODE_META_L,
- [0x36] = Q_KEY_CODE_META_R, /* There is no kVK_RightCommand */
[kVK_Space] = Q_KEY_CODE_SPC,
[kVK_ANSI_Keypad0] = Q_KEY_CODE_KP_0,
@@ -307,7 +300,7 @@ static void handleAnyDeviceErrors(Error * err)
NSWindow *fullScreenWindow;
float cx,cy,cw,ch,cdx,cdy;
pixman_image_t *pixman_image;
- BOOL modifiers_state[256];
+ QKbdState *kbd;
BOOL isMouseGrabbed;
BOOL isFullscreen;
BOOL isAbsoluteEnabled;
@@ -354,6 +347,7 @@ QemuCocoaView *cocoaView;
screen.width = frameRect.size.width;
screen.height = frameRect.size.height;
+ kbd = qkbd_state_init(dcl.con);
}
return self;
@@ -367,6 +361,7 @@ QemuCocoaView *cocoaView;
pixman_image_unref(pixman_image);
}
+ qkbd_state_free(kbd);
[super dealloc];
}
@@ -604,19 +599,8 @@ QemuCocoaView *cocoaView;
}
}
-- (void) toggleModifier: (int)keycode {
- // 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]);
-}
-
-- (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);
+- (void) toggleKey: (int)keycode {
+ qkbd_state_key_event(kbd, keycode, !qkbd_state_key_get(kbd, keycode));
}
// Does the work of sending input to the monitor
@@ -710,57 +694,86 @@ QemuCocoaView *cocoaView;
static bool switched_to_fullscreen = false;
// Location of event in virtual screen coordinates
NSPoint p = [self screenLocationOfEvent:event];
+ NSUInteger modifiers = [event modifierFlags];
+
+ // emulate caps lock keydown and keyup
+ if (!!(modifiers & NSEventModifierFlagCapsLock) !=
+ qkbd_state_modifier_get(kbd, QKBD_MOD_CAPSLOCK)) {
+ qkbd_state_key_event(kbd, Q_KEY_CODE_CAPS_LOCK, true);
+ qkbd_state_key_event(kbd, Q_KEY_CODE_CAPS_LOCK, false);
+ }
+
+ if (!(modifiers & NSEventModifierFlagShift)) {
+ qkbd_state_key_event(kbd, Q_KEY_CODE_SHIFT, false);
+ qkbd_state_key_event(kbd, Q_KEY_CODE_SHIFT_R, false);
+ }
+ if (!(modifiers & NSEventModifierFlagControl)) {
+ qkbd_state_key_event(kbd, Q_KEY_CODE_CTRL, false);
+ 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 (!(modifiers & NSEventModifierFlagCommand)) {
+ 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]) {
case NSEventTypeFlagsChanged:
- if ([event keyCode] == 0) {
- // When the Cocoa keyCode is zero that means keys should be
- // synthesized based on the values in in the eventModifiers
- // bitmask.
-
- if (qemu_console_is_graphic(NULL)) {
- NSUInteger modifiers = [event modifierFlags];
+ switch ([event keyCode]) {
+ case kVK_Shift:
+ if (!!(modifiers & NSEventModifierFlagShift)) {
+ [self toggleKey:Q_KEY_CODE_SHIFT];
+ }
+ break;
- if (!!(modifiers & NSEventModifierFlagCapsLock) != !!modifiers_state[Q_KEY_CODE_CAPS_LOCK]) {
- [self toggleStatefulModifier:Q_KEY_CODE_CAPS_LOCK];
+ case kVK_RightShift:
+ if (!!(modifiers & NSEventModifierFlagShift)) {
+ [self toggleKey:Q_KEY_CODE_SHIFT_R];
}
- if (!!(modifiers & NSEventModifierFlagShift) != !!modifiers_state[Q_KEY_CODE_SHIFT]) {
- [self toggleModifier:Q_KEY_CODE_SHIFT];
+ break;
+
+ case kVK_Control:
+ if (!!(modifiers & NSEventModifierFlagControl)) {
+ [self toggleKey:Q_KEY_CODE_CTRL];
}
- if (!!(modifiers & NSEventModifierFlagControl) != !!modifiers_state[Q_KEY_CODE_CTRL]) {
- [self toggleModifier:Q_KEY_CODE_CTRL];
+ break;
+
+ case kVK_RightControl:
+ if (!!(modifiers & NSEventModifierFlagControl)) {
+ [self toggleKey:Q_KEY_CODE_CTRL_R];
}
- if (!!(modifiers & NSEventModifierFlagOption) != !!modifiers_state[Q_KEY_CODE_ALT]) {
- [self toggleModifier:Q_KEY_CODE_ALT];
+ break;
+
+ case kVK_Option:
+ if (!!(modifiers & NSEventModifierFlagOption)) {
+ [self toggleKey:Q_KEY_CODE_ALT];
}
- if (!!(modifiers & NSEventModifierFlagCommand) != !!modifiers_state[Q_KEY_CODE_META_L]) {
- [self toggleModifier:Q_KEY_CODE_META_L];
+ break;
+
+ case kVK_RightOption:
+ if (!!(modifiers & NSEventModifierFlagOption)) {
+ [self toggleKey:Q_KEY_CODE_ALT_R];
}
- }
- } else {
- keycode = cocoa_keycode_to_qemu([event keyCode]);
- }
+ break;
- if ((keycode == Q_KEY_CODE_META_L || keycode == Q_KEY_CODE_META_R)
- && !isMouseGrabbed) {
- /* Don't pass command key changes to guest unless mouse is grabbed */
- keycode = 0;
- }
+ /* Don't pass command key changes to guest unless mouse is grabbed */
+ case kVK_Command:
+ if (isMouseGrabbed &&
+ !!(modifiers & NSEventModifierFlagCommand)) {
+ [self toggleKey:Q_KEY_CODE_META_L];
+ }
+ break;
- if (keycode) {
- // emulate caps lock and num lock keydown and keyup
- if (keycode == Q_KEY_CODE_CAPS_LOCK ||
- keycode == Q_KEY_CODE_NUM_LOCK) {
- [self toggleStatefulModifier:keycode];
- } else if (qemu_console_is_graphic(NULL)) {
- if (switched_to_fullscreen) {
- switched_to_fullscreen = false;
- } else {
- [self toggleModifier:keycode];
+ case kVK_RightCommand:
+ if (isMouseGrabbed &&
+ !!(modifiers & NSEventModifierFlagCommand)) {
+ [self toggleKey:Q_KEY_CODE_META_R];
}
- }
+ break;
}
-
break;
case NSEventTypeKeyDown:
keycode = cocoa_keycode_to_qemu([event keyCode]);
@@ -800,7 +813,7 @@ QemuCocoaView *cocoaView;
}
if (qemu_console_is_graphic(NULL)) {
- qemu_input_event_send_key_qcode(dcl.con, keycode, true);
+ qkbd_state_key_event(kbd, keycode, true);
} else {
[self handleMonitorInput: event];
}
@@ -815,7 +828,7 @@ QemuCocoaView *cocoaView;
}
if (qemu_console_is_graphic(NULL)) {
- qemu_input_event_send_key_qcode(dcl.con, keycode, false);
+ qkbd_state_key_event(kbd, keycode, false);
}
break;
case NSEventTypeMouseMoved:
@@ -999,17 +1012,8 @@ QemuCocoaView *cocoaView;
*/
- (void) raiseAllKeys
{
- const int max_index = ARRAY_SIZE(modifiers_state);
-
with_iothread_lock(^{
- int index;
-
- 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);
- }
- }
+ qkbd_state_lift_all_keys(kbd);
});
}
@end