fix(input): Windows Shift/Ctrl were dead + injected keys made no edges#99
Merged
proggeramlug merged 2 commits intoJul 15, 2026
Merged
Conversation
added 2 commits
July 15, 2026 17:29
keys_pressed is computed in begin_frame as (keys_down && !prev_keys_down), but prev_keys_down is copied from keys_down in end_frame. A REAL key arrives in poll_events(), which runs BEFORE begin_frame, so its edge is computed. An INJECTED key (bloom_inject_key_down) is raised from the game loop — AFTER begin_frame — so end_frame folded it into prev_keys_down before begin_frame ever looked at it, and the edge was never computed at all. isKeyDown() saw injected keys; isKeyPressed() never did. That silently made the injection API useless for anything edge-triggered — every menu and every UI in any game on this engine, which is the real cause behind "synthetic input doesn't reach menus". Injected keys are now staged in pending_key_down/up and applied at the TOP of begin_frame, before the edge loop. Real input (set_key_down/up from the message pump) is untouched. An injection on frame N now reads exactly like a real press on frame N+1. Verified: a shooter harness drives MAIN -> SETTINGS -> ESC-back -> PLAY entirely through injectKeyDown and asserts the menu state at each step. Claude-Session: https://claude.ai/code/session_01J1UWgMcrTNvwWeXcJ1T3Fp
…odifier VK map_keycode only knew the SIDE-SPECIFIC modifier codes (VK_LSHIFT 0xA0, VK_LCONTROL 0xA2, ...). But Windows delivers the GENERIC VK in WM_KEYDOWN/UP — VK_SHIFT (0x10) for either shift, VK_CONTROL (0x11) for either ctrl, VK_MENU (0x12) for alt. The side-specific codes come back only from GetKeyState, never in the message wParam. So map_keycode(0x10) hit `_ => 0` and the press was dropped: every Shift and Ctrl vanished before it reached isKeyDown(). Consequence in the shooter: sprint (Shift) and dodge (Ctrl) silently never fired on Windows. Only letters/arrows/F-keys ever worked — which is why WASD, Space, R and Q were fine and nobody spotted it. resolve_modifier_vk() recovers the side from lParam — scancode for shift (LShift 0x2A / RShift 0x36), the extended-key bit for ctrl/alt (the right-hand ones are extended) — and runs before map_keycode in BOTH wndprocs (standalone + embedded subclass). Verified with a real OS keypress, not injection: SendInput with KEYEVENTF_SCANCODE 0x2A produces a genuine WM_KEYDOWN with wParam=0x10, and a probe latch then reads isKeyDown(LEFT_SHIFT)=1 (it read 0 before this change). In-game: holding Shift now takes the player 4.50 -> 9.00 m/s. Claude-Session: https://claude.ai/code/session_01J1UWgMcrTNvwWeXcJ1T3Fp
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughKeyboard injection now stages events until frame start so pressed/released edges are computed correctly. Windows keyboard handlers resolve generic Shift, Ctrl, and Alt messages to side-specific keys before mapping them. ChangesKeyboard input handling
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant FFI
participant InputState
participant FrameLoop
FFI->>InputState: Inject synthetic key event
InputState->>InputState: Store pending key state
FrameLoop->>InputState: Begin frame
InputState->>InputState: Apply pending state
InputState-->>FrameLoop: Compute pressed/released edges
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two input bugs, both of which silently disabled whole features rather than
failing loudly.
1. Shift/Ctrl/Alt never registered on Windows
map_keycodeonly knew the side-specific modifier codes (VK_LSHIFT0xA0,VK_LCONTROL0xA2, …). But Windows delivers the generic VK inWM_KEYDOWN/UP—VK_SHIFT(0x10) for either shift,VK_CONTROL(0x11) foreither ctrl. The side-specific codes come back only from
GetKeyState, never inthe message
wParam. Somap_keycode(0x10)hit_ => 0and the press wasdropped: every Shift and Ctrl vanished before reaching
isKeyDown().Consequence in the shooter: sprint (Shift) and dodge (Ctrl) silently never
fired on Windows. Only letters/arrows/F-keys ever worked — which is why WASD,
Space, R and Q were fine and nobody spotted it.
resolve_modifier_vk()recovers the side fromlParam(scancode for shift —LShift 0x2A / RShift 0x36; the extended-key bit for ctrl/alt) and runs before
map_keycodein both wndprocs (standalone + embedded subclass).2. Injected keys never produced an
isKeyPressededgekeys_pressedis computed inbegin_frameaskeys_down && !prev_keys_down,but
prev_keys_downis copied inend_frame. A real key arrives inpoll_events()— beforebegin_frame— so it gets an edge. An injectedkey is raised from the game loop, after
begin_frame, soend_framefolded itinto
prev_keys_downbeforebegin_frameever looked.isKeyDown()saw it;isKeyPressed()never did — making the injection API useless for anythingedge-triggered (every menu/UI in any game on this engine).
Injected keys are now staged in
pending_key_down/upand applied at the topof
begin_frame, before the edge loop. Real input is untouched.Verification
SendInputwithKEYEVENTF_SCANCODE0x2Aproduces a genuine
WM_KEYDOWNwithwParam=0x10; a probe latch then readsisKeyDown(LEFT_SHIFT)=1(it read 0 before this change). In-game, holdingShift takes the player 4.50 → 9.00 m/s.
entirely through
injectKeyDown, asserting menu state at each step.Pairs with Bloom-Engine/shooter#20.
https://claude.ai/code/session_01J1UWgMcrTNvwWeXcJ1T3Fp
Summary by CodeRabbit