diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c index e281f5daaa..6023c88608 100644 --- a/src/events/SDL_keyboard.c +++ b/src/events/SDL_keyboard.c @@ -61,7 +61,6 @@ typedef struct SDL_Keyboard Uint32 keycode_options; bool autorelease_pending; Uint64 hardware_timestamp; - int next_reserved_scancode; } SDL_Keyboard; static SDL_Keyboard SDL_keyboard; @@ -295,16 +294,12 @@ void SDL_SetKeymap(SDL_Keymap *keymap, bool send_event) static SDL_Scancode GetNextReservedScancode(void) { SDL_Keyboard *keyboard = &SDL_keyboard; - SDL_Scancode scancode; - if (keyboard->next_reserved_scancode && keyboard->next_reserved_scancode < SDL_SCANCODE_RESERVED + 100) { - scancode = (SDL_Scancode)keyboard->next_reserved_scancode; - } else { - scancode = SDL_SCANCODE_RESERVED; + if (!keyboard->keymap) { + keyboard->keymap = SDL_CreateKeymap(true); } - keyboard->next_reserved_scancode = (int)scancode + 1; - return scancode; + return SDL_GetKeymapNextReservedScancode(keyboard->keymap); } static void SetKeymapEntry(SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keycode keycode) diff --git a/src/events/SDL_keymap.c b/src/events/SDL_keymap.c index 318fc6864b..21d287ccaa 100644 --- a/src/events/SDL_keymap.c +++ b/src/events/SDL_keymap.c @@ -183,6 +183,24 @@ SDL_Scancode SDL_GetKeymapScancode(SDL_Keymap *keymap, SDL_Keycode keycode, SDL_ return scancode; } +SDL_Scancode SDL_GetKeymapNextReservedScancode(SDL_Keymap *keymap) +{ + SDL_Scancode scancode; + + if (!keymap) { + return SDL_SCANCODE_UNKNOWN; + } + + if (keymap->next_reserved_scancode && keymap->next_reserved_scancode < SDL_SCANCODE_RESERVED + 100) { + scancode = keymap->next_reserved_scancode; + } else { + scancode = SDL_SCANCODE_RESERVED; + } + keymap->next_reserved_scancode = scancode + 1; + + return scancode; +} + void SDL_DestroyKeymap(SDL_Keymap *keymap) { if (!keymap) { diff --git a/src/events/SDL_keymap_c.h b/src/events/SDL_keymap_c.h index 311e397e67..8819924ab5 100644 --- a/src/events/SDL_keymap_c.h +++ b/src/events/SDL_keymap_c.h @@ -25,13 +25,14 @@ typedef struct SDL_Keymap { - SDL_HashTable *scancode_to_keycode; - SDL_HashTable *keycode_to_scancode; - bool auto_release; - bool layout_determined; - bool french_numbers; - bool latin_letters; - bool thai_keyboard; + SDL_HashTable *scancode_to_keycode; + SDL_HashTable *keycode_to_scancode; + SDL_Scancode next_reserved_scancode; + bool auto_release; + bool layout_determined; + bool french_numbers; + bool latin_letters; + bool thai_keyboard; } SDL_Keymap; /* This may return null even when a keymap is bound, depending on the current keyboard mapping options. @@ -42,6 +43,7 @@ SDL_Keymap *SDL_CreateKeymap(bool auto_release); void SDL_SetKeymapEntry(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keycode keycode); SDL_Keycode SDL_GetKeymapKeycode(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate); SDL_Scancode SDL_GetKeymapScancode(SDL_Keymap *keymap, SDL_Keycode keycode, SDL_Keymod *modstate); +SDL_Scancode SDL_GetKeymapNextReservedScancode(SDL_Keymap *keymap); void SDL_DestroyKeymap(SDL_Keymap *keymap); #endif // SDL_keymap_c_h_ diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 08288718dd..78162a3eae 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -304,6 +304,16 @@ void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display) } } +static void Wayland_SeatSetKeymap(SDL_WaylandSeat *seat) +{ + if (seat->keyboard.sdl_keymap && + seat->keyboard.xkb.current_layout < seat->keyboard.xkb.num_layouts && + seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout] != SDL_GetCurrentKeymap(true)) { + SDL_SetKeymap(seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout], true); + SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); + } +} + // Returns true if a key repeat event was due static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed) { @@ -440,10 +450,7 @@ int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) // If key repeat is active, we'll need to cap our maximum wait time to handle repeats wl_list_for_each (seat, &d->seat_list, link) { if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { - if (seat->keyboard.sdl_keymap != SDL_GetCurrentKeymap(true)) { - SDL_SetKeymap(seat->keyboard.sdl_keymap, true); - SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); - } + Wayland_SeatSetKeymap(seat); const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns; if (keyboard_repeat_handle(&seat->keyboard.repeat, elapsed)) { @@ -479,14 +486,13 @@ int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) // If key repeat is active, we might have woken up to generate a key event if (key_repeat_active) { wl_list_for_each (seat, &d->seat_list, link) { - if (seat->keyboard.sdl_keymap != SDL_GetCurrentKeymap(true)) { - SDL_SetKeymap(seat->keyboard.sdl_keymap, true); - SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); - } + if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { + Wayland_SeatSetKeymap(seat); - const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns; - if (keyboard_repeat_handle(&seat->keyboard.repeat, elapsed)) { - ++ret; + const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns; + if (keyboard_repeat_handle(&seat->keyboard.repeat, elapsed)) { + ++ret; + } } } } @@ -550,10 +556,7 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this) wl_list_for_each (seat, &d->seat_list, link) { if (keyboard_repeat_is_set(&seat->keyboard.repeat)) { - if (seat->keyboard.sdl_keymap != SDL_GetCurrentKeymap(true)) { - SDL_SetKeymap(seat->keyboard.sdl_keymap, true); - SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); - } + Wayland_SeatSetKeymap(seat); const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns; keyboard_repeat_handle(&seat->keyboard.repeat, elapsed); @@ -1410,7 +1413,7 @@ static const struct wl_touch_listener touch_listener = { touch_handler_orientation // Version 6 }; -static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) +static void Wayland_KeymapIterator(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) { SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; const xkb_keysym_t *syms; @@ -1420,76 +1423,75 @@ static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, vo 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)); - if (scancode == SDL_SCANCODE_UNKNOWN) { - return; + SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; + + // Look up the scancode for hardware keyboards. Virtual keyboards get the scancode from the keysym. + if (!seat->keyboard.is_virtual) { + scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8)); + if (scancode == SDL_SCANCODE_UNKNOWN) { + return; + } } - const xkb_level_index_t num_levels = WAYLAND_xkb_keymap_num_levels_for_key(seat->keyboard.xkb.keymap, key, seat->keyboard.xkb.current_layout); - 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) { - switch (scancode) { - case SDL_SCANCODE_RETURN: - keycode = SDLK_RETURN; - break; - case SDL_SCANCODE_ESCAPE: - keycode = SDLK_ESCAPE; - break; - case SDL_SCANCODE_BACKSPACE: - keycode = SDLK_BACKSPACE; - break; - case SDL_SCANCODE_DELETE: - keycode = SDLK_DELETE; - break; - default: - keycode = SDL_SCANCODE_TO_KEYCODE(scancode); - break; + for (xkb_layout_index_t layout = 0; layout < seat->keyboard.xkb.num_layouts; ++layout) { + const xkb_level_index_t num_levels = WAYLAND_xkb_keymap_num_levels_for_key(seat->keyboard.xkb.keymap, key, seat->keyboard.xkb.current_layout); + 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, layout, level, &syms) > 0) { + /* If the keyboard is virtual or the key didn't have a corresponding hardware scancode, try to + * look it up from the keysym. If there is still no corresponding scancode, skip this mapping + * for now, as it will be dynamically added with a reserved scancode on first use. + */ + if (scancode == SDL_SCANCODE_UNKNOWN) { + scancode = SDL_GetScancodeFromKeySym(syms[0], key); + if (scancode == SDL_SCANCODE_UNKNOWN) { + continue; } } - SDL_SetKeymapEntry(seat->keyboard.sdl_keymap, scancode, sdl_mod, keycode); + 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, 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) { + switch (scancode) { + case SDL_SCANCODE_RETURN: + keycode = SDLK_RETURN; + break; + case SDL_SCANCODE_ESCAPE: + keycode = SDLK_ESCAPE; + break; + case SDL_SCANCODE_BACKSPACE: + keycode = SDLK_BACKSPACE; + break; + case SDL_SCANCODE_DELETE: + keycode = SDLK_DELETE; + break; + default: + keycode = SDL_SCANCODE_TO_KEYCODE(scancode); + break; + } + } + + SDL_SetKeymapEntry(seat->keyboard.sdl_keymap[layout], scancode, sdl_mod, keycode); + } } } } } -static void Wayland_UpdateKeymap(SDL_WaylandSeat *seat) -{ - if (!seat->keyboard.is_virtual) { - SDL_DestroyKeymap(seat->keyboard.sdl_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 { - // Virtual keyboards use the default keymap. - SDL_SetKeymap(NULL, true); - SDL_DestroyKeymap(seat->keyboard.sdl_keymap); - seat->keyboard.sdl_keymap = NULL; - } -} - static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { @@ -1520,9 +1522,9 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, seat->keyboard.xkb.keymap = NULL; } seat->keyboard.xkb.keymap = WAYLAND_xkb_keymap_new_from_string(seat->display->xkb_context, - map_str, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); + map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); munmap(map_str, size); close(fd); @@ -1531,6 +1533,14 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, return; } + // Clear the old layouts. + for (xkb_layout_index_t i = 0; i < seat->keyboard.xkb.num_layouts; ++i) { + SDL_DestroyKeymap(seat->keyboard.sdl_keymap[i]); + } + SDL_free(seat->keyboard.sdl_keymap); + seat->keyboard.sdl_keymap = NULL; + seat->keyboard.xkb.num_layouts = 0; + #if SDL_XKBCOMMON_CHECK_VERSION(1, 10, 0) seat->keyboard.xkb.shift_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_MOD_NAME_SHIFT); seat->keyboard.xkb.ctrl_mask = WAYLAND_xkb_keymap_mod_get_mask(seat->keyboard.xkb.keymap, XKB_MOD_NAME_CTRL); @@ -1576,9 +1586,19 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, */ seat->keyboard.is_virtual = WAYLAND_xkb_keymap_layout_get_name(seat->keyboard.xkb.keymap, 0) == NULL; - // Update the keymap if changed. - if (seat->keyboard.xkb.current_layout != XKB_LAYOUT_INVALID) { - Wayland_UpdateKeymap(seat); + // Allocate and populate the new layout maps. + seat->keyboard.xkb.num_layouts = WAYLAND_xkb_keymap_num_layouts(seat->keyboard.xkb.keymap); + if (seat->keyboard.xkb.num_layouts) { + seat->keyboard.sdl_keymap = SDL_calloc(seat->keyboard.xkb.num_layouts, sizeof(SDL_Keymap *)); + for (xkb_layout_index_t i = 0; i < seat->keyboard.xkb.num_layouts; ++i) { + seat->keyboard.sdl_keymap[i] = SDL_CreateKeymap(false); + if (!seat->keyboard.sdl_keymap) { + return; + } + } + + WAYLAND_xkb_keymap_key_for_each(seat->keyboard.xkb.keymap, Wayland_KeymapIterator, seat); + Wayland_SeatSetKeymap(seat); } /* @@ -1637,16 +1657,20 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, * Virtual keyboards can have arbitrary layouts, arbitrary scancodes/keycodes, etc... * Key presses from these devices must be looked up by their keysym value. */ -static SDL_Scancode Wayland_GetScancodeForKey(SDL_WaylandSeat *seat, uint32_t key) +static SDL_Scancode Wayland_GetScancodeForKey(SDL_WaylandSeat *seat, uint32_t key, const xkb_keysym_t **syms) { SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; if (!seat->keyboard.is_virtual) { scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, key); - } else { - const xkb_keysym_t *syms; - if (WAYLAND_xkb_keymap_key_get_syms_by_level(seat->keyboard.xkb.keymap, key + 8, seat->keyboard.xkb.current_layout, 0, &syms) > 0) { - scancode = SDL_GetScancodeFromKeySym(syms[0], key); + } + if (scancode == SDL_SCANCODE_UNKNOWN) { + const xkb_keysym_t *keysym; + if (WAYLAND_xkb_state_key_get_syms(seat->keyboard.xkb.state, key + 8, &keysym) > 0) { + scancode = SDL_GetScancodeFromKeySym(keysym[0], key + 8); + if (syms) { + *syms = keysym; + } } } @@ -1884,30 +1908,30 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, Uint64 timestamp = SDL_GetTicksNS(); window->last_focus_event_time_ns = timestamp; - if (SDL_GetCurrentKeymap(true) != seat->keyboard.sdl_keymap) { - SDL_SetKeymap(seat->keyboard.sdl_keymap, true); - } + Wayland_SeatSetKeymap(seat); wl_array_for_each (key, keys) { - const SDL_Scancode scancode = Wayland_GetScancodeForKey(seat, *key); - const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); + const SDL_Scancode scancode = Wayland_GetScancodeForKey(seat, *key, NULL); + if (scancode != SDL_SCANCODE_UNKNOWN) { + const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); - switch (keycode) { - case SDLK_LSHIFT: - case SDLK_RSHIFT: - case SDLK_LCTRL: - case SDLK_RCTRL: - case SDLK_LALT: - case SDLK_RALT: - case SDLK_LGUI: - case SDLK_RGUI: - case SDLK_MODE: - case SDLK_LEVEL5_SHIFT: - Wayland_HandleModifierKeys(seat, scancode, true); - SDL_SendKeyboardKeyIgnoreModifiers(timestamp, seat->keyboard.sdl_id, *key, scancode, true); - break; - default: - break; + switch (keycode) { + case SDLK_LSHIFT: + case SDLK_RSHIFT: + case SDLK_LCTRL: + case SDLK_RCTRL: + case SDLK_LALT: + case SDLK_RALT: + case SDLK_LGUI: + case SDLK_RGUI: + case SDLK_MODE: + case SDLK_LEVEL5_SHIFT: + Wayland_HandleModifierKeys(seat, scancode, true); + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, seat->keyboard.sdl_id, *key, scancode, true); + break; + default: + break; + } } } } @@ -2044,10 +2068,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, state = WL_KEYBOARD_KEY_STATE_PRESSED; } - if (seat->keyboard.sdl_keymap != SDL_GetCurrentKeymap(true)) { - SDL_SetKeymap(seat->keyboard.sdl_keymap, true); - SDL_SetModState(seat->keyboard.pressed_modifiers | seat->keyboard.locked_modifiers); - } + Wayland_SeatSetKeymap(seat); if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); @@ -2068,9 +2089,27 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, keyboard_input_get_text(text, seat, key, false, &handled_by_ime); } - const SDL_Scancode scancode = Wayland_GetScancodeForKey(seat, key); + const xkb_keysym_t *syms = NULL; + SDL_Scancode scancode = Wayland_GetScancodeForKey(seat, key, &syms); Wayland_HandleModifierKeys(seat, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); + // If we have a key with unknown scancode, check if the keysym corresponds to a valid Unicode value, and assign it a reserved scancode. + if (scancode == SDL_SCANCODE_UNKNOWN && syms) { + const SDL_Keycode keycode = (SDL_Keycode)SDL_KeySymToUcs4(syms[0]); + if (keycode != SDLK_UNKNOWN) { + SDL_Keymod modstate = SDL_KMOD_NONE; + + // Check if this keycode already exists in the keymap. + scancode = SDL_GetKeymapScancode(seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout], keycode, &modstate); + + // Make sure we have this keycode in our keymap + if (scancode == SDL_SCANCODE_UNKNOWN && keycode < SDLK_SCANCODE_MASK) { + scancode = SDL_GetKeymapNextReservedScancode(seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout]); + SDL_SetKeymapEntry(seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout], scancode, modstate, keycode); + } + } + } + SDL_SendKeyboardKeyIgnoreModifiers(timestamp_ns, seat->keyboard.sdl_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { @@ -2116,13 +2155,15 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, } } - if (group == seat->keyboard.xkb.current_layout) { - return; - } + if (group != seat->keyboard.xkb.current_layout) { + seat->keyboard.xkb.current_layout = group; + Wayland_SeatSetKeymap(seat); - // The layout changed, remap and fire an event. Virtual keyboards use the default keymap. - seat->keyboard.xkb.current_layout = group; - Wayland_UpdateKeymap(seat); + if (seat->keyboard.xkb.compose_state) { + // Reset the compose state so composite and dead keys don't carry over. + WAYLAND_xkb_compose_state_reset(seat->keyboard.xkb.compose_state); + } + } } static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, @@ -2205,10 +2246,16 @@ static void Wayland_SeatDestroyKeyboard(SDL_WaylandSeat *seat, bool send_event) SDL_RemoveKeyboard(seat->keyboard.sdl_id, send_event); if (seat->keyboard.sdl_keymap) { - if (seat->keyboard.sdl_keymap == SDL_GetCurrentKeymap(true)) { + if (seat->keyboard.xkb.current_layout < seat->keyboard.xkb.num_layouts && + seat->keyboard.sdl_keymap[seat->keyboard.xkb.current_layout] == SDL_GetCurrentKeymap(true)) { SDL_SetKeymap(NULL, false); + SDL_SetModState(SDL_KMOD_NONE); } - SDL_DestroyKeymap(seat->keyboard.sdl_keymap); + for (xkb_layout_index_t i = 0; i < seat->keyboard.xkb.num_layouts; ++i) { + SDL_DestroyKeymap(seat->keyboard.sdl_keymap[i]); + } + SDL_free(seat->keyboard.sdl_keymap); + seat->keyboard.sdl_keymap = NULL; } if (seat->keyboard.key_inhibitor) { diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index ffa779e250..c43958b9a6 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -75,7 +75,7 @@ typedef struct SDL_WaylandSeat struct zwp_input_timestamps_v1 *timestamps; struct zwp_keyboard_shortcuts_inhibitor_v1 *key_inhibitor; SDL_WindowData *focus; - SDL_Keymap *sdl_keymap; + SDL_Keymap **sdl_keymap; char *current_locale; SDL_WaylandKeyboardRepeat repeat; @@ -96,6 +96,7 @@ typedef struct SDL_WaylandSeat struct xkb_compose_state *compose_state; // Current keyboard layout (aka 'group') + xkb_layout_index_t num_layouts; xkb_layout_index_t current_layout; // Modifier bitshift values diff --git a/src/video/wayland/SDL_waylandsym.h b/src/video/wayland/SDL_waylandsym.h index 5b0a0939c5..31f77fbf9e 100644 --- a/src/video/wayland/SDL_waylandsym.h +++ b/src/video/wayland/SDL_waylandsym.h @@ -150,6 +150,7 @@ SDL_WAYLAND_SYM(enum xkb_compose_feed_result, xkb_compose_state_feed, (struct xk SDL_WAYLAND_SYM(enum xkb_compose_status, xkb_compose_state_get_status, (struct xkb_compose_state *) ) 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,