fix(input): keys are physical positions on every layout; Input.key_label names them

The Windows runtime keyed the held set by the character MapVirtualKeyA gave a
virtual key, so a game's WASD belonged to whatever the active layout put there,
and with an input method on every letter arrived as VK_PROCESSKEY. The typing
block is now read from the scancode (the arrows, numpad and F-keys still by
virtual key); macOS reads keyCode the same way. Input.key_label(key) names a key
in the player's own layout (Windows) or as its US character elsewhere.

Verified on Windows with SendInput into a live window: VK_Z carrying W's scancode
holds 'w', VK_PROCESSKEY carrying A's holds 'a', Caps Lock changes nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-15 23:26:53 +03:00
parent 2a2f463d5b
commit 619476ffed
14 changed files with 60552 additions and 60168 deletions

View file

@ -372,6 +372,46 @@ function input_key_down(k: int) -> bool { in_init(); return in_bit_get(in_h
function input_key_pressed(k: int) -> bool { in_init(); return in_bit_get(in_held, k) and (not in_bit_get(in_prev, k)) }
function input_key_released(k: int) -> bool { in_init(); return (not in_bit_get(in_held, k)) and in_bit_get(in_prev, k) }
# The name to show a player for key code `k`. A letter, digit or punctuation code is a
# PHYSICAL key - the one that types it on a US layout - so the name is what the player's
# own layout types there: 'w' reads "W" on QWERTY and "Z" on AZERTY, and a binding
# shown as "W" is never a key the player cannot find. Named keys and the arrows get words.
# Headless, and on a platform that cannot ask the layout, it is the US character.
function input_key_label(k: int) -> string {
if k == 32 { return "Space" }
if k == 10 { return "Enter" }
if k == 27 { return "Esc" }
if k == 9 { return "Tab" }
if k == 8 { return "Backspace" }
if k == 16 { return "Shift" }
if k == 17 { return "Ctrl" }
if k == 18 { return "Alt" }
if k == 128 { return "Up" }
if k == 129 { return "Down" }
if k == 130 { return "Left" }
if k == 131 { return "Right" }
var c = 0
if is_windowed() { c = win_key_char(k) }
if c <= 0 and k > 32 and k < 127 {
c = k
if c >= 97 and c <= 122 { c = c - 32 }
}
if c <= 0 { return "" }
return in_utf8(c)
}
# one code point as a UTF-8 string
function in_utf8(c: int) -> string {
let out = bytes(5)
var n = 0
if c < 128 { out[0] = c; n = 1 }
else if c < 2048 { out[0] = 192 | (c >> 6); out[1] = 128 | (c & 63); n = 2 }
else if c < 65536 { out[0] = 224 | (c >> 12); out[1] = 128 | ((c >> 6) & 63); out[2] = 128 | (c & 63); n = 3 }
else { out[0] = 240 | (c >> 18); out[1] = 128 | ((c >> 12) & 63); out[2] = 128 | ((c >> 6) & 63); out[3] = 128 | (c & 63); n = 4 }
out[n] = 0
let s: string = out
return s
}
# inject a held key (AI, tutorial, testing, network) — persists until released.
function input_press(k: int) -> void { in_init(); in_bit_set(in_sim, k, true) }
function input_release(k: int) -> void { in_init(); in_bit_set(in_sim, k, false) }