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.
This commit is contained in:
Frank Praznik
2025-10-03 13:47:52 -04:00
parent 1c1f7082ab
commit 2c02e6f8bb
2 changed files with 30 additions and 20 deletions

View File

@@ -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) {

View File

@@ -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;