Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix(seat): report held keys to a new focus in evdev codes, not xkb
`wl_keyboard.enter`'s key array is the same space as `wl_keyboard.key` --
the client is what adds 8 to reach an xkb keycode -- but
`keyboard_notify_enter` added it first, shifting every held key up by 8.
That array only goes out when an X11 override-redirect window takes focus
(`XwaylandOverrideRedirect::focus_if_desired`), which is exactly when a
popup menu opens under the key that opened it. Holding Tab (evdev 15) to
open Houdini's TAB menu handed Xwayland keycode 23, which it turned into
X keycode 31 -- `i` -- and typed that into the menu's search field. No
release followed, because the real Tab release frees keycode 23, so X
went on autorepeating it.
Reproduced with an X11 client that maps an override-redirect popup on its
first key press while a virtual keyboard holds Tab: the enter carried
[23] and XQueryKeymap reported `i` down; it now carries [15] and reports
Tab.
Co-Authored-By: Claude Opus 5 <[email protected]>
src/server/seat.rs | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/server/seat.rs b/src/server/seat.rs
index ad7aeea..d84dd4e 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -698,13 +698,21 @@ impl Seat {
if !kbd.is_null() {
let group_ptr = ffi::river_wlr_keyboard_get_data(kbd) as *mut crate::keyboard_group::KeyboardGroup;
if !group_ptr.is_null() {
+ // Raw evdev keycodes, NOT xkb ones: `wl_keyboard.enter`'s key
+ // array is the same space as `wl_keyboard.key`, and the client
+ // is the one that adds 8 to reach an xkb keycode. Adding it
+ // here shifted every held key up by 8 on the way out — and an
+ // X11 popup that opens under a held key is exactly when this
+ // array is sent, so opening Houdini's TAB menu (evdev 15)
+ // handed Xwayland keycode 31 and typed an `i` into it, with no
+ // release to follow, so X autorepeated it.
let mut buffer = [0u32; 32];
let mut count = 0;
for &keycode in (*group_ptr).pressed.keys() {
if count >= 32 {
break;
}
- buffer[count] = keycode + 8;
+ buffer[count] = keycode;
count += 1;
}
let modifiers = ffi::river_wlr_keyboard_get_modifiers(kbd);