Skip to content

Commit 7cc3fee

Browse files
committed
keyboard: Search for the correct base key value when querying the keycode from a scancode
When querying the keycode produced by a scancode with a certain set of modifiers, it would fall back to defaults if a key hash value with the exact set of modifiers wasn't found, which resulted in certain modifier combination returning incorrect keycodes on non-ANSI keyboard layouts. For example, querying SDL_SCANCODE_Y with the alt modifier on a QWERTZ layout returns SDLK_Y instead of SDLK_Z on most platforms, as the backends don't generate a specific entry for this key + modifier combo, so the lookup would fall back to the default ANSI layout. Adding additional key+modifier combinations when building the keymap is one solution, but it makes an already expensive operation even more so, pushing the time needed to build the keymap into double-digit milliseconds in some cases due to the large amount of key combos that need to be queried, most of which are redundant. Instead, falling back to searching through the shift levels for the given modifier state when querying the keymap will ensure that the most appropriate keycode is returned. This does add some overhead to lookups if the key doesn't have an entry with the exact set of modifiers, but it is minimal as hash table lookups are an inexpensive operation, and unnecessary lookups are avoided. In my own testing of an optimized build, the difference between best-case and worst-case performance (the latter of which is highly unlikely in real-world usage) is only a few hundred nanoseconds. Additionally, the unmodified keys are queried when pumping events, so there is no additional overhead in that case.
1 parent 22fa45b commit 7cc3fee

File tree

1 file changed

+66
-8
lines changed

1 file changed

+66
-8
lines changed

src/events/SDL_keymap.c

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,74 @@ void SDL_SetKeymapEntry(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod mo
9797

9898
SDL_Keycode SDL_GetKeymapKeycode(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate)
9999
{
100-
SDL_Keycode keycode;
100+
if (keymap) {
101+
const void *value;
102+
const SDL_Keymod normalized_modstate = NormalizeModifierStateForKeymap(modstate);
103+
Uint32 key = ((Uint32)normalized_modstate << 16) | scancode;
101104

102-
const Uint32 key = ((Uint32)NormalizeModifierStateForKeymap(modstate) << 16) | scancode;
103-
const void *value;
104-
if (keymap && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
105-
keycode = (SDL_Keycode)(uintptr_t)value;
106-
} else {
107-
keycode = SDL_GetDefaultKeyFromScancode(scancode, modstate);
105+
// First, try the requested set of modifiers.
106+
if (SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
107+
return (SDL_Keycode)(uintptr_t)value;
108+
}
109+
110+
// If the requested set of modifiers was not found, search for the key from the highest to lowest modifier levels.
111+
if (normalized_modstate) {
112+
SDL_Keymod caps_mask = normalized_modstate & SDL_KMOD_CAPS;
113+
114+
for (int i = caps_mask ? 2 : 1; i; --i) {
115+
// Shift level 5
116+
if (normalized_modstate & SDL_KMOD_LEVEL5) {
117+
const SDL_Keymod shifted_modstate = SDL_KMOD_LEVEL5 | caps_mask;
118+
key = ((Uint32)shifted_modstate << 16) | scancode;
119+
120+
if (shifted_modstate != normalized_modstate && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
121+
return (SDL_Keycode)(uintptr_t)value;
122+
}
123+
}
124+
125+
// Shift level 4 (Level 3 + Shift)
126+
if ((normalized_modstate & (SDL_KMOD_MODE | SDL_KMOD_SHIFT)) == (SDL_KMOD_MODE | SDL_KMOD_SHIFT)) {
127+
const SDL_Keymod shifted_modstate = SDL_KMOD_MODE | SDL_KMOD_SHIFT | caps_mask;
128+
key = ((Uint32)shifted_modstate << 16) | scancode;
129+
130+
if (shifted_modstate != normalized_modstate && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
131+
return (SDL_Keycode)(uintptr_t)value;
132+
}
133+
}
134+
135+
// Shift level 3
136+
if (normalized_modstate & SDL_KMOD_MODE) {
137+
const SDL_Keymod shifted_modstate = SDL_KMOD_MODE | caps_mask;
138+
key = ((Uint32)shifted_modstate << 16) | scancode;
139+
140+
if (shifted_modstate != normalized_modstate && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
141+
return (SDL_Keycode)(uintptr_t)value;
142+
}
143+
}
144+
145+
// Shift level 2
146+
if (normalized_modstate & SDL_KMOD_SHIFT) {
147+
const SDL_Keymod shifted_modstate = SDL_KMOD_SHIFT | caps_mask;
148+
key = ((Uint32)shifted_modstate << 16) | scancode;
149+
150+
if (shifted_modstate != normalized_modstate && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
151+
return (SDL_Keycode)(uintptr_t)value;
152+
}
153+
}
154+
155+
// Shift Level 1 (unmodified)
156+
key = ((Uint32)caps_mask << 16) | scancode;
157+
if (SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
158+
return (SDL_Keycode)(uintptr_t)value;
159+
}
160+
161+
// Clear the capslock mask, if set.
162+
caps_mask = SDL_KMOD_NONE;
163+
}
164+
}
108165
}
109-
return keycode;
166+
167+
return SDL_GetDefaultKeyFromScancode(scancode, modstate);
110168
}
111169

112170
SDL_Scancode SDL_GetKeymapScancode(SDL_Keymap *keymap, SDL_Keycode keycode, SDL_Keymod *modstate)

0 commit comments

Comments
 (0)