diff --git a/src/events/SDL_keymap.c b/src/events/SDL_keymap.c index 32e4975e7d..318fc6864b 100644 --- a/src/events/SDL_keymap.c +++ b/src/events/SDL_keymap.c @@ -97,16 +97,74 @@ void SDL_SetKeymapEntry(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod mo SDL_Keycode SDL_GetKeymapKeycode(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate) { - SDL_Keycode keycode; + if (keymap) { + const void *value; + const SDL_Keymod normalized_modstate = NormalizeModifierStateForKeymap(modstate); + Uint32 key = ((Uint32)normalized_modstate << 16) | scancode; - const Uint32 key = ((Uint32)NormalizeModifierStateForKeymap(modstate) << 16) | scancode; - const void *value; - if (keymap && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) { - keycode = (SDL_Keycode)(uintptr_t)value; - } else { - keycode = SDL_GetDefaultKeyFromScancode(scancode, modstate); + // First, try the requested set of modifiers. + if (SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) { + return (SDL_Keycode)(uintptr_t)value; + } + + // If the requested set of modifiers was not found, search for the key from the highest to lowest modifier levels. + if (normalized_modstate) { + SDL_Keymod caps_mask = normalized_modstate & SDL_KMOD_CAPS; + + for (int i = caps_mask ? 2 : 1; i; --i) { + // Shift level 5 + if (normalized_modstate & SDL_KMOD_LEVEL5) { + const SDL_Keymod shifted_modstate = SDL_KMOD_LEVEL5 | caps_mask; + key = ((Uint32)shifted_modstate << 16) | scancode; + + if (shifted_modstate != normalized_modstate && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) { + return (SDL_Keycode)(uintptr_t)value; + } + } + + // Shift level 4 (Level 3 + Shift) + if ((normalized_modstate & (SDL_KMOD_MODE | SDL_KMOD_SHIFT)) == (SDL_KMOD_MODE | SDL_KMOD_SHIFT)) { + const SDL_Keymod shifted_modstate = SDL_KMOD_MODE | SDL_KMOD_SHIFT | caps_mask; + key = ((Uint32)shifted_modstate << 16) | scancode; + + if (shifted_modstate != normalized_modstate && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) { + return (SDL_Keycode)(uintptr_t)value; + } + } + + // Shift level 3 + if (normalized_modstate & SDL_KMOD_MODE) { + const SDL_Keymod shifted_modstate = SDL_KMOD_MODE | caps_mask; + key = ((Uint32)shifted_modstate << 16) | scancode; + + if (shifted_modstate != normalized_modstate && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) { + return (SDL_Keycode)(uintptr_t)value; + } + } + + // Shift level 2 + if (normalized_modstate & SDL_KMOD_SHIFT) { + const SDL_Keymod shifted_modstate = SDL_KMOD_SHIFT | caps_mask; + key = ((Uint32)shifted_modstate << 16) | scancode; + + if (shifted_modstate != normalized_modstate && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) { + return (SDL_Keycode)(uintptr_t)value; + } + } + + // Shift Level 1 (unmodified) + key = ((Uint32)caps_mask << 16) | scancode; + if (SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) { + return (SDL_Keycode)(uintptr_t)value; + } + + // Clear the capslock mask, if set. + caps_mask = SDL_KMOD_NONE; + } + } } - return keycode; + + return SDL_GetDefaultKeyFromScancode(scancode, modstate); } SDL_Scancode SDL_GetKeymapScancode(SDL_Keymap *keymap, SDL_Keycode keycode, SDL_Keymod *modstate)