fix: request keyboard enhancement flags for proper Release events
Root cause of both issues: terminals don't send Release events by default. When pressing W while holding A, terminal stops repeating A (but doesn't send Release), so A times out and strafing breaks. On key release, 400ms timeout means 400ms of extra movement. Fix: PushKeyboardEnhancementFlags(REPORT_EVENT_TYPES) in terminal init. This asks the terminal to send proper Press/Release/Repeat events for ALL keys, not just special ones. With Release events: - Key release is instant (no 400ms timeout delay) - Pressing W doesn't cancel A's held state (terminal sends Release only when A is actually released) - A/D strafing works while W is held Fallback: if terminal doesn't support enhancement flags (old xterm), got_release flag stays false and 150ms timeout is used. Once any Release event is received, all keys switch to Release-based mode (infinite timeout, rely on actual Release events). 109 tests, 0 warnings, 0 failures
This commit is contained in:
+19
-19
@@ -44,11 +44,10 @@ pub enum HeldKey {
|
|||||||
|
|
||||||
struct HeldState {
|
struct HeldState {
|
||||||
last_seen: Instant,
|
last_seen: Instant,
|
||||||
seen_repeat: bool,
|
got_release: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
const HOLD_TIMEOUT_PRESS: Duration = Duration::from_millis(400);
|
const FALLBACK_TIMEOUT: Duration = Duration::from_millis(150);
|
||||||
const HOLD_TIMEOUT_REPEAT: Duration = Duration::from_millis(80);
|
|
||||||
|
|
||||||
pub struct InputHandler {
|
pub struct InputHandler {
|
||||||
pub paint_brush: MaterialBrush,
|
pub paint_brush: MaterialBrush,
|
||||||
@@ -104,13 +103,6 @@ impl InputHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_held(&mut self, key: HeldKey, is_repeat: bool) {
|
|
||||||
self.held.insert(key, HeldState {
|
|
||||||
last_seen: Instant::now(),
|
|
||||||
seen_repeat: is_repeat,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn release(&mut self, key: HeldKey) {
|
fn release(&mut self, key: HeldKey) {
|
||||||
self.held.remove(&key);
|
self.held.remove(&key);
|
||||||
}
|
}
|
||||||
@@ -124,11 +116,6 @@ impl InputHandler {
|
|||||||
self.jump_pressed = false;
|
self.jump_pressed = false;
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
|
||||||
self.held.retain(|_, state| {
|
|
||||||
let timeout = if state.seen_repeat { HOLD_TIMEOUT_REPEAT } else { HOLD_TIMEOUT_PRESS };
|
|
||||||
now.duration_since(state.last_seen) < timeout
|
|
||||||
});
|
|
||||||
|
|
||||||
let events: Vec<Event> = match &self.receiver {
|
let events: Vec<Event> = match &self.receiver {
|
||||||
Some(rx) => rx.try_iter().collect(),
|
Some(rx) => rx.try_iter().collect(),
|
||||||
None => return one_shots,
|
None => return one_shots,
|
||||||
@@ -171,17 +158,30 @@ impl InputHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(held_key) = Self::key_to_held(code) {
|
if let Some(held_key) = Self::key_to_held(code) {
|
||||||
if is_press {
|
if is_press || is_repeat {
|
||||||
self.mark_held(held_key, false);
|
let prev_got_release = self.held.get(&held_key).map(|s| s.got_release).unwrap_or(false);
|
||||||
} else if is_repeat {
|
self.held.insert(held_key, HeldState {
|
||||||
self.mark_held(held_key, true);
|
last_seen: Instant::now(),
|
||||||
|
got_release: prev_got_release,
|
||||||
|
});
|
||||||
} else if is_release {
|
} else if is_release {
|
||||||
self.release(held_key);
|
self.release(held_key);
|
||||||
|
for state in self.held.values_mut() {
|
||||||
|
state.got_release = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.held.retain(|_, state| {
|
||||||
|
if state.got_release {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
now.duration_since(state.last_seen) < FALLBACK_TIMEOUT
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
one_shots
|
one_shots
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::io::{self, Write, stdout};
|
use std::io::{self, Write, stdout};
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor::{Hide, MoveTo, Show},
|
cursor::{Hide, MoveTo, Show},
|
||||||
event::{DisableMouseCapture, EnableMouseCapture},
|
event::{DisableMouseCapture, EnableMouseCapture, KeyboardEnhancementFlags, PushKeyboardEnhancementFlags, PopKeyboardEnhancementFlags},
|
||||||
execute, queue,
|
execute, queue,
|
||||||
style::{Color, SetBackgroundColor, SetForegroundColor, ResetColor, Print},
|
style::{Color, SetBackgroundColor, SetForegroundColor, ResetColor, Print},
|
||||||
terminal::{self, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, size as term_size},
|
terminal::{self, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, size as term_size},
|
||||||
@@ -53,6 +53,10 @@ impl Renderer for TerminalRenderer {
|
|||||||
stdout(),
|
stdout(),
|
||||||
EnterAlternateScreen,
|
EnterAlternateScreen,
|
||||||
Hide,
|
Hide,
|
||||||
|
PushKeyboardEnhancementFlags(
|
||||||
|
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||||
|
| KeyboardEnhancementFlags::REPORT_EVENT_TYPES
|
||||||
|
),
|
||||||
EnableMouseCapture,
|
EnableMouseCapture,
|
||||||
Clear(ClearType::All),
|
Clear(ClearType::All),
|
||||||
)?;
|
)?;
|
||||||
@@ -169,6 +173,7 @@ impl Renderer for TerminalRenderer {
|
|||||||
stdout(),
|
stdout(),
|
||||||
ResetColor,
|
ResetColor,
|
||||||
Show,
|
Show,
|
||||||
|
PopKeyboardEnhancementFlags,
|
||||||
LeaveAlternateScreen,
|
LeaveAlternateScreen,
|
||||||
DisableMouseCapture,
|
DisableMouseCapture,
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user