From nobody Mon Feb 9 03:52:30 2026 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=1615350795; cv=none; d=zohomail.com; s=zohoarc; b=TLlBxjuWSGn15tlKp1cxqwAbeoKkaSYo/pxsfI9DlgDgjqt4/I1VVTvvr+6a87RaPBVUQLq62j6T9V80MVDffbdjDPFexQXQlE7Lxp3srdafc3X5+0t8nxp/BOIniwkT//wfqbW9auDXwr1QjeC71CMdEPymvMndqAgAKek1p0M= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1615350795; 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=81/zDxGA/GAe20UdlArsvzRltxNMvMaxAmiGKLao1cM=; b=ieO0xfgiQYh0Ttzct/tYk9gAeOAH+VOBGLjcU7oQKXj+g2qlnq8Vrx8yP3WziMtDy5aRtADDzrtoX6JaCx++5rWep7V1axda8b5jMYvug1WrtwymWoo4adOKaq0tNh9wOTjKgKTy6HhQdIA1YZhr1XCbBTxl2lfFCprdgCc6/Zw= 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 1615350795843929.7698000467635; Tue, 9 Mar 2021 20:33:15 -0800 (PST) Received: from localhost ([::1]:42180 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lJqX4-00069q-BC for importer@patchew.org; Tue, 09 Mar 2021 23:33:14 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50426) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lJqO7-0007wj-U1 for qemu-devel@nongnu.org; Tue, 09 Mar 2021 23:23:59 -0500 Received: from mail-pl1-x629.google.com ([2607:f8b0:4864:20::629]:39688) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1lJqO5-0003tJ-Ua for qemu-devel@nongnu.org; Tue, 09 Mar 2021 23:23:59 -0500 Received: by mail-pl1-x629.google.com with SMTP id j6so7835346plx.6 for ; Tue, 09 Mar 2021 20:23:56 -0800 (PST) Received: from localhost.localdomain ([2400:4050:c360:8200:681d:e6e5:d1b0:3153]) by smtp.gmail.com with ESMTPSA id l15sm4380652pjq.9.2021.03.09.20.23.53 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 09 Mar 2021 20:23:54 -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=81/zDxGA/GAe20UdlArsvzRltxNMvMaxAmiGKLao1cM=; b=mEYadOwRzd4++rNi6Xt8SA10ZZ5rqOYDm7iuIj+TpDKPwmxxyH9AEW1Zc0WuQ2Zgtg VjdP/rs2ALlUK1nEXHiUrUASCVOwxfcjT8Lz6l0H5dEjAO0ECM7i/91acfsKrn0TfsqK obZw6Jn1DQomzhGu1wizsi/oQw6eVBJF6oH+In3SEZt0ruJ44Q4EQXOG3lat0gzRRtji R4G8M3wR3xCiOpx50uIK8oJlToWdlg3Db9zcMYglUZVsgblIOfgeysc5lfn9+vg2Loyg Vwm5D4j58hGs9RCSqujxZ+dmCtfCZITDY1woEa7UpUC9ePcEOxNawFqHyj0zqpkaS6nj ROAw== 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=81/zDxGA/GAe20UdlArsvzRltxNMvMaxAmiGKLao1cM=; b=aV3GHniH47+7XHtDpd/hHoBU0ycOSrn0l1VI4E1CkEICNdVx/9Ay7zHTQE5hUJ3Ydw T3NGJEu3sDsw6AVppTjY/idwVS1ol38em+Qb7wxHlfbCvUYEEprYggi007gunNwZHAsq m7/IBoRwNhWMIAE9GluNxlAqh3tsZZyjuLNc9WineruDJpVB9w7KzKexdfrVEZApUOLB Ph/IA2jMZy50K3GwnCv2fsWHz/8Jo09g1ARH850N+fkgnZ9Li/lDsZ7HMsLqkP8XFBSn sqZlHAlhoAstsG+HjCpWvS8RPckxtu8LvGHOwuyLQTmFoeHPkVCfkOXCHSllv74aBr2Y wsQQ== X-Gm-Message-State: AOAM530kTT7agHi0zm0ZCBmkDToHLu71/fIU9ysO4QaXvVjyyinJLtgp J5eH3Ihl8QkPG+WCr7Vhr/Ge0v1bxOTVPA== X-Google-Smtp-Source: ABdhPJz1HPWOPqVivMsgX3QjirrTR2V5/VkiOHUN1MoquqVcFlrm/axbiU7LK4XRrDFclGtmsPFipg== X-Received: by 2002:a17:90a:fd0b:: with SMTP id cv11mr1471898pjb.183.1615350235382; Tue, 09 Mar 2021 20:23:55 -0800 (PST) From: Akihiko Odaki To: Subject: [PATCH v2] ui/cocoa: Clear modifiers whenever possible Date: Wed, 10 Mar 2021 13:23:48 +0900 Message-Id: <20210310042348.21931-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::629; envelope-from=akihiko.odaki@gmail.com; helo=mail-pl1-x629.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 , 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. 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, 92 insertions(+), 56 deletions(-) diff --git a/ui/cocoa.m b/ui/cocoa.m index f27beb30e6e..2b6aea429f8 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -189,14 +189,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, @@ -615,9 +607,24 @@ - (void) toggleModifier: (int)keycode { qemu_input_event_send_key_qcode(dcl.con, keycode, modifiers_state[keyc= ode]); } =20 -- (void) toggleStatefulModifier: (int)keycode { +- (void) clearModifier: (int)keycode { + if (!modifiers_state[keycode]) { + return; + } + + // Clear the stored state. + modifiers_state[keycode] =3D NO; + // Send a keyup. + qemu_input_event_send_key_qcode(dcl.con, keycode, false); +} + +- (void) setStatefulModifier: (int)keycode down:(BOOL)down { + if (down =3D=3D modifiers_state[keycode]) { + return; + } + // Toggle the stored state. - modifiers_state[keycode] =3D !modifiers_state[keycode]; + modifiers_state[keycode] =3D down; // Generate keydown and keyup. qemu_input_event_send_key_qcode(dcl.con, keycode, true); qemu_input_event_send_key_qcode(dcl.con, keycode, false); @@ -714,57 +721,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]; =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]; + // emulate caps lock keydown and keyup + [self setStatefulModifier:Q_KEY_CODE_CAPS_LOCK down:!!(modifiers & NSE= ventModifierFlagCapsLock)]; =20 - if (!!(modifiers & NSEventModifierFlagCapsLock) !=3D != !modifiers_state[Q_KEY_CODE_CAPS_LOCK]) { - [self toggleStatefulModifier:Q_KEY_CODE_CAPS_LOCK]; - } - if (!!(modifiers & NSEventModifierFlagShift) !=3D !!mo= difiers_state[Q_KEY_CODE_SHIFT]) { - [self toggleModifier:Q_KEY_CODE_SHIFT]; - } - if (!!(modifiers & NSEventModifierFlagControl) !=3D !!= modifiers_state[Q_KEY_CODE_CTRL]) { - [self toggleModifier:Q_KEY_CODE_CTRL]; - } - if (!!(modifiers & NSEventModifierFlagOption) !=3D !!m= odifiers_state[Q_KEY_CODE_ALT]) { - [self toggleModifier:Q_KEY_CODE_ALT]; - } - if (!!(modifiers & NSEventModifierFlagCommand) !=3D !!= modifiers_state[Q_KEY_CODE_META_L]) { - [self toggleModifier:Q_KEY_CODE_META_L]; - } - } - } else { - keycode =3D cocoa_keycode_to_qemu([event keyCode]); - } - - 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; - } + if (qemu_console_is_graphic(NULL)) { + if (!(modifiers & NSEventModifierFlagShift)) { + [self clearModifier:Q_KEY_CODE_SHIFT]; + [self clearModifier:Q_KEY_CODE_SHIFT_R]; + } + if (!(modifiers & NSEventModifierFlagControl)) { + [self clearModifier:Q_KEY_CODE_CTRL]; + [self clearModifier:Q_KEY_CODE_CTRL_R]; + } + if (!(modifiers & NSEventModifierFlagOption)) { + [self clearModifier:Q_KEY_CODE_ALT]; + [self clearModifier:Q_KEY_CODE_ALT_R]; + } + if (!(modifiers & NSEventModifierFlagCommand)) { + [self clearModifier:Q_KEY_CODE_META_L]; + [self clearModifier:Q_KEY_CODE_META_R]; + } + } =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]; - } + switch ([event type]) { + case NSEventTypeFlagsChanged: + if (qemu_console_is_graphic(NULL)) { + switch ([event keyCode]) { + case kVK_Shift: + if (!!(modifiers & NSEventModifierFlagShift)) { + [self toggleModifier:Q_KEY_CODE_SHIFT]; + } + break; + + case kVK_RightShift: + if (!!(modifiers & NSEventModifierFlagShift)) { + [self toggleModifier:Q_KEY_CODE_SHIFT_R]; + } + break; + + case kVK_Control: + if (!!(modifiers & NSEventModifierFlagControl)) { + [self toggleModifier:Q_KEY_CODE_CTRL]; + } + break; + + case kVK_RightControl: + if (!!(modifiers & NSEventModifierFlagControl)) { + [self toggleModifier:Q_KEY_CODE_CTRL_R]; + } + break; + + case kVK_Option: + if (!!(modifiers & NSEventModifierFlagOption)) { + [self toggleModifier:Q_KEY_CODE_ALT]; + } + break; + + case kVK_RightOption: + if (!!(modifiers & NSEventModifierFlagOption)) { + [self toggleModifier:Q_KEY_CODE_ALT_R]; + } + break; + + /* Don't pass command key changes to guest unless mous= e is grabbed */ + case kVK_Command: + if (isMouseGrabbed && + !!(modifiers & NSEventModifierFlagCommand)) { + [self toggleModifier:Q_KEY_CODE_META_L]; + } + break; + + case kVK_RightCommand: + if (isMouseGrabbed && + !!(modifiers & NSEventModifierFlagCommand)) { + [self toggleModifier:Q_KEY_CODE_META_R]; + } + break; } } - break; case NSEventTypeKeyDown: keycode =3D cocoa_keycode_to_qemu([event keyCode]); --=20 2.24.3 (Apple Git-128)