From nobody Fri Apr 11 16:12:24 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1615387677; cv=none; d=zohomail.com; s=zohoarc; b=CX2Bjzx+ZBYsC/eGot8PD7eSnhMY+bJqr7t9FKYJwOcYbIW8cUifXZ6UJ8pZZ06xnBeuqY8/nnXTCmS4TC5rH2SXBb2hRYFmijyw7lwFgbmm4pfJfxSEW9AJI3yYprp8m4CSIqhrHgCnndNAJ7roySLvB+eQQxKi7oCLn14atUM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1615387677; h=Content-Transfer-Encoding:Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject; bh=HCxl6f7DCzbd6lpJWFn7bQPobY8yW9uHMyQpi4zXnYE=; b=UEZzS5XqIFUlaezrFm/qM6YG94lfb/nf0a9t9BaCjIXBAvGwg7m4h3lVywI9S4UtwvahVF1H5z7fl8SI5N2xjwzVPyhx6imB1FAqeJfxFUbP1KvCLKDC5KPDJUYRilCqoZW5uCyqLzLQHsEwhyLPb6iNjW4Y9lU4V7R3k4YXmWo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1615387677356273.01287761941785; Wed, 10 Mar 2021 06:47:57 -0800 (PST) Received: from localhost ([::1]:58742 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lK07u-0004q4-QG for importer@patchew.org; Wed, 10 Mar 2021 09:47:54 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40022) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lK06I-0004Bn-6A for qemu-devel@nongnu.org; Wed, 10 Mar 2021 09:46:14 -0500 Received: from mail-pj1-x1035.google.com ([2607:f8b0:4864:20::1035]:44728) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1lK06F-0001X7-TG for qemu-devel@nongnu.org; Wed, 10 Mar 2021 09:46:13 -0500 Received: by mail-pj1-x1035.google.com with SMTP id kk2-20020a17090b4a02b02900c777aa746fso7602866pjb.3 for ; Wed, 10 Mar 2021 06:46:11 -0800 (PST) Received: from localhost.localdomain ([2400:4050:c360:8200:681d:e6e5:d1b0:3153]) by smtp.gmail.com with ESMTPSA id i14sm6807117pjh.17.2021.03.10.06.46.07 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 10 Mar 2021 06:46:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=HCxl6f7DCzbd6lpJWFn7bQPobY8yW9uHMyQpi4zXnYE=; b=FgmtEUP62uh5WXIMzFyphZO0BIKDQff1FfmstyRgbpwFIggJptHlld/EjNmgSMwJNq um2hldRqmdltSzz7Bf4TcHvMBNWghftiy7LLS6xW5N1bJbD/GG5ukxYgktJ3ibn8jq3j l8tb1uVT0/K6Y6AdyBdSwb+ZnNIBnZJjvJu+mDCioS75B2f05xAphAI3H60iMH+FwWLw pvopbi55WmqAXrQWFqPhjYwBjXhi8T4seI+PgSTuyihw5KQJX+ElV1AAb4x3fGp32y2k B1M1HD8DwSazagk4yCrKHM2sBb/LBsmNqLEL/e+jhFfi5qHK1JZUmDwZDDVeiCUGgyuL QPjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=HCxl6f7DCzbd6lpJWFn7bQPobY8yW9uHMyQpi4zXnYE=; b=FVomQGaIo0729L/41CIOTVg9NXCA9ipcrjDttwhsPKXLEMGVRJYD/RowgIBnMeRiG8 JUmm92gFvGXTRutUG6DvQjezAdg782uowWnl7yjSaXLXHEY+qnLzxbQi4fFLGuyA4nEh qYXYpon3eXstSXmbldIMTmePeIvGPoa4Sm3BcjXIzL3B8da85sfGQZ0VCBCkqIs0HKP8 gdJ+qLPBzKPseFxwP/vj/T2FHfJl3GHx2Pw4n7zO2t52ThOyhjkT+N2NsU7Q0n4eWCPn E5Q9PqSNuxb42otIvQ8osQ8goyvTTgmFIHoKgjFHySJy2oz7RdhEH4idzYM6mMQmLNjc XjEw== X-Gm-Message-State: AOAM531q7Ncxjd6cAs5S9S9aBGBjPh6UDS9eqBoZzeehz1UQRHQnYzj2 CuY18AzL/X8wUyNo+15682k/a8TcTTqleg== X-Google-Smtp-Source: ABdhPJx/TA+SOKyA2EsHph7QfBB5/otd6j4ROWTF47uzUgpbKyPVFr/eZ9oudilvBO7r+NRgUe7pug== X-Received: by 2002:a17:902:cec8:b029:e4:a497:da8d with SMTP id d8-20020a170902cec8b02900e4a497da8dmr3310829plg.16.1615387569949; Wed, 10 Mar 2021 06:46:09 -0800 (PST) From: Akihiko Odaki To: Subject: [PATCH v3] ui/cocoa: Clear modifiers whenever possible Date: Wed, 10 Mar 2021 23:46:02 +0900 Message-Id: <20210310144602.58528-1-akihiko.odaki@gmail.com> X-Mailer: git-send-email 2.24.3 (Apple Git-128) MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1035; envelope-from=akihiko.odaki@gmail.com; helo=mail-pj1-x1035.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , Konstantin Nazarov , qemu-devel@nongnu.org, Akihiko Odaki , Gerd Hoffmann Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" 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#gistco= mment-3659419 Signed-off-by: Akihiko Odaki --- ui/cocoa.m | 148 +++++++++++++++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 72 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index f27beb30e6e..035f96aab04 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" @@ -189,14 +190,6 @@ static bool bool_with_iothread_lock(BoolCodeBlock bloc= k) [kVK_ANSI_Comma] =3D Q_KEY_CODE_COMMA, [kVK_ANSI_Period] =3D Q_KEY_CODE_DOT, [kVK_ANSI_Slash] =3D Q_KEY_CODE_SLASH, - [kVK_Shift] =3D Q_KEY_CODE_SHIFT, - [kVK_RightShift] =3D Q_KEY_CODE_SHIFT_R, - [kVK_Control] =3D Q_KEY_CODE_CTRL, - [kVK_RightControl] =3D Q_KEY_CODE_CTRL_R, - [kVK_Option] =3D Q_KEY_CODE_ALT, - [kVK_RightOption] =3D Q_KEY_CODE_ALT_R, - [kVK_Command] =3D Q_KEY_CODE_META_L, - [0x36] =3D Q_KEY_CODE_META_R, /* There is no kVK_RightCommand */ [kVK_Space] =3D Q_KEY_CODE_SPC, =20 [kVK_ANSI_Keypad0] =3D Q_KEY_CODE_KP_0, @@ -306,7 +299,7 @@ @interface QemuCocoaView : NSView 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; @@ -353,6 +346,7 @@ - (id)initWithFrame:(NSRect)frameRect =20 screen.width =3D frameRect.size.width; screen.height =3D frameRect.size.height; + kbd =3D qkbd_state_init(dcl.con); =20 } return self; @@ -366,6 +360,7 @@ - (void) dealloc pixman_image_unref(pixman_image); } =20 + qkbd_state_free(kbd); [super dealloc]; } =20 @@ -608,19 +603,8 @@ - (void) toggleFullScreen:(id)sender } } =20 -- (void) toggleModifier: (int)keycode { - // Toggle the stored state. - modifiers_state[keycode] =3D !modifiers_state[keycode]; - // Send a keyup or keydown depending on the state. - qemu_input_event_send_key_qcode(dcl.con, keycode, modifiers_state[keyc= ode]); -} - -- (void) toggleStatefulModifier: (int)keycode { - // Toggle the stored state. - modifiers_state[keycode] =3D !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)); } =20 // Does the work of sending input to the monitor @@ -714,57 +698,86 @@ - (bool) handleEventLocked:(NSEvent *)event static bool switched_to_fullscreen =3D false; // Location of event in virtual screen coordinates NSPoint p =3D [self screenLocationOfEvent:event]; + NSUInteger modifiers =3D [event modifierFlags]; + + // emulate caps lock keydown and keyup + if (!!(modifiers & NSEventModifierFlagCapsLock) !=3D + 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); + } =20 switch ([event type]) { case NSEventTypeFlagsChanged: - if ([event keyCode] =3D=3D 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 =3D [event modifierFlags]; + switch ([event keyCode]) { + case kVK_Shift: + if (!!(modifiers & NSEventModifierFlagShift)) { + [self toggleKey:Q_KEY_CODE_SHIFT]; + } + break; =20 - if (!!(modifiers & NSEventModifierFlagCapsLock) !=3D != !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) !=3D !!mo= difiers_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) !=3D !!= 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) !=3D !!m= odifiers_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) !=3D !!= 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 =3D cocoa_keycode_to_qemu([event keyCode]); - } + break; =20 - if ((keycode =3D=3D Q_KEY_CODE_META_L || keycode =3D=3D Q_KEY_= CODE_META_R) - && !isMouseGrabbed) { - /* Don't pass command key changes to guest unless mouse is g= rabbed */ - keycode =3D 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; =20 - if (keycode) { - // emulate caps lock and num lock keydown and keyup - if (keycode =3D=3D Q_KEY_CODE_CAPS_LOCK || - keycode =3D=3D Q_KEY_CODE_NUM_LOCK) { - [self toggleStatefulModifier:keycode]; - } else if (qemu_console_is_graphic(NULL)) { - if (switched_to_fullscreen) { - switched_to_fullscreen =3D false; - } else { - [self toggleModifier:keycode]; + case kVK_RightCommand: + if (isMouseGrabbed && + !!(modifiers & NSEventModifierFlagCommand)) { + [self toggleKey:Q_KEY_CODE_META_R]; } - } + break; } - break; case NSEventTypeKeyDown: keycode =3D cocoa_keycode_to_qemu([event keyCode]); @@ -804,7 +817,7 @@ - (bool) handleEventLocked:(NSEvent *)event } =20 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]; } @@ -819,7 +832,7 @@ - (bool) handleEventLocked:(NSEvent *)event } =20 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: @@ -1003,17 +1016,8 @@ - (QEMUScreen) gscreen {return screen;} */ - (void) raiseAllKeys { - const int max_index =3D ARRAY_SIZE(modifiers_state); - with_iothread_lock(^{ - int index; - - for (index =3D 0; index < max_index; index++) { - if (modifiers_state[index]) { - modifiers_state[index] =3D 0; - qemu_input_event_send_key_qcode(dcl.con, index, false); - } - } + qkbd_state_lift_all_keys(kbd); }); } @end --=20 2.24.3 (Apple Git-128)