Bug 513897 - Wayland client doesn't receive a key event after switching a keyboard layout
Summary: Wayland client doesn't receive a key event after switching a keyboard layout
Status: CONFIRMED
Alias: None
Product: kwin
Classification: Plasma
Component: wayland-generic (other bugs)
Version First Reported In: 6.5.4
Platform: Arch Linux Linux
: HI normal
Target Milestone: ---
Assignee: KWin default assignee
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2025-12-27 23:14 UTC by bjoel2
Modified: 2026-01-05 19:28 UTC (History)
2 users (show)

See Also:
Latest Commit:
Version Fixed/Implemented In:
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description bjoel2 2025-12-27 23:14:02 UTC
SUMMARY
After switching a keyboard layout, one of the keys used to switch layouts might get stuck in a state such that the wayland client won't receive a "Key" event upon releasing that key (but will receive a "Modifier" event).

STEPS TO REPRODUCE
1. Configure two or more keyboard layouts
2. Set the layout switch shortcut to Ctrl+Shift
3. run ``WAYLAND_DEBUG=client dolphin``
4. Input the following key sequence into an opened window:
  - Press CTRL
  - Press SHIFT
  - Release CTRL
  - Release SHIFT
  - Out of curiosity, press CTRL again
  - Release CTRL

OBSERVED RESULT
You will see something like this in terminal:
```
wl_keyboard#27.key(38315, 48717702, 29, 1) //CTRL pressed
wl_keyboard#27.modifiers(38317, 4, 0, 0, 1) //CTRL pressed
wl_keyboard#27.key(38318, 48719295, 42, 1) //SHIFT pressed
wl_keyboard#27.modifiers(38320, 5, 0, 0, 1) //SHIFT pressed
wl_keyboard#27.modifiers(38322, 1, 0, 0, 1) //CTRL released
wl_keyboard#27.modifiers(38323, 1, 0, 0, 0) //CTRL released
wl_keyboard#27.key(38324, 48724027, 42, 0) //SHIFT released
wl_keyboard#27.modifiers(38326, 0, 0, 0, 0) //SHIFT released
wl_keyboard#27.modifiers(38328, 4, 0, 0, 0) //CTRL pressed
wl_keyboard#27.key(38329, 48727759, 29, 0) //CTRL released
wl_keyboard#27.modifiers(38330, 0, 0, 0, 0) //CTRL released
```

EXPECTED RESULT
```
wl_keyboard#27.key(38315, 48717702, 29, 1) //CTRL pressed
wl_keyboard#27.modifiers(38317, 4, 0, 0, 1) //CTRL pressed
wl_keyboard#27.key(38318, 48719295, 42, 1) //SHIFT pressed
wl_keyboard#27.modifiers(38320, 5, 0, 0, 1) //SHIFT pressed
wl_keyboard#27.key(38329, 48727759, 29, 0) // Additional CTRL released "Key event"
wl_keyboard#27.modifiers(38322, 1, 0, 0, 1) //CTRL released
wl_keyboard#27.modifiers(38323, 1, 0, 0, 0) //CTRL released
wl_keyboard#27.key(38324, 48724027, 42, 0) //Just like SHIFT released "Key event" here
wl_keyboard#27.modifiers(38326, 0, 0, 0, 0) //SHIFT released
wl_keyboard#27.modifiers(38328, 4, 0, 0, 0) //CTRL pressed
wl_keyboard#27.key(38329, 48727759, 29, 0) //CTRL released
wl_keyboard#27.modifiers(38330, 0, 0, 0, 0) //CTRL released
```

SOFTWARE/OS VERSIONS
Operating System: Arch Linux 
KDE Plasma Version: 6.5.4
KDE Frameworks Version: 6.21.0
Qt Version: 6.10.1
Kernel Version: 6.18.2-arch2-1 (64-bit)
Graphics Platform: Wayland

ADDITIONAL IMFORMATION
This is a problem because after switching layouts, if you were to press the "stuck key" again, the client will only receive a modifier event. This breaks applications like video games that use modifier keys as an input source (see glfw issue: https://github.com/glfw/glfw/issues/1011 , ultimately breaking Minecraft)
Comment 1 bjoel2 2025-12-27 23:19:40 UTC
Uhm, actually, I've messed up the expected result section a little bit. This is the correct one:
```
wl_keyboard#27.key(38315, 48717702, 29, 1) //CTRL pressed
wl_keyboard#27.modifiers(38317, 4, 0, 0, 1) //CTRL pressed
wl_keyboard#27.key(38318, 48719295, 42, 1) //SHIFT pressed
wl_keyboard#27.modifiers(38320, 5, 0, 0, 1) //SHIFT pressed
wl_keyboard#27.key(38329, 48727759, 29, 0) // Additional CTRL released "Key event"
wl_keyboard#27.modifiers(38322, 1, 0, 0, 1) //CTRL released
wl_keyboard#27.modifiers(38323, 1, 0, 0, 0) //CTRL released
wl_keyboard#27.key(38324, 48724027, 42, 0) //Just like SHIFT released "Key event" here
wl_keyboard#27.modifiers(38326, 0, 0, 0, 0) //SHIFT released
wl_keyboard#27.key(38315, 48717702, 29, 1) //Additional CTRL pressed here
wl_keyboard#27.modifiers(38328, 4, 0, 0, 0) //CTRL pressed
wl_keyboard#27.key(38329, 48727759, 29, 0) //CTRL released
wl_keyboard#27.modifiers(38330, 0, 0, 0, 0) //CTRL released
```
Comment 2 bjoel2 2025-12-31 13:23:32 UTC
So I debugged this a little bit, and here's what I could find:
- When you press or release a button, eventually, KeyboardInputRedirection::processKey() (https://invent.kde.org/plasma/kwin/-/blob/master/src/keyboard_input.cpp#L279) is going to be called.
- It will then call m_input->processFilters() (https://invent.kde.org/plasma/kwin/-/blob/master/src/keyboard_input.cpp#L327), which iterates through every filter and tries to apply them. If a filter returns ``true``, the process of applying filters for the current event will stop. (https://invent.kde.org/plasma/kwin/-/blob/master/src/input.h#L153)
- If you only press CTRL, ``KWin::GlobalShortcutFilter`` will fail, and the event will be processed by ``KWin::Xwl::XwaylandInputFilter`` and/or (not sure yet) ``KWin::ForwardInputFilter`` keyboardKey() member functions (https://invent.kde.org/plasma/kwin/-/blob/master/src/xwayland/xwayland.cpp#L239), (https://invent.kde.org/plasma/kwin/-/blob/master/src/input.cpp#L2187), which will trigger both KeyboardInterface::sendKey() and KeyboardInterface::sendModifiers() as mentioned in the issue description (and this behavior is absolutely correct). sendKey() and sendModifiers() are defined at (https://invent.kde.org/plasma/kwin/-/blob/master/src/wayland/keyboard.cpp#L190) and (https://invent.kde.org/plasma/kwin/-/blob/master/src/wayland/keyboard.cpp#L227) respectively.
- As a bonus, later in the code, sendModifiers() will be called by ``KWin::Xkb::forwardModifiers()`` (https://invent.kde.org/plasma/kwin/-/blob/master/src/keyboard_input.cpp#L333)

However, this is what happens if you trigger a layout switch upon CTRL release:
- KeyboardInputRedirection::processKey() is called as usual
- m_input->processFilters() tries to apply filters as usual
- ``KWin::GlobalShortcutFilter`` will be applied successfully, returning ``true``, and thus every other filter will be skipped (including ``KWin::Xwl::XwaylandInputFilter`` and ``KWin::ForwardInputFilter``)
- Since all other filters were skipped, neither sendKey(), nor sendModifiers() will be called
- However, later in the code, ``KWin::Xkb::forwardModifiers()`` will call sendModifiers() (https://invent.kde.org/plasma/kwin/-/blob/master/src/keyboard_input.cpp#L333), as seen in the issue description (but where is the missing sendKey() call??)

So the rough explanation of a problem is as follows: because a GlobalShortcutFilter is being triggered, KeyboardInterface::sendKey() won't be called (but KeyboardInterface::sendModifiers() will be!).

Oh, also please keep in mind that I could have interpreted the code incorrectly (I'm a noob)