Skip to content

Commit acb3b0b

Browse files
committed
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.
1 parent 34616d1 commit acb3b0b

File tree

1 file changed

+91
-20
lines changed

1 file changed

+91
-20
lines changed

src/video/windows/SDL_windowskeyboard.c

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,36 +54,59 @@ static void IME_SetTextInputArea(SDL_VideoData *videodata, HWND hwnd, const SDL_
5454
#define MAPVK_VSC_TO_VK 1
5555
#endif
5656

57-
// Alphabetic scancodes for PC keyboards
58-
void WIN_InitKeyboard(SDL_VideoDevice *_this)
57+
/* Building keymaps is expensive, so keep a reasonably-sized LRU cache to
58+
* enable fast switching between commonly used ones.
59+
*/
60+
static struct WIN_KeymapCache
5961
{
60-
#ifndef SDL_DISABLE_WINDOWS_IME
61-
SDL_VideoData *data = _this->internal;
62+
HKL keyboard_layout;
63+
SDL_Keymap *keymap;
64+
} keymap_cache[4];
6265

63-
data->ime_candlistindexbase = 1;
64-
data->ime_composition_length = 32 * sizeof(WCHAR);
65-
data->ime_composition = (WCHAR *)SDL_calloc(data->ime_composition_length, sizeof(WCHAR));
66-
#endif // !SDL_DISABLE_WINDOWS_IME
66+
static int keymap_cache_size;
6767

68-
WIN_UpdateKeymap(false);
68+
static SDL_Keymap *WIN_GetCachedKeymap(HKL layout)
69+
{
70+
SDL_Keymap *keymap = NULL;
71+
for (int i = 0; i < keymap_cache_size; ++i) {
72+
if (keymap_cache[i].keyboard_layout == layout) {
73+
keymap = keymap_cache[i].keymap;
74+
75+
// Move the map to the front of the list.
76+
if (i) {
77+
SDL_memmove(keymap_cache + 1, keymap_cache, sizeof(struct WIN_KeymapCache) * i);
78+
keymap_cache[0].keyboard_layout = layout;
79+
keymap_cache[0].keymap = keymap;
80+
}
81+
break;
82+
}
83+
}
84+
return keymap;
85+
}
6986

70-
SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
71-
SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows");
72-
SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows");
87+
static void WIN_CacheKeymap(HKL layout, SDL_Keymap *keymap)
88+
{
89+
// If the cache is full, evict the last keymap.
90+
if (keymap_cache_size == SDL_arraysize(keymap_cache)) {
91+
SDL_DestroyKeymap(keymap_cache[--keymap_cache_size].keymap);
92+
}
7393

74-
// Are system caps/num/scroll lock active? Set our state to match.
75-
SDL_ToggleModState(SDL_KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) ? true : false);
76-
SDL_ToggleModState(SDL_KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) ? true : false);
77-
SDL_ToggleModState(SDL_KMOD_SCROLL, (GetKeyState(VK_SCROLL) & 0x0001) ? true : false);
94+
// Move all elements down by one.
95+
if (keymap_cache_size) {
96+
SDL_memmove(keymap_cache + 1, keymap_cache, sizeof(struct WIN_KeymapCache) * keymap_cache_size);
97+
}
98+
99+
keymap_cache[0].keyboard_layout = layout;
100+
keymap_cache[0].keymap = keymap;
101+
++keymap_cache_size;
78102
}
79103

80-
void WIN_UpdateKeymap(bool send_event)
104+
static SDL_Keymap *WIN_BuildKeymap()
81105
{
82106
SDL_Scancode scancode;
83-
SDL_Keymap *keymap;
84107
BYTE keyboardState[256] = { 0 };
85108
WCHAR buffer[16];
86-
SDL_Keymod mods[] = {
109+
const SDL_Keymod mods[] = {
87110
SDL_KMOD_NONE,
88111
SDL_KMOD_SHIFT,
89112
SDL_KMOD_CAPS,
@@ -96,7 +119,10 @@ void WIN_UpdateKeymap(bool send_event)
96119

97120
WIN_ResetDeadKeys();
98121

99-
keymap = SDL_CreateKeymap(true);
122+
SDL_Keymap *keymap = SDL_CreateKeymap(false);
123+
if (!keymap) {
124+
return NULL;
125+
}
100126

101127
for (int m = 0; m < SDL_arraysize(mods); ++m) {
102128
for (int i = 0; i < SDL_arraysize(windows_scancode_table); i++) {
@@ -160,9 +186,47 @@ void WIN_UpdateKeymap(bool send_event)
160186
}
161187
}
162188

189+
return keymap;
190+
}
191+
192+
void WIN_UpdateKeymap(bool send_event)
193+
{
194+
HKL layout = GetKeyboardLayout(0);
195+
SDL_Keymap *keymap = WIN_GetCachedKeymap(layout);
196+
if (!keymap) {
197+
keymap = WIN_BuildKeymap();
198+
if (keymap) {
199+
WIN_CacheKeymap(layout, keymap);
200+
}
201+
}
202+
163203
SDL_SetKeymap(keymap, send_event);
164204
}
165205

206+
// Alphabetic scancodes for PC keyboards
207+
void WIN_InitKeyboard(SDL_VideoDevice *_this)
208+
{
209+
#ifndef SDL_DISABLE_WINDOWS_IME
210+
SDL_VideoData *data = _this->internal;
211+
212+
data->ime_candlistindexbase = 1;
213+
data->ime_composition_length = 32 * sizeof(WCHAR);
214+
data->ime_composition = (WCHAR *)SDL_calloc(data->ime_composition_length, sizeof(WCHAR));
215+
#endif // !SDL_DISABLE_WINDOWS_IME
216+
217+
// Build and bind the current keymap.
218+
WIN_UpdateKeymap(false);
219+
220+
SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
221+
SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows");
222+
SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows");
223+
224+
// Are system caps/num/scroll lock active? Set our state to match.
225+
SDL_ToggleModState(SDL_KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) ? true : false);
226+
SDL_ToggleModState(SDL_KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) ? true : false);
227+
SDL_ToggleModState(SDL_KMOD_SCROLL, (GetKeyState(VK_SCROLL) & 0x0001) ? true : false);
228+
}
229+
166230
void WIN_QuitKeyboard(SDL_VideoDevice *_this)
167231
{
168232
#ifndef SDL_DISABLE_WINDOWS_IME
@@ -175,6 +239,13 @@ void WIN_QuitKeyboard(SDL_VideoDevice *_this)
175239
data->ime_composition = NULL;
176240
}
177241
#endif // !SDL_DISABLE_WINDOWS_IME
242+
243+
SDL_SetKeymap(NULL, false);
244+
for (int i = 0; i < keymap_cache_size; ++i) {
245+
SDL_DestroyKeymap(keymap_cache[i].keymap);
246+
}
247+
SDL_memset(keymap_cache, 0, sizeof(keymap_cache));
248+
keymap_cache_size = 0;
178249
}
179250

180251
void WIN_ResetDeadKeys(void)

0 commit comments

Comments
 (0)