[Bug 59650] New: wayland: Modifier keys (Alt/Ctrl) get stuck when window loses focus due to race condition in release_all_keys()
http://bugs.winehq.org/show_bug.cgi?id=59650 Bug ID: 59650 Summary: wayland: Modifier keys (Alt/Ctrl) get stuck when window loses focus due to race condition in release_all_keys() Product: Wine Version: 11.6 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: winewayland Assignee: wine-bugs@list.winehq.org Reporter: lldmakhbfcrmqyluns@nesopf.com Distribution: --- Symptoms: When using native Wayland, pressing a global window manager shortcut (like Alt+Tab or a custom Ctrl+Tab bound in KWin) causes the modifier key to become permanently "stuck" inside the Wine application. For example, after Alt+Tab out of the application and returning to it, the application behaves as if Alt is still held down, immediately activating the window's top menu bar (VK_MENU). The issue is absent in X Wayland. Steps to reproduce: Run any 2 application in Wayland Wine. Press Alt+Tab several times to trigger windows switching. The application acts as if the Alt modifier is still pressed. Technical Analysis (Root Cause): With WINEDEBUG=+waylanddrv,+keyboard,+key, the log shows that Wayland sends the key press event, immediately followed by the wl_keyboard.leave event. However, the key release is never generated by Wine. The bug is a Race Condition located in dlls/winewayland.drv/wayland_keyboard.c within the release_all_keys(HWND hwnd) function. Currently, when keyboard_handle_leave is called, it triggers release_all_keys. This function uses NtUserGetAsyncKeyboardState(state) to check which keys need a synthetic KEYUP event: c static void release_all_keys(HWND hwnd) { BYTE state[256]; NtUserGetAsyncKeyboardState(state); // ... sends KEYUP if state[vkey] & 0x80 } Because Wayland events are processed very fast, the initial wl_keyboard.key (pressed) event is sent to the Wine server queue via NtUserSendHardwareInput, but the server has not processed it yet. When NtUserGetAsyncKeyboardState is immediately called during the leave event, the server reports that the key is not pressed. Therefore, release_all_keys skips sending the KEYUP event. Milliseconds later, the server processes the KEYDOWN, and the key becomes stuck forever. Proposed Solution / Patch: The Wayland driver must not rely on the asynchronous Wine server state to release keys during focus loss. It should maintain its own local array of physically pressed evdev keys and release them accordingly. Here is a proposed patch to fix this issue: diff --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -79,6 +79,9 @@ static struct rxkb_context *rxkb_context; static HKL keyboard_hkl; /* the HKL matching the currently active xkb group */ +/* Local state to track physically pressed keys to avoid race conditions with wine server */ +static BYTE wayland_pressed_keys[KEY_MAX]; + void activate_keyboard_hkl(HWND hwnd, BOOL ime) { HKL hkl = ime && !is_ime_hkl(keyboard_hkl) ? get_ime_hkl(LOWORD(keyboard_hkl)) : keyboard_hkl; @@ -430,24 +433,23 @@ static void release_all_keys(HWND hwnd) { - BYTE state[256]; - int vkey; - INPUT input = {.type = INPUT_KEYBOARD}; - - NtUserGetAsyncKeyboardState(state); - - for (vkey = 1; vkey < 256; vkey++) + int key; + + for (key = 0; key < ARRAY_SIZE(wayland_pressed_keys); key++) { - /* Skip mouse buttons. */ - if (vkey < 7 && vkey != VK_CANCEL) continue; - /* Skip left/right-agnostic modifier vkeys. */ - if (vkey == VK_SHIFT || vkey == VK_CONTROL || vkey == VK_MENU) continue; - - if (state[vkey] & 0x80) + if (wayland_pressed_keys[key]) { - UINT scan = NtUserMapVirtualKeyEx(vkey, MAPVK_VK_TO_VSC_EX, - keyboard_hkl); - input.ki.wVk = vkey; + UINT scan = key2scan(key); + INPUT input = {0}; + + if (key == KEY_RIGHTALT) send_right_control(hwnd, WL_KEYBOARD_KEY_STATE_RELEASED); + + input.type = INPUT_KEYBOARD; + input.ki.wScan = (scan & 0x300) ? scan + 0xdf00 : scan; + input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; + if (scan & ~0xff) input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + + NtUserSendHardwareInput(hwnd, 0, &input, 0); + + wayland_pressed_keys[key] = 0; } } } @@ -524,6 +526,9 @@ InterlockedExchange(&process_wayland.input_serial, serial); + if (key < ARRAY_SIZE(wayland_pressed_keys)) + wayland_pressed_keys[key] = (state == WL_KEYBOARD_KEY_STATE_PRESSED); + if (!(hwnd = wayland_keyboard_get_focused_hwnd())) return; TRACE_(key)("serial=%u hwnd=%p key=%d scan=%#x state=%#x\n", serial, hwnd, key, scan, state); -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=59650 --- Comment #1 from T_Im <lldmakhbfcrmqyluns@nesopf.com> --- FYI: I'm not a C dev and don't plan to submit a formal patch. I'm just sharing analysis to help fix the bug for everyone. Feel free to use or rewrite the code. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=59650 T_Im <lldmakhbfcrmqyluns@nesopf.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |rbernon@codeweavers.com, | |stefan@codeweavers.com -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
participants (1)
-
WineHQ Bugzilla