diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 72cabeafc35fe..f0a87b3a3df27 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -1414,6 +1414,58 @@ static const struct wl_touch_listener touch_listener = { touch_handler_orientation // Version 6 }; +// Fallback for xkb_keymap_key_get_mods_for_level(), which is only available from 1.0.0, while the SDL minimum os 0.5.0. +#if !SDL_XKBCOMMON_CHECK_VERSION(1, 0, 0) +static size_t xkb_legacy_get_mods_for_level(SDL_WaylandSeat *seat, xkb_keycode_t key, xkb_layout_index_t layout, xkb_level_index_t level, xkb_mod_mask_t *masks_out, size_t masks_size) +{ + if (!masks_out || !masks_size) { + return 0; + } + + // Level 0 is always unmodified, so early out. + if (level == 0) { + *masks_out = 0; + return 1; + } + + struct xkb_state *state = WAYLAND_xkb_state_new(seat->keyboard.xkb.keymap); + if (state) { + const xkb_mod_mask_t keymod_masks[] = { + 0, + seat->keyboard.xkb.shift_mask, + seat->keyboard.xkb.caps_mask, + seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask, + seat->keyboard.xkb.level3_mask, + seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.shift_mask, + seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.caps_mask, + seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask, + seat->keyboard.xkb.level5_mask, + seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.shift_mask, + seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.caps_mask, + seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask + }; + const xkb_mod_mask_t pressed_mod_mask = seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.level5_mask; + const xkb_mod_mask_t locked_mod_mask = seat->keyboard.xkb.caps_mask; + + size_t mask_idx = 0; + + for (size_t i = 0; i < SDL_arraysize(keymod_masks); ++i) { + WAYLAND_xkb_state_update_mask(state, keymod_masks[i] & pressed_mod_mask, 0, keymod_masks[i] & locked_mod_mask, 0, 0, layout); + if (WAYLAND_xkb_state_key_get_level(state, key, layout) == level) { + masks_out[mask_idx] = keymod_masks[i]; + + if (++mask_idx == masks_size) { + break; + } + } + } + + WAYLAND_xkb_state_unref(state); + } + return mask_idx; +} +#endif + static void Wayland_KeymapIterator(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) { SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; @@ -1450,7 +1502,11 @@ static void Wayland_KeymapIterator(struct xkb_keymap *keymap, xkb_keycode_t key, } xkb_mod_mask_t xkb_mod_masks[16]; +#if SDL_XKBCOMMON_CHECK_VERSION(1, 0, 0) const size_t num_masks = WAYLAND_xkb_keymap_key_get_mods_for_level(seat->keyboard.xkb.keymap, key, layout, level, xkb_mod_masks, SDL_arraysize(xkb_mod_masks)); +#else + const size_t num_masks = xkb_legacy_get_mods_for_level(seat, key, layout, level, xkb_mod_masks, SDL_arraysize(xkb_mod_masks)); +#endif for (size_t mask = 0; mask < num_masks; ++mask) { // Ignore this modifier set if it uses unsupported modifier types. if ((xkb_mod_masks[mask] | xkb_valid_mod_mask) != xkb_valid_mod_mask) { diff --git a/src/video/wayland/SDL_waylandsym.h b/src/video/wayland/SDL_waylandsym.h index 31f77fbf9efe4..8d45d59ffdc67 100644 --- a/src/video/wayland/SDL_waylandsym.h +++ b/src/video/wayland/SDL_waylandsym.h @@ -151,22 +151,19 @@ SDL_WAYLAND_SYM(enum xkb_compose_status, xkb_compose_state_get_status, (struct x SDL_WAYLAND_SYM(xkb_keysym_t, xkb_compose_state_get_one_sym, (struct xkb_compose_state *) ) SDL_WAYLAND_SYM(void, xkb_keymap_key_for_each, (struct xkb_keymap *, xkb_keymap_key_iter_t, void *) ) SDL_WAYLAND_SYM(xkb_layout_index_t, xkb_keymap_num_layouts, (struct xkb_keymap *) ) -SDL_WAYLAND_SYM(int, xkb_keymap_key_get_syms_by_level, (struct xkb_keymap *, - xkb_keycode_t, - xkb_layout_index_t, - xkb_level_index_t, - const xkb_keysym_t **) ) +SDL_WAYLAND_SYM(int, xkb_keymap_key_get_syms_by_level, (struct xkb_keymap *, xkb_keycode_t, xkb_layout_index_t, xkb_level_index_t, const xkb_keysym_t **) ) SDL_WAYLAND_SYM(xkb_level_index_t, xkb_keymap_num_levels_for_key, (struct xkb_keymap *, xkb_keycode_t, xkb_layout_index_t) ) -SDL_WAYLAND_SYM(size_t, xkb_keymap_key_get_mods_for_level, (struct xkb_keymap *, - xkb_keycode_t, - xkb_layout_index_t, - xkb_level_index_t, - xkb_mod_mask_t *, - size_t masks_size) ) SDL_WAYLAND_SYM(uint32_t, xkb_keysym_to_utf32, (xkb_keysym_t) ) -SDL_WAYLAND_SYM(uint32_t, xkb_keymap_mod_get_index, (struct xkb_keymap *, - const char *) ) +SDL_WAYLAND_SYM(uint32_t, xkb_keymap_mod_get_index, (struct xkb_keymap *, const char *) ) SDL_WAYLAND_SYM(const char *, xkb_keymap_layout_get_name, (struct xkb_keymap *, xkb_layout_index_t)) + +#if SDL_XKBCOMMON_CHECK_VERSION(1, 0, 0) +SDL_WAYLAND_SYM(size_t, xkb_keymap_key_get_mods_for_level, (struct xkb_keymap *, xkb_keycode_t, xkb_layout_index_t, xkb_level_index_t, xkb_mod_mask_t *, size_t) ) +#else +// Only needed in the fallback replacement for xkb_keymap_key_get_mods_for_level(). +SDL_WAYLAND_SYM(xkb_level_index_t, xkb_state_key_get_level, (struct xkb_state *, xkb_keycode_t, xkb_layout_index_t) ) +#endif + #if SDL_XKBCOMMON_CHECK_VERSION(1, 10, 0) SDL_WAYLAND_SYM(xkb_mod_mask_t, xkb_keymap_mod_get_mask, (struct xkb_keymap *, const char *)) #endif