From 2c02e6f8bbff72e15410329602d8fbe052279224 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 3 Oct 2025 13:47:52 -0400 Subject: [PATCH] wayland: Restore valid state information when building keymaps The spec doesn't guarantee that a modifier event won't arrive before a keymap event, or that it will always be sent after a keymap change if the modifiers and layout index haven't changed, so restore any valid state after allocation when building a new keymap. --- src/video/wayland/SDL_waylandevents.c | 49 +++++++++++++++---------- src/video/wayland/SDL_waylandevents_c.h | 1 + 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 8b4c2920bb..c52b8b5586 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -1663,6 +1663,11 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, } WAYLAND_xkb_keymap_key_for_each(seat->keyboard.xkb.keymap, Wayland_KeymapIterator, seat); + + // Restore any previously set modifier/layout information, if valid. + WAYLAND_xkb_state_update_mask(seat->keyboard.xkb.state, + seat->keyboard.xkb.wl_pressed_modifiers, seat->keyboard.xkb.wl_latched_modifiers, seat->keyboard.xkb.wl_locked_modifiers, + 0, 0, seat->keyboard.xkb.current_layout < seat->keyboard.xkb.num_layouts ? seat->keyboard.xkb.current_layout : 0); Wayland_SeatSetKeymap(seat); } @@ -1806,7 +1811,9 @@ static void Wayland_ReconcileModifiers(SDL_WaylandSeat *seat, bool key_pressed) * The modifier will remain active until the latch/lock is released by * the system. */ - if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.shift_mask) { + const xkb_mod_mask_t xkb_locked_modifiers = seat->keyboard.xkb.wl_latched_modifiers | seat->keyboard.xkb.wl_locked_modifiers; + + if (xkb_locked_modifiers & seat->keyboard.xkb.shift_mask) { if (seat->keyboard.pressed_modifiers & SDL_KMOD_SHIFT) { seat->keyboard.locked_modifiers &= ~SDL_KMOD_SHIFT; seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_SHIFT); @@ -1817,7 +1824,7 @@ static void Wayland_ReconcileModifiers(SDL_WaylandSeat *seat, bool key_pressed) seat->keyboard.locked_modifiers &= ~SDL_KMOD_SHIFT; } - if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.ctrl_mask) { + if (xkb_locked_modifiers & seat->keyboard.xkb.ctrl_mask) { if (seat->keyboard.pressed_modifiers & SDL_KMOD_CTRL) { seat->keyboard.locked_modifiers &= ~SDL_KMOD_CTRL; seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_CTRL); @@ -1828,7 +1835,7 @@ static void Wayland_ReconcileModifiers(SDL_WaylandSeat *seat, bool key_pressed) seat->keyboard.locked_modifiers &= ~SDL_KMOD_CTRL; } - if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.alt_mask) { + if (xkb_locked_modifiers & seat->keyboard.xkb.alt_mask) { if (seat->keyboard.pressed_modifiers & SDL_KMOD_ALT) { seat->keyboard.locked_modifiers &= ~SDL_KMOD_ALT; seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_ALT); @@ -1839,7 +1846,7 @@ static void Wayland_ReconcileModifiers(SDL_WaylandSeat *seat, bool key_pressed) seat->keyboard.locked_modifiers &= ~SDL_KMOD_ALT; } - if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.gui_mask) { + if (xkb_locked_modifiers & seat->keyboard.xkb.gui_mask) { if (seat->keyboard.pressed_modifiers & SDL_KMOD_GUI) { seat->keyboard.locked_modifiers &= ~SDL_KMOD_GUI; seat->keyboard.locked_modifiers |= (seat->keyboard.pressed_modifiers & SDL_KMOD_GUI); @@ -1850,26 +1857,26 @@ static void Wayland_ReconcileModifiers(SDL_WaylandSeat *seat, bool key_pressed) seat->keyboard.locked_modifiers &= ~SDL_KMOD_GUI; } - if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.level3_mask) { + if (xkb_locked_modifiers & seat->keyboard.xkb.level3_mask) { seat->keyboard.locked_modifiers |= SDL_KMOD_MODE; } else { seat->keyboard.locked_modifiers &= ~SDL_KMOD_MODE; } - if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.level5_mask) { + if (xkb_locked_modifiers & seat->keyboard.xkb.level5_mask) { seat->keyboard.locked_modifiers |= SDL_KMOD_LEVEL5; } else { seat->keyboard.locked_modifiers &= ~SDL_KMOD_LEVEL5; } // Capslock and Numlock can only be locked, not pressed. - if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.caps_mask) { + if (xkb_locked_modifiers & seat->keyboard.xkb.caps_mask) { seat->keyboard.locked_modifiers |= SDL_KMOD_CAPS; } else { seat->keyboard.locked_modifiers &= ~SDL_KMOD_CAPS; } - if (seat->keyboard.xkb.wl_locked_modifiers & seat->keyboard.xkb.num_mask) { + if (xkb_locked_modifiers & seat->keyboard.xkb.num_mask) { seat->keyboard.locked_modifiers |= SDL_KMOD_NUM; } else { seat->keyboard.locked_modifiers &= ~SDL_KMOD_NUM; @@ -2195,20 +2202,23 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t group) { SDL_WaylandSeat *seat = data; + const uint32_t previous_layout = seat->keyboard.xkb.current_layout; - if (seat->keyboard.xkb.state == NULL) { - /* if we get a modifier notification before the keymap, there's nothing we can do with the information - */ + seat->keyboard.xkb.wl_pressed_modifiers = mods_depressed; + seat->keyboard.xkb.wl_latched_modifiers = mods_latched; + seat->keyboard.xkb.wl_locked_modifiers = mods_locked; + seat->keyboard.xkb.current_layout = group; + + Wayland_ReconcileModifiers(seat, false); + + // If we get a modifier notification before the keymap, there's no further state to update yet. + if (!seat->keyboard.xkb.state) { return; } - WAYLAND_xkb_state_update_mask(seat->keyboard.xkb.state, mods_depressed, mods_latched, - mods_locked, 0, 0, group); - - seat->keyboard.xkb.wl_pressed_modifiers = mods_depressed; - seat->keyboard.xkb.wl_locked_modifiers = mods_latched | mods_locked; - - Wayland_ReconcileModifiers(seat, false); + WAYLAND_xkb_state_update_mask(seat->keyboard.xkb.state, + mods_depressed, mods_latched, mods_locked, + 0, 0, group); // If a key is repeating, update the text to apply the modifier. if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { @@ -2220,8 +2230,7 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, } } - if (group != seat->keyboard.xkb.current_layout) { - seat->keyboard.xkb.current_layout = group; + if (group != previous_layout) { Wayland_SeatSetKeymap(seat); if (seat->keyboard.xkb.compose_state) { diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index ef560b4e15..c56a3adf06 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -110,6 +110,7 @@ typedef struct SDL_WaylandSeat // Current system modifier flags xkb_mod_mask_t wl_pressed_modifiers; + xkb_mod_mask_t wl_latched_modifiers; xkb_mod_mask_t wl_locked_modifiers; } xkb; } keyboard;