Recent changes allowed the Wayland driver to properly deal with numpad keys depending on numlock status. This MR ensures that numlock and other lock state is properly synced, and concludes the fix for https://bugs.winehq.org/show_bug.cgi?id=56397.
This MR also syncs the key press state when a window gains keyboard focus, including any modifier press state. Note that we currently ignore the modifier press information from the `modifier` event, since: 1. it doesn't differentiate between left-right keys, 2. mod press state changes will be normally preceded by a matching key event, so we are able to sync properly. However, if we find that we need to handle mod press state changes without corresponding key events, we will need to implement some sensible way to sync these, too.
Although I would like for the driver work exclusively in terms of scancodes, I still needed to use vkeys in this case, since this is how wineserver expresses keyboard state at the moment. This means that I had to introduce/duplicate some extra scan->vkey logic in the driver (e.g., numpad keys based on numlock state) to get things right.
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/winewayland.drv/wayland_keyboard.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index bdef56e8f0c..5a3a26bb52e 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -633,11 +633,21 @@ static BOOL get_async_key_state(BYTE state[256]) return ret; }
+static void send_vkey(HWND hwnd, WORD vkey, DWORD flags) +{ + INPUT input = {.type = INPUT_KEYBOARD}; + UINT scan = NtUserMapVirtualKeyEx(vkey, MAPVK_VK_TO_VSC_EX, keyboard_hkl); + input.ki.wVk = vkey; + input.ki.wScan = scan & 0xff; + input.ki.dwFlags = flags; + if (scan & ~0xff) input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + NtUserSendHardwareInput(hwnd, 0, &input, 0); +} + static void release_all_keys(HWND hwnd) { BYTE state[256]; int vkey; - INPUT input = {.type = INPUT_KEYBOARD};
get_async_key_state(state);
@@ -648,16 +658,7 @@ static void release_all_keys(HWND hwnd) /* Skip left/right-agnostic modifier vkeys. */ if (vkey == VK_SHIFT || vkey == VK_CONTROL || vkey == VK_MENU) continue;
- if (state[vkey] & 0x80) - { - UINT scan = NtUserMapVirtualKeyEx(vkey, MAPVK_VK_TO_VSC_EX, - keyboard_hkl); - input.ki.wVk = vkey; - input.ki.wScan = scan & 0xff; - input.ki.dwFlags = KEYEVENTF_KEYUP; - if (scan & ~0xff) input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; - NtUserSendHardwareInput(hwnd, 0, &input, 0); - } + if (state[vkey] & 0x80) send_vkey(hwnd, vkey, KEYEVENTF_KEYUP); } }
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56397 --- dlls/winewayland.drv/wayland_keyboard.c | 64 +++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index 5a3a26bb52e..a29f2a7c94a 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -633,6 +633,17 @@ static BOOL get_async_key_state(BYTE state[256]) return ret; }
+static void set_async_key_state(const BYTE state[256]) +{ + SERVER_START_REQ(set_key_state) + { + req->async = 1; + wine_server_add_data(req, state, 256); + wine_server_call(req); + } + SERVER_END_REQ; +} + static void send_vkey(HWND hwnd, WORD vkey, DWORD flags) { INPUT input = {.type = INPUT_KEYBOARD}; @@ -662,6 +673,52 @@ static void release_all_keys(HWND hwnd) } }
+static void update_key_lock_state(HWND hwnd, BYTE keystate[256], WORD vkey, BOOL lock) +{ + BOOL prev_lock = !!(keystate[vkey] & 0x01); + + /* If the the vkey is pressed use the existing Windows lock state, since it + * might differ from the Wayland state (e.g., due to Windows clearing lock + * state on key press rather than release). */ + if (keystate[vkey] & 0x80) return; + /* If the vkey already has the requested lock state there is nothing to do. */ + if (prev_lock == lock) return; + + TRACE_(key)("vkey=0x%03x lock=%d state=0x%02x\n", vkey, lock, keystate[vkey]); + + send_vkey(hwnd, vkey, 0); + send_vkey(hwnd, vkey, KEYEVENTF_KEYUP); + + /* Ensure we have the proper state in case key events were blocked by hooks. */ + if (get_async_key_state(keystate) && !!(keystate[vkey] & 0x01) == prev_lock) + { + WARN("keystate %x not changed (%#.2x), probably blocked by hooks\n", + vkey, keystate[vkey]); + keystate[vkey] ^= 0x01; + set_async_key_state(keystate); + } +} + +static void update_keyboard_lock_state(HWND hwnd, struct xkb_state *xkb_state) +{ + BYTE keystate[256]; + + if (!get_async_key_state(keystate)) return; + + update_key_lock_state(hwnd, keystate, VK_CAPITAL, + xkb_state_mod_name_is_active(xkb_state, + XKB_MOD_NAME_CAPS, + XKB_STATE_MODS_LOCKED)); + update_key_lock_state(hwnd, keystate, VK_NUMLOCK, + xkb_state_mod_name_is_active(xkb_state, + XKB_MOD_NAME_NUM, + XKB_STATE_MODS_LOCKED)); + update_key_lock_state(hwnd, keystate, VK_SCROLL, + xkb_state_mod_name_is_active(xkb_state, + "ScrollLock", + XKB_STATE_MODS_LOCKED)); +} + /********************************************************************** * Keyboard handling */ @@ -852,10 +909,11 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar uint32_t xkb_group) { struct wayland_keyboard *keyboard = &process_wayland.keyboard; + HWND hwnd = wayland_keyboard_get_focused_hwnd();
- if (!wayland_keyboard_get_focused_hwnd()) return; + if (!hwnd) return;
- TRACE("serial=%u mods_depressed=%#x mods_latched=%#x mods_locked=%#x xkb_group=%d stub!\n", + TRACE("serial=%u mods_depressed=%#x mods_latched=%#x mods_locked=%#x xkb_group=%d\n", serial, mods_depressed, mods_latched, mods_locked, xkb_group);
pthread_mutex_lock(&keyboard->mutex); @@ -865,7 +923,7 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar
set_current_xkb_group(xkb_group);
- /* FIXME: Sync wine modifier state with XKB modifier state. */ + update_keyboard_lock_state(hwnd, keyboard->xkb_state); }
static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
From: Alexandros Frantzis alexandros.frantzis@collabora.com
--- dlls/winewayland.drv/wayland_keyboard.c | 112 ++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 2 + 2 files changed, 114 insertions(+)
diff --git a/dlls/winewayland.drv/wayland_keyboard.c b/dlls/winewayland.drv/wayland_keyboard.c index a29f2a7c94a..32157e15518 100644 --- a/dlls/winewayland.drv/wayland_keyboard.c +++ b/dlls/winewayland.drv/wayland_keyboard.c @@ -719,6 +719,94 @@ static void update_keyboard_lock_state(HWND hwnd, struct xkb_state *xkb_state) XKB_STATE_MODS_LOCKED)); }
+static BOOL vkey_is_mod(WORD vkey) +{ + return vkey == VK_LSHIFT || vkey == VK_RSHIFT || + vkey == VK_LCONTROL || vkey == VK_RCONTROL || + vkey == VK_RMENU || vkey == VK_LMENU || + vkey == VK_CAPITAL || vkey == VK_NUMLOCK || vkey == VK_SCROLL; +} + +static WORD scan2vkey(WORD scan, BYTE keystate[256]) +{ + WORD vkey; + + vkey = NtUserMapVirtualKeyEx(scan, MAPVK_VSC_TO_VK_EX, keyboard_hkl); + + if ((keystate[VK_NUMLOCK] & 0x01) && !(keystate[VK_SHIFT] & 0x80)) + { + switch (vkey) + { + case VK_INSERT: vkey = VK_NUMPAD0; break; + case VK_END: vkey = VK_NUMPAD1; break; + case VK_DOWN: vkey = VK_NUMPAD2; break; + case VK_NEXT: vkey = VK_NUMPAD3; break; + case VK_LEFT: vkey = VK_NUMPAD4; break; + case VK_CLEAR: vkey = VK_NUMPAD5; break; + case VK_RIGHT: vkey = VK_NUMPAD6; break; + case VK_HOME: vkey = VK_NUMPAD7; break; + case VK_UP: vkey = VK_NUMPAD8; break; + case VK_PRIOR: vkey = VK_NUMPAD9; break; + case VK_DELETE: vkey = VK_DECIMAL; break; + default: break; + } + } + + return vkey; +} + +static void update_keyboard_pressed_keys(HWND hwnd, struct wl_array *pressed_keys, + BOOL mods_only) +{ + uint32_t *key; + BYTE keystate[256]; + BYTE pressed[256] = {0}; + WORD vkey; + + if (!get_async_key_state(keystate)) return; + + wl_array_for_each(key, pressed_keys) + pressed[scan2vkey(key2scan(*key), keystate) & 0xff] = TRUE; + + for (vkey = 1; vkey < 256; ++vkey) + { + /* 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; + /* Skip wrong type of vkeys. */ + if (vkey_is_mod(vkey) != mods_only) continue; + /* Skip unchanged vkeys. */ + if (!pressed[vkey] == !(keystate[vkey] & 0x80)) continue; + + TRACE_(key)("vkey=0x%03x pressed=%d state=0x%02x\n", + vkey, pressed[vkey], keystate[vkey]); + + send_vkey(hwnd, vkey, pressed[vkey] ? 0 : KEYEVENTF_KEYUP); + + /* Ensure we have the proper state in case key events were blocked by hooks. */ + if (get_async_key_state(keystate) && (!(keystate[vkey] & 0x80)) == pressed[vkey]) + { + WARN("keystate %x not changed (%#.2x), probably blocked by hooks\n", + vkey, keystate[vkey]); + keystate[vkey] ^= 0x80; + if ((keystate[VK_LSHIFT] | keystate[VK_RSHIFT]) & 0x80) + keystate[VK_SHIFT] |= 0x80; + else + keystate[VK_SHIFT] &= ~0x80; + if ((keystate[VK_LCONTROL] | keystate[VK_RCONTROL]) & 0x80) + keystate[VK_CONTROL] |= 0x80; + else + keystate[VK_CONTROL] &= ~0x80; + if ((keystate[VK_LMENU] | keystate[VK_RMENU]) & 0x80) + keystate[VK_MENU] |= 0x80; + else + keystate[VK_MENU] &= ~0x80; + set_async_key_state(keystate); + } + } +} + /********************************************************************** * Keyboard handling */ @@ -827,6 +915,11 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
pthread_mutex_lock(&keyboard->mutex); keyboard->focused_hwnd = hwnd; + /* Store pressed keys to be processed after we receive the modifier state. */ + wl_array_release(&keyboard->pressed_keys_on_enter); + wl_array_init(&keyboard->pressed_keys_on_enter); + wl_array_copy(&keyboard->pressed_keys_on_enter, keys); + keyboard->have_pressed_keys_on_enter = TRUE; pthread_mutex_unlock(&keyboard->mutex);
NtUserPostMessage(keyboard->focused_hwnd, WM_INPUTLANGCHANGEREQUEST, 0 /*FIXME*/, @@ -910,20 +1003,37 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar { struct wayland_keyboard *keyboard = &process_wayland.keyboard; HWND hwnd = wayland_keyboard_get_focused_hwnd(); + struct wl_array pressed_keys; + BOOL have_pressed_keys;
if (!hwnd) return;
TRACE("serial=%u mods_depressed=%#x mods_latched=%#x mods_locked=%#x xkb_group=%d\n", serial, mods_depressed, mods_latched, mods_locked, xkb_group);
+ wl_array_init(&pressed_keys); + pthread_mutex_lock(&keyboard->mutex); xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, xkb_group); + if ((have_pressed_keys = keyboard->have_pressed_keys_on_enter)) + { + pressed_keys = keyboard->pressed_keys_on_enter; + wl_array_init(&keyboard->pressed_keys_on_enter); + keyboard->have_pressed_keys_on_enter = FALSE; + } pthread_mutex_unlock(&keyboard->mutex);
set_current_xkb_group(xkb_group);
+ /* Update the modifier key press state first, so that subsequent pressed + * keys will be modified accordingly and the lock state state logic, which + * depends on having mod key events before modifiers, will work properly. */ + if (have_pressed_keys) update_keyboard_pressed_keys(hwnd, &pressed_keys, TRUE); update_keyboard_lock_state(hwnd, keyboard->xkb_state); + if (have_pressed_keys) update_keyboard_pressed_keys(hwnd, &pressed_keys, FALSE); + + wl_array_release(&pressed_keys); }
static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, @@ -979,6 +1089,7 @@ void wayland_keyboard_init(struct wl_keyboard *wl_keyboard) pthread_mutex_lock(&keyboard->mutex); keyboard->wl_keyboard = wl_keyboard; keyboard->xkb_context = xkb_context; + wl_array_init(&keyboard->pressed_keys_on_enter); pthread_mutex_unlock(&keyboard->mutex); wl_keyboard_add_listener(keyboard->wl_keyboard, &keyboard_listener, NULL); } @@ -1006,6 +1117,7 @@ void wayland_keyboard_deinit(void) xkb_state_unref(keyboard->xkb_state); keyboard->xkb_state = NULL; } + wl_array_release(&keyboard->pressed_keys_on_enter); pthread_mutex_unlock(&keyboard->mutex);
if (rxkb_context) diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 7852c23b690..612097b5a5b 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -79,6 +79,8 @@ struct wayland_keyboard struct xkb_context *xkb_context; struct xkb_state *xkb_state; HWND focused_hwnd; + struct wl_array pressed_keys_on_enter; + BOOL have_pressed_keys_on_enter; pthread_mutex_t mutex; };
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=145855
Your paranoid android.
=== debian11b (64 bit WoW report) ===
mfmediaengine: mfmediaengine.c:2564: Test failed: Unexpected time 0.000000.
Rémi Bernon (@rbernon) commented about dlls/winewayland.drv/wayland_keyboard.c:
- /* If the vkey already has the requested lock state there is nothing to do. */
- if (prev_lock == lock) return;
- TRACE_(key)("vkey=0x%03x lock=%d state=0x%02x\n", vkey, lock, keystate[vkey]);
- send_vkey(hwnd, vkey, 0);
- send_vkey(hwnd, vkey, KEYEVENTF_KEYUP);
- /* Ensure we have the proper state in case key events were blocked by hooks. */
- if (get_async_key_state(keystate) && !!(keystate[vkey] & 0x01) == prev_lock)
- {
WARN("keystate %x not changed (%#.2x), probably blocked by hooks\n",
vkey, keystate[vkey]);
keystate[vkey] ^= 0x01;
set_async_key_state(keystate);
- }
I understand the need to update the keyboard state, and I know winex11 does this, but sending key input when window gains focus feels wrong to me.
Rémi Bernon (@rbernon) commented about dlls/winewayland.drv/wayland_keyboard.c:
if ((have_pressed_keys = keyboard->have_pressed_keys_on_enter))
{
pressed_keys = keyboard->pressed_keys_on_enter;
wl_array_init(&keyboard->pressed_keys_on_enter);
keyboard->have_pressed_keys_on_enter = FALSE;
} pthread_mutex_unlock(&keyboard->mutex);
set_current_xkb_group(xkb_group);
/* Update the modifier key press state first, so that subsequent pressed
* keys will be modified accordingly and the lock state state logic, which
* depends on having mod key events before modifiers, will work properly. */
if (have_pressed_keys) update_keyboard_pressed_keys(hwnd, &pressed_keys, TRUE); update_keyboard_lock_state(hwnd, keyboard->xkb_state);
if (have_pressed_keys) update_keyboard_pressed_keys(hwnd, &pressed_keys, FALSE);
Same thing here, updating the state of non-modifier keys might be desired, but sending keyboard input feels wrong.
Rémi Bernon (@rbernon) commented about dlls/winewayland.drv/wayland_keyboard.c:
/* Skip left/right-agnostic modifier vkeys. */ if (vkey == VK_SHIFT || vkey == VK_CONTROL || vkey == VK_MENU) continue;
if (state[vkey] & 0x80)
{
UINT scan = NtUserMapVirtualKeyEx(vkey, MAPVK_VK_TO_VSC_EX,
keyboard_hkl);
input.ki.wVk = vkey;
input.ki.wScan = scan & 0xff;
input.ki.dwFlags = KEYEVENTF_KEYUP;
if (scan & ~0xff) input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
NtUserSendHardwareInput(hwnd, 0, &input, 0);
}
Fwiw, I probably missed that previously and it may be what Wayland spec suggest, but I also don't think we should send key release input when focus is lost. That's not what Windows does.
In order to avoid having to duplicate the scan2vk mapping in the drivers, I would suggest maybe extending the `NtUserSendHardwareInput` call, for instance with a custom flag and add support for syncing the full keystate, passing the state array in lparam (and some dummy INPUT_KEYBOARD structure).
You could then probably re-use the `send_hardware_message` request HID report data to pass the new vkey state to wineserver, and handle the key updates there, that would save some `get_async_keystate` roundtrips.
On Fri May 24 08:54:04 2024 +0000, Rémi Bernon wrote:
Fwiw, I probably missed that previously and it may be what Wayland spec suggest, but I also don't think we should send key release input when focus is lost. That's not what Windows does.
So the concern (in this and other cases) is strictly about the key events, and you would prefer if we just updated the async keyboard state?
Although that should work fine in many cases, one issue is autorepeat functionality, since it is handled (at the moment) in win32u based on key messages. We would like to: 1. Stop autorepeat when a window loses focus. 2. Start autorepeat of key if it is pressed when gaining focus.
(1) is critical to get right, but seems easy enough to achieve without key event involvement. If this matches Windows behavior, it would make sense to integrate a "has focus" check into the win32u repeat timer callback. At the moment it needs to happen from the driver too because we don't update window focus when handling the keyboard leave event (but also a special `NtUserSendHardwareInput` invocation with all scancodes up (see below) could deal with that in win32u).
(2) seems less straightforward, since there isn't currently any feedback from the wineserver to hwnd when the async key state changes. But if we use a special `NtUserSendHardwareInput` invocation as you suggest below, perhaps wineserver can detect the scenario and do something sensible here.
On Fri May 24 10:33:28 2024 +0000, Rémi Bernon wrote:
In order to avoid having to duplicate the scan2vk mapping in the drivers, I would suggest maybe extending the `NtUserSendHardwareInput` call, for instance with a custom flag and add support for syncing the full keystate, passing the state array in lparam (and some dummy INPUT_KEYBOARD structure). You could then probably re-use the `send_hardware_message` request HID report data to pass the new vkey state to wineserver, and handle the key updates there, that would save some `get_async_keystate` roundtrips.
If the driver needs to pass vkeys (that's what I understood by 'state array'), it already needs to do some translation along the lines of `dlls/win32u/input.c:map_scan_to_kbd_vkey`. Perhaps we can directly send scan codes instead and win32u can perform the translation to vkey[256] itself?:
`NtUserSendHardwareInput(SEND_HWMSG_SCANCODES, lparam=<array of pressed scancodes>)`
On Fri May 24 10:33:28 2024 +0000, Alexandros Frantzis wrote:
If the driver needs to pass vkeys (that's what I understood by 'state array'), it already needs to do some translation along the lines of `dlls/win32u/input.c:map_scan_to_kbd_vkey`. Perhaps we can directly send scan codes instead and win32u can perform the translation to vkey[256] itself?: `NtUserSendHardwareInput(SEND_HWMSG_SCANCODES, lparam=<array of pressed scancodes>)`
Yes that's what I meant.
On Fri May 24 10:33:28 2024 +0000, Alexandros Frantzis wrote:
So the concern (in this and other cases) is strictly about the key events, and you would prefer if we just updated the async keyboard state? Although that should work fine in many cases, one issue is autorepeat functionality, since it is handled (at the moment) in win32u based on key messages. We would like to:
- Stop autorepeat when a window loses focus.
- Start autorepeat of key if it is pressed when gaining focus.
(1) is critical to get right, but seems easy enough to achieve without key event involvement. If this matches Windows behavior, it would make sense to integrate a "has focus" check into the win32u repeat timer callback. At the moment it needs to happen from the driver too because we don't update window focus when handling the keyboard leave event (but also a special `NtUserSendHardwareInput` invocation with all scancodes up (see below) could deal with that in win32u). (2) seems less straightforward, since there isn't currently any feedback from the wineserver to hwnd when the async key state changes. But if we use a special `NtUserSendHardwareInput` invocation as you suggest below, perhaps wineserver can detect the scenario and do something sensible here.
Hmm... after looking a bit more into it seems like I'm maybe wrong, but the trick may actually be that Windows apparently handles key repeat on the driver or hardware level. (Still, it doesn't send KEYUP on focus loss)
So, the way I've implemented it, with a process timer that repeats WM_KEYDOWN message isn't correct, and in particular it won't trigger repetitions of rawinput messages (which could cause other input issues with winewayland).
Maybe it would be better to implement it the same way as we now have pointer input repeat (see `queue_pointer_message` in `server/queue.c`), repeating input for any key that is pressed.
Then, assuming input is dispatched correctly, and assuming wineserver is informed of focus loss so that the old window doesn't receive the repeated input, you could simply keep the repeating active until another window gains focus and can tell wineserver of the updated key state, clearing repeat timer or starting repeat again if any keys are still pressed.
Then, assuming input is dispatched correctly, and assuming wineserver is informed of focus loss so that the old window doesn't receive the repeated input, you could simply keep the repeating active until another window gains focus and can tell wineserver of the updated key state, clearing repeat timer or starting repeat again if any keys are still pressed.
Alright, let me experiment a bit with these ideas. A couple of early thoughts:
1. Concerning giving up focus on the keyboard leave event, I remember that we don't do it because of unfortunate side-effects, but I will need to revisit to remember the details and see if/how we can make some progress here.
2. Wouldn't keeping the repeat active in wineserver cause unnecessary cpu usage? Should we effectively pause the repeat until we have a new focused window?
Wouldn't keeping the repeat active in wineserver cause unnecessary cpu usage? Should we effectively pause the repeat until we have a new focused window?
I don't think we need to worry about a couple of wake-ups for key input for the rare cases where keys are kept pressed on focus loss, but sure, that's a possible optimization.
Note that Windows applications can listen to input while in the background, and although that doesn't work very well for the moment, it's still supposedly the correct approach.
OTOH we also don't receive any input when no Wine window is focused, so we could consider that it's not worth trying to be correct here. We also currently kind of miss the ability to tell whether no Wine window is active or whether the desktop window really is: winex11 focuses the desktop window when all windows are losing focus, but the virtual desktop window can very well be focused on its own.
llyyr (@llyyr) commented about dlls/winewayland.drv/wayland_keyboard.c:
pthread_mutex_lock(&keyboard->mutex); keyboard->focused_hwnd = hwnd;
- /* Store pressed keys to be processed after we receive the modifier state. */
How is this information used by winewayland? The protocol does not guarantee that the keys in the array are in the order they were pressed, this information only exists for things like on screen keyboards that may want to e.g. highlight currently held keys.