diff --git a/src/video/windows/SDL_windowskeyboard.c b/src/video/windows/SDL_windowskeyboard.c index 560437598f..c8342c3cef 100644 --- a/src/video/windows/SDL_windowskeyboard.c +++ b/src/video/windows/SDL_windowskeyboard.c @@ -54,36 +54,59 @@ static void IME_SetTextInputArea(SDL_VideoData *videodata, HWND hwnd, const SDL_ #define MAPVK_VSC_TO_VK 1 #endif -// Alphabetic scancodes for PC keyboards -void WIN_InitKeyboard(SDL_VideoDevice *_this) +/* Building keymaps is expensive, so keep a reasonably-sized LRU cache to + * enable fast switching between commonly used ones. + */ +static struct WIN_KeymapCache { -#ifndef SDL_DISABLE_WINDOWS_IME - SDL_VideoData *data = _this->internal; + HKL keyboard_layout; + SDL_Keymap *keymap; +} keymap_cache[4]; - data->ime_candlistindexbase = 1; - data->ime_composition_length = 32 * sizeof(WCHAR); - data->ime_composition = (WCHAR *)SDL_calloc(data->ime_composition_length, sizeof(WCHAR)); -#endif // !SDL_DISABLE_WINDOWS_IME +static int keymap_cache_size; - WIN_UpdateKeymap(false); +static SDL_Keymap *WIN_GetCachedKeymap(HKL layout) +{ + SDL_Keymap *keymap = NULL; + for (int i = 0; i < keymap_cache_size; ++i) { + if (keymap_cache[i].keyboard_layout == layout) { + keymap = keymap_cache[i].keymap; - SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu"); - SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows"); - SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows"); - - // Are system caps/num/scroll lock active? Set our state to match. - SDL_ToggleModState(SDL_KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) ? true : false); - SDL_ToggleModState(SDL_KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) ? true : false); - SDL_ToggleModState(SDL_KMOD_SCROLL, (GetKeyState(VK_SCROLL) & 0x0001) ? true : false); + // Move the map to the front of the list. + if (i) { + SDL_memmove(keymap_cache + 1, keymap_cache, sizeof(struct WIN_KeymapCache) * i); + keymap_cache[0].keyboard_layout = layout; + keymap_cache[0].keymap = keymap; + } + break; + } + } + return keymap; } -void WIN_UpdateKeymap(bool send_event) +static void WIN_CacheKeymap(HKL layout, SDL_Keymap *keymap) +{ + // If the cache is full, evict the last keymap. + if (keymap_cache_size == SDL_arraysize(keymap_cache)) { + SDL_DestroyKeymap(keymap_cache[--keymap_cache_size].keymap); + } + + // Move all elements down by one. + if (keymap_cache_size) { + SDL_memmove(keymap_cache + 1, keymap_cache, sizeof(struct WIN_KeymapCache) * keymap_cache_size); + } + + keymap_cache[0].keyboard_layout = layout; + keymap_cache[0].keymap = keymap; + ++keymap_cache_size; +} + +static SDL_Keymap *WIN_BuildKeymap() { SDL_Scancode scancode; - SDL_Keymap *keymap; BYTE keyboardState[256] = { 0 }; WCHAR buffer[16]; - SDL_Keymod mods[] = { + const SDL_Keymod mods[] = { SDL_KMOD_NONE, SDL_KMOD_SHIFT, SDL_KMOD_CAPS, @@ -96,7 +119,10 @@ void WIN_UpdateKeymap(bool send_event) WIN_ResetDeadKeys(); - keymap = SDL_CreateKeymap(true); + SDL_Keymap *keymap = SDL_CreateKeymap(false); + if (!keymap) { + return NULL; + } for (int m = 0; m < SDL_arraysize(mods); ++m) { for (int i = 0; i < SDL_arraysize(windows_scancode_table); i++) { @@ -160,9 +186,47 @@ void WIN_UpdateKeymap(bool send_event) } } + return keymap; +} + +void WIN_UpdateKeymap(bool send_event) +{ + HKL layout = GetKeyboardLayout(0); + SDL_Keymap *keymap = WIN_GetCachedKeymap(layout); + if (!keymap) { + keymap = WIN_BuildKeymap(); + if (keymap) { + WIN_CacheKeymap(layout, keymap); + } + } + SDL_SetKeymap(keymap, send_event); } +// Alphabetic scancodes for PC keyboards +void WIN_InitKeyboard(SDL_VideoDevice *_this) +{ +#ifndef SDL_DISABLE_WINDOWS_IME + SDL_VideoData *data = _this->internal; + + data->ime_candlistindexbase = 1; + data->ime_composition_length = 32 * sizeof(WCHAR); + data->ime_composition = (WCHAR *)SDL_calloc(data->ime_composition_length, sizeof(WCHAR)); +#endif // !SDL_DISABLE_WINDOWS_IME + + // Build and bind the current keymap. + WIN_UpdateKeymap(false); + + SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu"); + SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows"); + SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows"); + + // Are system caps/num/scroll lock active? Set our state to match. + SDL_ToggleModState(SDL_KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) ? true : false); + SDL_ToggleModState(SDL_KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) ? true : false); + SDL_ToggleModState(SDL_KMOD_SCROLL, (GetKeyState(VK_SCROLL) & 0x0001) ? true : false); +} + void WIN_QuitKeyboard(SDL_VideoDevice *_this) { #ifndef SDL_DISABLE_WINDOWS_IME @@ -175,6 +239,13 @@ void WIN_QuitKeyboard(SDL_VideoDevice *_this) data->ime_composition = NULL; } #endif // !SDL_DISABLE_WINDOWS_IME + + SDL_SetKeymap(NULL, false); + for (int i = 0; i < keymap_cache_size; ++i) { + SDL_DestroyKeymap(keymap_cache[i].keymap); + } + SDL_memset(keymap_cache, 0, sizeof(keymap_cache)); + keymap_cache_size = 0; } void WIN_ResetDeadKeys(void)