wayland: Optimize keymap creation

Iterate over the list of keys only once while assembling all related state for key levels and modifiers.
This commit is contained in:
Frank Praznik
2025-07-09 12:57:14 -04:00
parent 34d9db3657
commit 9b00f3a728
2 changed files with 62 additions and 80 deletions

View File

@@ -1410,24 +1410,40 @@ static const struct wl_touch_listener touch_listener = {
touch_handler_orientation // Version 6 touch_handler_orientation // Version 6
}; };
typedef struct Wayland_KeymapBuilderState
{
SDL_Keymap *keymap;
struct xkb_state *state;
SDL_Keymod modstate;
} Wayland_KeymapBuilderState;
static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)
{ {
Wayland_KeymapBuilderState *sdlKeymap = (Wayland_KeymapBuilderState *)data; SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
const xkb_keysym_t *syms; const xkb_keysym_t *syms;
const xkb_mod_mask_t xkb_valid_mod_mask = seat->keyboard.xkb.shift_mask |
seat->keyboard.xkb.alt_mask |
seat->keyboard.xkb.gui_mask |
seat->keyboard.xkb.level3_mask |
seat->keyboard.xkb.level5_mask |
seat->keyboard.xkb.caps_mask;
const SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8)); const SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8));
if (scancode == SDL_SCANCODE_UNKNOWN) { if (scancode == SDL_SCANCODE_UNKNOWN) {
return; return;
} }
if (WAYLAND_xkb_state_key_get_syms(sdlKeymap->state, key, &syms) > 0) { const xkb_level_index_t num_levels = WAYLAND_xkb_keymap_num_levels_for_key(seat->keyboard.xkb.keymap, key, seat->keyboard.xkb.current_layout);
SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(syms[0], key, sdlKeymap->modstate); for (xkb_level_index_t level = 0; level < num_levels; ++level) {
if (WAYLAND_xkb_keymap_key_get_syms_by_level(seat->keyboard.xkb.keymap, key, seat->keyboard.xkb.current_layout, level, &syms) > 0) {
xkb_mod_mask_t xkb_mod_masks[16];
const size_t num_masks = WAYLAND_xkb_keymap_key_get_mods_for_level(seat->keyboard.xkb.keymap, key, seat->keyboard.xkb.current_layout, level, xkb_mod_masks, SDL_arraysize(xkb_mod_masks));
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) {
continue;
}
const SDL_Keymod sdl_mod = (xkb_mod_masks[mask] & seat->keyboard.xkb.shift_mask ? SDL_KMOD_SHIFT : 0) |
(xkb_mod_masks[mask] & seat->keyboard.xkb.alt_mask ? SDL_KMOD_ALT : 0) |
(xkb_mod_masks[mask] & seat->keyboard.xkb.gui_mask ? SDL_KMOD_GUI : 0) |
(xkb_mod_masks[mask] & seat->keyboard.xkb.level3_mask ? SDL_KMOD_MODE : 0) |
(xkb_mod_masks[mask] & seat->keyboard.xkb.level5_mask ? SDL_KMOD_LEVEL5 : 0) |
(xkb_mod_masks[mask] & seat->keyboard.xkb.caps_mask ? SDL_KMOD_CAPS : 0);
SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(syms[0], key, sdl_mod);
if (!keycode) { if (!keycode) {
switch (scancode) { switch (scancode) {
@@ -1449,64 +1465,23 @@ static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, vo
} }
} }
SDL_SetKeymapEntry(sdlKeymap->keymap, scancode, sdlKeymap->modstate, keycode); SDL_SetKeymapEntry(seat->keyboard.sdl_keymap, scancode, sdl_mod, keycode);
}
}
} }
} }
static void Wayland_UpdateKeymap(SDL_WaylandSeat *seat) static void Wayland_UpdateKeymap(SDL_WaylandSeat *seat)
{ {
struct Keymod_masks
{
SDL_Keymod sdl_mask;
xkb_mod_mask_t xkb_mask;
} const keymod_masks[] = {
{ SDL_KMOD_NONE, 0 },
{ SDL_KMOD_SHIFT, seat->keyboard.xkb.shift_mask },
{ SDL_KMOD_CAPS, seat->keyboard.xkb.caps_mask },
{ SDL_KMOD_SHIFT | SDL_KMOD_CAPS, seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask },
{ SDL_KMOD_MODE, seat->keyboard.xkb.level3_mask },
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT, seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.shift_mask },
{ SDL_KMOD_MODE | SDL_KMOD_CAPS, seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.caps_mask },
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask },
{ SDL_KMOD_LEVEL5, seat->keyboard.xkb.level5_mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.shift_mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.caps_mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.level3_mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.shift_mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.caps_mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, seat->keyboard.xkb.level5_mask | seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.caps_mask },
};
if (!seat->keyboard.is_virtual) { if (!seat->keyboard.is_virtual) {
Wayland_KeymapBuilderState keymap;
keymap.keymap = SDL_CreateKeymap(false);
if (!keymap.keymap) {
return;
}
keymap.state = WAYLAND_xkb_state_new(seat->keyboard.xkb.keymap);
if (!keymap.state) {
SDL_SetError("failed to create XKB state");
SDL_DestroyKeymap(keymap.keymap);
return;
}
for (int i = 0; i < SDL_arraysize(keymod_masks); ++i) {
keymap.modstate = keymod_masks[i].sdl_mask;
WAYLAND_xkb_state_update_mask(keymap.state,
keymod_masks[i].xkb_mask & (seat->keyboard.xkb.shift_mask | seat->keyboard.xkb.level3_mask | seat->keyboard.xkb.level5_mask), 0, keymod_masks[i].xkb_mask & seat->keyboard.xkb.caps_mask,
0, 0, seat->keyboard.xkb.current_layout);
WAYLAND_xkb_keymap_key_for_each(seat->keyboard.xkb.keymap,
Wayland_keymap_iter,
&keymap);
}
WAYLAND_xkb_state_unref(keymap.state);
SDL_SetKeymap(keymap.keymap, true);
SDL_DestroyKeymap(seat->keyboard.sdl_keymap); SDL_DestroyKeymap(seat->keyboard.sdl_keymap);
seat->keyboard.sdl_keymap = keymap.keymap; seat->keyboard.sdl_keymap = SDL_CreateKeymap(false);
if (!seat->keyboard.sdl_keymap) {
return;
}
WAYLAND_xkb_keymap_key_for_each(seat->keyboard.xkb.keymap, Wayland_keymap_iter, seat);
SDL_SetKeymap(seat->keyboard.sdl_keymap, true);
} else { } else {
// Virtual keyboards use the default keymap. // Virtual keyboards use the default keymap.
SDL_SetKeymap(NULL, true); SDL_SetKeymap(NULL, true);

View File

@@ -153,8 +153,15 @@ SDL_WAYLAND_SYM(void, xkb_keymap_key_for_each, (struct xkb_keymap *, xkb_keymap_
SDL_WAYLAND_SYM(int, xkb_keymap_key_get_syms_by_level, (struct xkb_keymap *, SDL_WAYLAND_SYM(int, xkb_keymap_key_get_syms_by_level, (struct xkb_keymap *,
xkb_keycode_t, xkb_keycode_t,
xkb_layout_index_t, xkb_layout_index_t,
xkb_layout_index_t, xkb_level_index_t,
const xkb_keysym_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_keysym_to_utf32, (xkb_keysym_t) )
SDL_WAYLAND_SYM(uint32_t, xkb_keymap_mod_get_index, (struct xkb_keymap *, SDL_WAYLAND_SYM(uint32_t, xkb_keymap_mod_get_index, (struct xkb_keymap *,
const char *) ) const char *) )