win32: Implement keymap caching

Keymap construction is an expensive process, so keymaps are cached to facilitate fast switching, as they are static after initial construction, and do not need to be rebuilt every time.
This commit is contained in:
Frank Praznik
2025-07-22 12:32:46 -04:00
parent 34616d1b00
commit acb3b0b4be

View File

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