diff --git a/runtime/include/host_keymap.h b/runtime/include/host_keymap.h index 7ad5476e..dd077093 100644 --- a/runtime/include/host_keymap.h +++ b/runtime/include/host_keymap.h @@ -30,6 +30,12 @@ void host_keymap_load(const char *config_ini_path); /* 1 if (keycode, mod) matches a binding for `action`. */ int host_keymap_match(HostKeymapAction action, int keycode, int mod); +/* Match a key event by its logical keycode or physical scancode. SDL can + * report a layout-dependent/unknown keycode while still providing the stable + * scancode stored with the configured binding. */ +int host_keymap_match_event(HostKeymapAction action, int keycode, + int scancode, int mod); + /* 1 if any configured bind for `action` is currently held. */ int host_keymap_down(HostKeymapAction action, const uint8_t *keys, int mod); diff --git a/runtime/src/host_keymap.c b/runtime/src/host_keymap.c index da2bbfbf..42263a76 100644 --- a/runtime/src/host_keymap.c +++ b/runtime/src/host_keymap.c @@ -205,19 +205,26 @@ void host_keymap_load(const char *config_ini_path) { apply_defaults(); } -int host_keymap_match(HostKeymapAction action, int keycode, int mod) { +int host_keymap_match_event(HostKeymapAction action, int keycode, + int scancode, int mod) { const HostKeyAction *a; const int relevant = (int)(KMOD_CTRL | KMOD_ALT | KMOD_SHIFT); int i; if (action < 0 || action >= HOST_KEYMAP_ACTION_COUNT) return 0; a = &s_actions[action]; for (i = 0; i < a->count; i++) { - if (a->binds[i].keycode != keycode) continue; + if (a->binds[i].keycode != keycode && + (scancode <= 0 || a->binds[i].scancode != scancode)) + continue; if ((mod & relevant) == a->binds[i].mods) return 1; } return 0; } +int host_keymap_match(HostKeymapAction action, int keycode, int mod) { + return host_keymap_match_event(action, keycode, 0, mod); +} + int host_keymap_down(HostKeymapAction action, const uint8_t *keys, int mod) { const HostKeyAction *a; const int relevant = (int)(KMOD_CTRL | KMOD_ALT | KMOD_SHIFT); diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 872d201e..b388cc3c 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -5657,7 +5657,8 @@ static int savestate_menu_slot_from_key(SDL_Keycode key) { return -1; } -static void savestate_menu_handle_key(SDL_Keycode key, int mod, int repeat) { +static void savestate_menu_handle_key(SDL_Keycode key, SDL_Scancode scancode, + int mod, int repeat) { int slot; if (repeat) return; @@ -5669,7 +5670,8 @@ static void savestate_menu_handle_key(SDL_Keycode key, int mod, int repeat) { savestate_menu_sync_overlay(); return; } - if (host_keymap_match(HOST_KEYMAP_SAVE_STATE_MENU, (int)key, mod) || + if (host_keymap_match_event(HOST_KEYMAP_SAVE_STATE_MENU, (int)key, + (int)scancode, mod) || key == SDLK_ESCAPE || key == SDLK_BACKSPACE) { savestate_menu_close(); } else if (key == SDLK_LEFT || key == SDLK_UP) { @@ -5846,14 +5848,17 @@ static void rewind_host_pause_loop(void) { #if defined(PSX_SDL3) const SDL_Keymod mod = ev.key.mod; const SDL_Keycode key = ev.key.key; + const SDL_Scancode scancode = ev.key.scancode; const int repeat = ev.key.repeat ? 1 : 0; #else const Uint16 mod = ev.key.keysym.mod; const SDL_Keycode key = ev.key.keysym.sym; + const SDL_Scancode scancode = ev.key.keysym.scancode; const int repeat = ev.key.repeat ? 1 : 0; #endif if (!repeat && - host_keymap_match(HOST_KEYMAP_REWIND, (int)key, (int)mod)) { + host_keymap_match_event(HOST_KEYMAP_REWIND, (int)key, + (int)scancode, (int)mod)) { psx_rewind_toggle(); } } @@ -5884,13 +5889,15 @@ static void savestate_menu_host_pause_loop(void) { #if defined(PSX_SDL3) const SDL_Keymod mod = ev.key.mod; const SDL_Keycode key = ev.key.key; + const SDL_Scancode scancode = ev.key.scancode; const int repeat = ev.key.repeat ? 1 : 0; #else const Uint16 mod = ev.key.keysym.mod; const SDL_Keycode key = ev.key.keysym.sym; + const SDL_Scancode scancode = ev.key.keysym.scancode; const int repeat = ev.key.repeat ? 1 : 0; #endif - savestate_menu_handle_key(key, (int)mod, repeat); + savestate_menu_handle_key(key, scancode, (int)mod, repeat); } else if (ev.type == SDL_KEYUP) { #if defined(PSX_SDL3) const SDL_Keycode key = ev.key.key; @@ -6099,10 +6106,12 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) { #if defined(PSX_SDL3) const SDL_Keymod mod = ev.key.mod; const SDL_Keycode key = ev.key.key; + const SDL_Scancode scancode = ev.key.scancode; const int key_repeat = ev.key.repeat ? 1 : 0; #else const Uint16 mod = ev.key.keysym.mod; const SDL_Keycode key = ev.key.keysym.sym; + const SDL_Scancode scancode = ev.key.keysym.scancode; const int key_repeat = ev.key.repeat ? 1 : 0; #endif if (key == SDLK_ESCAPE && psx_netplay_active()) { @@ -6110,12 +6119,14 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) { return ep; } if (!key_repeat && - host_keymap_match(HOST_KEYMAP_REWIND, (int)key, (int)mod)) { + host_keymap_match_event(HOST_KEYMAP_REWIND, (int)key, + (int)scancode, (int)mod)) { psx_rewind_toggle(); } else if (!key_repeat && - host_keymap_match(HOST_KEYMAP_SAVE_STATE_MENU, - (int)key, (int)mod)) { + host_keymap_match_event(HOST_KEYMAP_SAVE_STATE_MENU, + (int)key, (int)scancode, + (int)mod)) { savestate_menu_toggle(key); } else if (key == SDLK_c && (mod & KMOD_CTRL)) { @@ -6124,17 +6135,20 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) { host_osd_push("CD reinsert", 1500); } else if (!key_repeat && - host_keymap_match(HOST_KEYMAP_DISPLAY_PERF, (int)key, - (int)mod)) { + host_keymap_match_event(HOST_KEYMAP_DISPLAY_PERF, + (int)key, (int)scancode, + (int)mod)) { fps_telemetry_toggle(); } /* Host volume: config.ini [KeyMap] VolumeUp/VolumeDown * (defaults: keypad +/-). 5% steps; shows right-side bar. */ - else if (host_keymap_match(HOST_KEYMAP_VOLUME_UP, (int)key, - (int)mod)) { + else if (host_keymap_match_event(HOST_KEYMAP_VOLUME_UP, + (int)key, (int)scancode, + (int)mod)) { host_volume_adjust(+5); - } else if (host_keymap_match(HOST_KEYMAP_VOLUME_DOWN, (int)key, - (int)mod)) { + } else if (host_keymap_match_event(HOST_KEYMAP_VOLUME_DOWN, + (int)key, (int)scancode, + (int)mod)) { host_volume_adjust(-5); } /* Fullscreen toggle: Alt+Enter or Cmd/Ctrl+F. Toggles between @@ -6146,8 +6160,9 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) { * SDL_WINDOW_FULLSCREEN_DESKTOP, so testing just that bit * detects "currently fullscreen, either mode". */ else if (!key_repeat && - host_keymap_match(HOST_KEYMAP_FULLSCREEN, (int)key, - (int)mod)) { + host_keymap_match_event(HOST_KEYMAP_FULLSCREEN, + (int)key, (int)scancode, + (int)mod)) { Uint32 is_fs = SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_FULLSCREEN; if (is_fs) { diff --git a/runtime/tests/test_host_keymap.c b/runtime/tests/test_host_keymap.c index 25ddb8f5..6a498504 100644 --- a/runtime/tests/test_host_keymap.c +++ b/runtime/tests/test_host_keymap.c @@ -39,6 +39,10 @@ int main(int argc, char **argv) { "default volume down is keypad minus"); check(host_keymap_match(HOST_KEYMAP_DISPLAY_PERF, (int)SDLK_f, 0), "default display perf is F"); + check(host_keymap_match_event(HOST_KEYMAP_DISPLAY_PERF, + (int)SDLK_UNKNOWN, + (int)SDL_SCANCODE_F, 0), + "default display perf accepts its physical scancode"); f = fopen(cfg, "wb"); check(f != NULL, "create temporary config.ini"); @@ -71,8 +75,16 @@ int main(int argc, char **argv) { "volume down rebind uses Down"); check(!host_keymap_match(HOST_KEYMAP_DISPLAY_PERF, (int)SDLK_f, 0), "display perf rebind disables F fallback"); + check(!host_keymap_match_event(HOST_KEYMAP_DISPLAY_PERF, + (int)SDLK_UNKNOWN, + (int)SDL_SCANCODE_F, 0), + "display perf rebind disables the old F scancode"); check(host_keymap_match(HOST_KEYMAP_DISPLAY_PERF, (int)SDLK_F10, 0), "display perf rebind uses F10"); + check(host_keymap_match_event(HOST_KEYMAP_DISPLAY_PERF, + (int)SDLK_UNKNOWN, + (int)SDL_SCANCODE_F10, 0), + "display perf rebind accepts the F10 scancode"); remove(cfg); if (failures) {