Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions native/shared/src/ffi_core/game_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,15 +348,15 @@ macro_rules! __bloom_ffi_game_loop {
#[no_mangle]
pub extern "C" fn bloom_inject_key_down(key: f64) {
$crate::ffi::guard("bloom_inject_key_down", move || {
engine().input.set_key_down(key as usize);
engine().input.inject_key_down(key as usize);
})
}

// bloom_inject_key_up [source: macos]
#[no_mangle]
pub extern "C" fn bloom_inject_key_up(key: f64) {
$crate::ffi::guard("bloom_inject_key_up", move || {
engine().input.set_key_up(key as usize);
engine().input.inject_key_up(key as usize);
})
}

Expand Down
42 changes: 42 additions & 0 deletions native/shared/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ pub struct InputState {
keys_released: [bool; MAX_KEYS],
prev_keys_down: [bool; MAX_KEYS],

// Injected keys (bloom_inject_key_down/up) are staged here and applied at the
// top of begin_frame, rather than written straight into keys_down.
//
// They HAVE to be, and the reason is the frame order: a real key arrives in
// poll_events(), which runs BEFORE begin_frame(), so the edge
// (keys_down && !prev_keys_down) is computed for it. An injected key is set
// from the game loop — AFTER begin_frame — so end_frame() copied it into
// prev_keys_down before begin_frame ever got to look at it, and the edge was
// never computed at all. is_key_down() saw the key; is_key_pressed() never
// did. That silently made the injection API useless for anything
// edge-triggered, which is to say every menu and every UI in every game
// built on this engine.
pending_key_down: [bool; MAX_KEYS],
pending_key_up: [bool; MAX_KEYS],

pub mouse_x: f64,
pub mouse_y: f64,
pub mouse_delta_x: f64,
Expand Down Expand Up @@ -83,6 +98,8 @@ impl InputState {
keys_down: [false; MAX_KEYS],
keys_released: [false; MAX_KEYS],
prev_keys_down: [false; MAX_KEYS],
pending_key_down: [false; MAX_KEYS],
pending_key_up: [false; MAX_KEYS],
mouse_x: 0.0,
mouse_y: 0.0,
mouse_delta_x: 0.0,
Expand Down Expand Up @@ -117,6 +134,20 @@ impl InputState {
}

pub fn begin_frame(&mut self) {
// Apply injected keys BEFORE the edge computation below, so an injection
// made during the previous frame reads exactly like a real key press:
// down this frame, not-down last frame => keys_pressed. See the note on
// pending_key_down.
for i in 0..MAX_KEYS {
if self.pending_key_down[i] {
self.keys_down[i] = true;
self.pending_key_down[i] = false;
}
if self.pending_key_up[i] {
self.keys_down[i] = false;
self.pending_key_up[i] = false;
}
}
if self.cursor_disabled {
self.mouse_delta_x = self.raw_delta_x;
self.mouse_delta_y = self.raw_delta_y;
Expand Down Expand Up @@ -160,9 +191,20 @@ impl InputState {
}

// Keyboard
/// Real key events, delivered from the platform's message pump. These run
/// before begin_frame(), so writing keys_down directly is correct here.
pub fn set_key_down(&mut self, key: usize) { if key < MAX_KEYS { self.keys_down[key] = true; } }
pub fn set_key_up(&mut self, key: usize) { if key < MAX_KEYS { self.keys_down[key] = false; } }

/// Synthetic key events (bloom_inject_key_down/up), which callers raise from
/// inside the game loop. Staged, not written — see pending_key_down.
pub fn inject_key_down(&mut self, key: usize) {
if key < MAX_KEYS { self.pending_key_down[key] = true; self.pending_key_up[key] = false; }
}
pub fn inject_key_up(&mut self, key: usize) {
if key < MAX_KEYS { self.pending_key_up[key] = true; self.pending_key_down[key] = false; }
}

pub fn is_key_pressed(&self, key: usize) -> bool { key < MAX_KEYS && self.keys_pressed[key] }
pub fn is_key_down(&self, key: usize) -> bool { key < MAX_KEYS && self.keys_down[key] }
pub fn is_key_released(&self, key: usize) -> bool { key < MAX_KEYS && self.keys_released[key] }
Expand Down
28 changes: 24 additions & 4 deletions native/windows/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,26 @@ fn map_keycode(vk: u32) -> usize {
}
}

// Windows reports the GENERIC modifier VKs — VK_SHIFT (0x10), VK_CONTROL (0x11),
// VK_MENU (0x12) — in WM_KEYDOWN/UP for BOTH the left and right keys. The
// side-specific codes (VK_LSHIFT 0xA0, etc.) come back only from GetKeyState,
// never in the message wParam. map_keycode knows only the side-specific codes,
// so a raw generic VK mapped to 0 and was dropped: every Shift and Ctrl press
// vanished, which is why sprint (Shift) and dodge (Ctrl) silently never fired on
// Windows. Recover the side from lParam — scancode for Shift (LShift = 0x2A,
// RShift = 0x36), the extended-key bit for Ctrl/Alt (the right-hand ones are
// flagged extended).
fn resolve_modifier_vk(vk: u32, lparam: isize) -> u32 {
let scancode = ((lparam >> 16) & 0xFF) as u32;
let extended = (lparam >> 24) & 1 != 0;
match vk {
0x10 => if scancode == 0x36 { 0xA1 } else { 0xA0 }, // VK_SHIFT -> R/L
0x11 => if extended { 0xA3 } else { 0xA2 }, // VK_CONTROL -> R/L
0x12 => if extended { 0xA5 } else { 0xA4 }, // VK_MENU -> R/L
_ => vk,
}
}

// ---------------------------------------------------------------------------
// Crash reporting (title-freeze / EN-020 investigation): the game was dying
// with empty stderr, no WER event, and no dump — i.e. invisibly. Catch
Expand Down Expand Up @@ -246,7 +266,7 @@ mod win32 {
LRESULT(0)
}
WM_KEYDOWN => {
let bloom_key = map_keycode(wparam.0 as u32);
let bloom_key = map_keycode(resolve_modifier_vk(wparam.0 as u32, lparam.0));
if bloom_key > 0 {
if let Some(eng) = ENGINE.get_mut() {
eng.input.set_key_down(bloom_key);
Expand Down Expand Up @@ -275,7 +295,7 @@ mod win32 {
DefWindowProcW(hwnd, msg, wparam, lparam)
}
WM_KEYUP => {
let bloom_key = map_keycode(wparam.0 as u32);
let bloom_key = map_keycode(resolve_modifier_vk(wparam.0 as u32, lparam.0));
if bloom_key > 0 {
if let Some(eng) = ENGINE.get_mut() {
eng.input.set_key_up(bloom_key);
Expand Down Expand Up @@ -494,11 +514,11 @@ mod win32 {
}
}
WM_KEYDOWN => {
let k = map_keycode(wparam.0 as u32);
let k = map_keycode(resolve_modifier_vk(wparam.0 as u32, lparam.0));
if k > 0 { if let Some(eng) = ENGINE.get_mut() { eng.input.set_key_down(k); } }
}
WM_KEYUP => {
let k = map_keycode(wparam.0 as u32);
let k = map_keycode(resolve_modifier_vk(wparam.0 as u32, lparam.0));
if k > 0 { if let Some(eng) = ENGINE.get_mut() { eng.input.set_key_up(k); } }
}
WM_MOUSEMOVE => {
Expand Down
Loading