shuyi_ui/input/state.rs
1//! [`InputState`]: turns key and controller-button presses and releases
2//! plus frame time into a game's actions, with repeat for held repeatable
3//! keys and buttons (game ADR-0015, game ADR-0034), and button chords (ticket 1034).
4//! Generic over the game's [`ActionSet`] (ticket 1106).
5//!
6//! **How a button chord fires.** A [button chord](ButtonChord) `X+Y` fires
7//! when one of its buttons goes down while the other is down and still
8//! holding back its own actions ([`Keymap::chord_wait`]): the chord's
9//! actions instead of either button's. A button that holds back waits
10//! for the chord window (`keymap.ron`'s `chord`) or until it is let go,
11//! whichever is first, or, if a partner of its acts on press, until it is
12//! let go; it then gives its own actions, unless a chord used it while it
13//! was held (then nothing). With the Playdate's `A+B`, `B+←` and `B+→`:
14//!
15//! - `A` and `B` pressed within the window, in either order, give End turn
16//! and neither Confirm nor Cancel; `B` held, `A` any time gives it too.
17//! - `B` held, `←` gives Previous unit, as often as `←` is pressed while
18//! `B` is held, and `←` held repeats it as it repeats the cursor;
19//! letting go of `B` then gives nothing.
20//! - `B` tapped (or held) alone gives Cancel when it is let go; `A` alone
21//! gives Confirm after the window, or on release if that is sooner.
22//!
23//! Buttons in no chord, keys, and every pad whose bindings have no chords
24//! act on press exactly as before.
25
26use crate::prelude::*;
27use core::time::Duration;
28use shuyi_assets::keymap::{ActionSet, Button, ButtonChord, Chord, Key, RepeatDef};
29
30use super::keymap::{ChordWait, Keymap};
31use super::pad::{ButtonSet, PadKind};
32
33/// At most this many repeats are emitted by one [`InputState::update`], so a
34/// lag spike can't teleport the cursor across the map.
35pub const MAX_REPEATS_PER_UPDATE: u64 = 5;
36
37/// What the player is playing with: whatever they pressed last
38/// (`docs/design/controls.md`, *Switching between keyboard and
39/// controller*). Help text names keys or this pad's buttons to match.
40#[derive(
41 Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize,
42)]
43pub enum Device {
44 /// The keyboard.
45 #[default]
46 Keyboard,
47 /// A controller of this kind.
48 Pad(PadKind),
49}
50
51/// Something the player can hold down: a keyboard key or a controller
52/// button (a stick direction counts as a button).
53#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
54pub enum Input {
55 /// A keyboard key.
56 Key(Key),
57 /// A controller button.
58 Pad(Button),
59}
60
61/// The repeat currently running for the most recently pressed repeatable
62/// key or button.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub(super) struct Repeat<A> {
65 pub(super) input: Input,
66 pub(super) action: A,
67 /// Time held so far, in microseconds.
68 held_us: u64,
69 /// Repeats accounted for so far (emitted or dropped by the cap).
70 counted: u64,
71}
72
73impl<A> Repeat<A> {
74 fn start(input: Input, action: A) -> Self {
75 Self {
76 input,
77 action,
78 held_us: 0,
79 counted: 0,
80 }
81 }
82}
83
84/// A key or button that is down with an action it triggered.
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86struct Held<A> {
87 input: Input,
88 action: A,
89 /// Whether holding it re-emits the action.
90 repeats: bool,
91 /// For a button chord's action: the chord's other button, whose
92 /// release ends it too.
93 partner: Option<Button>,
94}
95
96/// How much longer a [`Waiting`] button holds back its own actions.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98enum Until {
99 /// Until the chord window has passed (or it is let go): time waited,
100 /// in microseconds, counted from the first update after the press.
101 Window { waited_us: u64, counting: bool },
102 /// Until it is let go.
103 Release,
104}
105
106/// A chord button that is down and holding back its own actions
107/// ([`ChordWait`]).
108#[derive(Debug, Clone, PartialEq, Eq)]
109struct Waiting<A> {
110 button: Button,
111 /// Its own actions, given when it stops waiting, unless `used`.
112 actions: Vec<A>,
113 until: Until,
114 /// A chord fired with it while it was held: it gives nothing.
115 used: bool,
116}
117
118/// Turns key and controller-button events and elapsed time into actions.
119///
120/// Feed it every key press ([`key_down`](Self::key_down)) and release
121/// ([`key_up`](Self::key_up)), and every controller button press
122/// ([`pad_down`](Self::pad_down)) and release ([`pad_up`](Self::pad_up)),
123/// then call [`update`](Self::update) once per frame with the frame time.
124/// Keys and buttons follow the same rules. A press emits its action once.
125/// While a repeatable action's key or button is held, it re-emits after the
126/// repeat delay and then every interval. The most recently pressed
127/// repeatable one is the one that repeats; releasing it hands repeating
128/// back to the most recent repeatable one still held, which waits a full
129/// delay before repeating. Button chords: see the [module docs](self).
130/// After a time it heard no keys (a resume from a suspend), it lets go of
131/// everything ([`release_all`](Self::release_all)).
132#[derive(Debug, Clone)]
133pub struct InputState<A: ActionSet> {
134 keymap: Keymap<A>,
135 /// Actions from presses and releases since the last update.
136 pending: Vec<A>,
137 /// Actions let go since the last update, in order.
138 letting_go: Vec<A>,
139 /// The last update's actions from presses, without its repeats.
140 presses: Vec<A>,
141 /// The actions let go before the last update, in order.
142 released: Vec<A>,
143 /// Bound keys and buttons currently down with the actions they
144 /// triggered (one entry each), oldest first.
145 held: Vec<Held<A>>,
146 pub(super) repeat: Option<Repeat<A>>,
147 /// Every controller button down since this keymap was set, bound or
148 /// not.
149 buttons_down: ButtonSet,
150 /// Chord buttons holding back their own actions, oldest first.
151 waiting: Vec<Waiting<A>>,
152 /// Where the last bound press came from.
153 device: Device,
154}
155
156impl<A: ActionSet> InputState<A> {
157 /// Input handling with the given bindings and repeat timings.
158 pub fn new(keymap: Keymap<A>) -> Self {
159 Self {
160 keymap,
161 pending: Vec::new(),
162 letting_go: Vec::new(),
163 presses: Vec::new(),
164 released: Vec::new(),
165 held: Vec::new(),
166 repeat: None,
167 buttons_down: ButtonSet::EMPTY,
168 waiting: Vec::new(),
169 device: Device::default(),
170 }
171 }
172
173 /// Starts as if `device` had been pressed last (a platform whose only
174 /// input is its pad).
175 #[must_use]
176 pub fn starting_on(mut self, device: Device) -> Self {
177 self.device = device;
178 self
179 }
180
181 /// What the player pressed last: the keyboard, or a pad of some kind.
182 /// Only presses that do something count (a bound key or button going
183 /// down), so an unbound key or a resting stick never switches it.
184 /// The keyboard until anything is pressed, unless the game started it
185 /// on the platform's own pad.
186 pub fn device(&self) -> Device {
187 self.device
188 }
189
190 /// The bindings in use.
191 pub fn keymap(&self) -> &Keymap<A> {
192 &self.keymap
193 }
194
195 /// Switches to other bindings (the player picked a layout). Lets go of
196 /// everything first ([`release_all`](Self::release_all)), so nothing
197 /// pressed under the old bindings leaks into the new ones.
198 pub fn set_keymap(&mut self, keymap: Keymap<A>) {
199 self.keymap = keymap;
200 self.release_all();
201 }
202
203 /// Lets go of everything: forgets held keys and buttons, pending
204 /// presses, waiting chord buttons (which give nothing) and any running
205 /// repeat. A key or button still down is ignored until it is pressed
206 /// again. The actions it held are let go
207 /// ([`released`](Self::released) after the next update). The
208 /// [`device`](Self::device) and the bindings stay. The game calls it
209 /// on the first frame after a resume, whose keys may have come up
210 /// while it was suspended.
211 pub fn release_all(&mut self) {
212 // Every field named, so a new one can't be forgotten here.
213 let Self {
214 keymap: _,
215 pending,
216 letting_go,
217 presses,
218 released,
219 held,
220 repeat,
221 buttons_down,
222 waiting,
223 device: _,
224 } = self;
225 for h in held.drain(..) {
226 if !letting_go.contains(&h.action) {
227 letting_go.push(h.action);
228 }
229 }
230 pending.clear();
231 presses.clear();
232 released.clear();
233 *repeat = None;
234 *buttons_down = ButtonSet::EMPTY;
235 waiting.clear();
236 }
237
238 /// A key went down (with its modifier state). Unbound chords and keys
239 /// already held are ignored.
240 pub fn key_down(&mut self, chord: Chord) {
241 let actions = self.keymap.actions(chord).to_vec();
242 self.press(Input::Key(chord.key), &actions, Device::Keyboard);
243 }
244
245 /// A key went down that the game kept for itself (it opened the layout
246 /// picker): the keyboard is the [`device`](Self::device) pressed last,
247 /// and nothing else happens.
248 pub fn keyboard_used(&mut self) {
249 self.device = Device::Keyboard;
250 }
251
252 /// A key went up. Stops its repeat, if it was the repeating one.
253 pub fn key_up(&mut self, key: Key) {
254 self.release(|h| h.input == Input::Key(key));
255 }
256
257 /// A controller button went down, on a pad of `kind`
258 /// ([`Pads`](super::pad::Pads) merges the pads and gives the button's
259 /// binding position). Unbound buttons and buttons already down are
260 /// ignored. A button in a chord may fire the chord, or hold back its
261 /// own actions (see the [module docs](self)).
262 pub fn pad_down(&mut self, button: Button, kind: PadKind) {
263 if self.buttons_down.contains(button) {
264 return;
265 }
266 self.buttons_down.insert(button);
267 let device = Device::Pad(kind);
268 if self.fire_chord(button, device) {
269 return;
270 }
271 let actions = self.keymap.pad_actions(button).to_vec();
272 let until = match self.keymap.chord_wait(button) {
273 ChordWait::None => return self.press(Input::Pad(button), &actions, device),
274 ChordWait::Window => Until::Window {
275 waited_us: 0,
276 counting: false,
277 },
278 ChordWait::Release => Until::Release,
279 };
280 self.device = device;
281 self.waiting.push(Waiting {
282 button,
283 actions,
284 until,
285 used: false,
286 });
287 }
288
289 /// The controller button `button`, just pressed, was taken by the game
290 /// for itself (it ended a "press any button" wait), so it does nothing
291 /// else: if it is holding back its own actions for a chord, it gives
292 /// none of them. (A button that acts on press has given its actions
293 /// already, in this frame's update, for the game to ignore.)
294 pub fn pad_taken(&mut self, button: Button) {
295 if let Some(w) = self.waiting.iter_mut().find(|w| w.button == button) {
296 w.used = true;
297 w.until = Until::Release;
298 }
299 }
300
301 /// Fires the chord of `button` (just pressed) and the most recently
302 /// pressed waiting button it has a chord with, if any: true if one
303 /// fired.
304 fn fire_chord(&mut self, button: Button, device: Device) -> bool {
305 let keymap = &self.keymap;
306 let chord_of = |w: &Waiting<A>| ButtonChord::pair(button, w.button);
307 let found = self.waiting.iter_mut().rev().find_map(|w| {
308 let actions = keymap.chord_actions(chord_of(w)?);
309 (!actions.is_empty()).then(|| (w, actions.to_vec()))
310 });
311 let Some((partner, actions)) = found else {
312 return false;
313 };
314 // The partner gives nothing now, but stays a partner while held.
315 partner.used = true;
316 partner.until = Until::Release;
317 let partner = partner.button;
318 // Pressed with a button that repeats (the D-pad), the chord repeats
319 // as that button would.
320 let own = self.keymap.pad_actions(button);
321 let repeats = own.iter().any(|a| a.is_repeatable());
322 self.device = device;
323 self.hold(Input::Pad(button), Some(partner), &actions, |a| {
324 repeats || a.is_repeatable()
325 });
326 true
327 }
328
329 /// A controller button went up. A waiting chord button gives its own
330 /// actions now, unless a chord used it. Stops its repeat, if it was the
331 /// repeating one, and ends the chords it is in.
332 pub fn pad_up(&mut self, button: Button) {
333 self.buttons_down.remove(button);
334 if let Some(i) = self.waiting.iter().position(|w| w.button == button) {
335 let waited = self.waiting.remove(i);
336 if !waited.used {
337 // Pressed and let go at once: a tap.
338 self.pending.extend_from_slice(&waited.actions);
339 self.letting_go.extend(waited.actions);
340 }
341 }
342 // Its own actions, and those of the chords it is the partner in.
343 self.release(|h| h.input == Input::Pad(button) || h.partner == Some(button));
344 }
345
346 /// `input` went down on `device`, bound to `actions` (none if
347 /// unbound).
348 fn press(&mut self, input: Input, actions: &[A], device: Device) {
349 if actions.is_empty() || self.held.iter().any(|h| h.input == input) {
350 return;
351 }
352 self.device = device;
353 self.hold(input, None, actions, A::is_repeatable);
354 }
355
356 /// Emits `actions` for `input`'s press and holds them while it (and a
357 /// chord's `partner`) is down; those `repeats` says yes to repeat.
358 fn hold(
359 &mut self,
360 input: Input,
361 partner: Option<Button>,
362 actions: &[A],
363 repeats: impl Fn(A) -> bool,
364 ) {
365 for &action in actions {
366 let repeats = repeats(action);
367 self.held.push(Held {
368 input,
369 action,
370 repeats,
371 partner,
372 });
373 self.pending.push(action);
374 if repeats {
375 self.repeat = Some(Repeat::start(input, action));
376 }
377 }
378 }
379
380 /// Lets go of the held actions that `gone` picks; an action no key or
381 /// button holds any more is let go ([`released`](Self::released)). If
382 /// the repeating one is among them, repeating passes to the most recent
383 /// repeating one still held.
384 fn release(&mut self, gone: impl Fn(&Held<A>) -> bool) {
385 let repeating = |h: &Held<A>| {
386 self.repeat
387 .is_some_and(|r| (r.input, r.action) == (h.input, h.action))
388 };
389 let stops = self.held.iter().any(|h| gone(h) && repeating(h));
390 let mut letting_go: Vec<A> = Vec::new();
391 for h in self.held.iter().filter(|h| gone(h)) {
392 if !letting_go.contains(&h.action) {
393 letting_go.push(h.action);
394 }
395 }
396 self.held.retain(|h| !gone(h));
397 letting_go.retain(|&action| !self.is_held(action));
398 self.letting_go.append(&mut letting_go);
399 if stops {
400 self.repeat = self
401 .held
402 .iter()
403 .rev()
404 .find(|h| h.repeats)
405 .map(|h| Repeat::start(h.input, h.action));
406 }
407 }
408
409 /// Every held key or button with the action it holds, oldest first.
410 #[cfg(test)]
411 pub(super) fn held_pairs(&self) -> Vec<(Input, A)> {
412 self.held.iter().map(|h| (h.input, h.action)).collect()
413 }
414
415 /// Whether any held key or button is bound to `action` (e.g. hold to
416 /// fast-forward). A chord button holding back its action doesn't hold
417 /// it yet.
418 pub fn is_held(&self, action: A) -> bool {
419 self.held.iter().any(|h| h.action == action)
420 }
421
422 /// The last [`update`](Self::update)'s actions from presses, in order,
423 /// without its key repeats: what a fixed step reads
424 /// ([`TickDriver`](crate::tick::TickDriver)). An action pressed again
425 /// while another of its keys holds it is here again.
426 pub fn presses(&self) -> &[A] {
427 &self.presses
428 }
429
430 /// The actions let go before the last [`update`](Self::update), in
431 /// order: each when no key or button holds it any more, and a chord
432 /// button's own action, given as the button is let go
433 /// ([module docs](self)), at once.
434 pub fn released(&self) -> &[A] {
435 &self.released
436 }
437
438 /// Advances time by `dt` seconds and returns the actions to handle this
439 /// frame, in order: chord buttons whose window has just passed, presses
440 /// and releases since the last update, then repeats (at most
441 /// [`MAX_REPEATS_PER_UPDATE`]; repeats beyond the cap are dropped, not
442 /// deferred). Negative or non-finite `dt` counts as zero. The same
443 /// actions without the repeats are [`presses`](Self::presses) until the
444 /// next update, and the actions let go since the last one
445 /// [`released`](Self::released).
446 pub fn update(&mut self, dt: f32) -> Vec<A> {
447 let dt_us = seconds_to_micros(dt);
448 let mut actions = self.windows_passed(dt_us);
449 actions.append(&mut self.pending);
450 self.presses.clone_from(&actions);
451 self.released.clear();
452 self.released.append(&mut self.letting_go);
453 let RepeatDef {
454 delay_ms,
455 interval_ms,
456 } = self.keymap.repeat();
457 if let Some(r) = &mut self.repeat {
458 r.held_us = r.held_us.saturating_add(dt_us);
459 let delay_us = u64::from(delay_ms) * 1000;
460 // A zero interval is rejected by the loader; guard anyway.
461 let interval_us = u64::from(interval_ms).max(1) * 1000;
462 let due = match r.held_us.checked_sub(delay_us) {
463 Some(past_delay) => 1 + past_delay / interval_us,
464 None => 0,
465 };
466 let new = due.saturating_sub(r.counted).min(MAX_REPEATS_PER_UPDATE);
467 r.counted = due;
468 for _ in 0..new {
469 actions.push(r.action);
470 }
471 }
472 actions
473 }
474
475 /// Counts `dt_us` towards the chord window of the buttons waiting for
476 /// it (from the first update after each press, so a long frame never
477 /// eats the window of a button pressed during it) and gives the own
478 /// actions of those whose window has passed, which are held from then
479 /// on. Their actions never repeat: a button whose own action repeats
480 /// never waits.
481 fn windows_passed(&mut self, dt_us: u64) -> Vec<A> {
482 let window_us = u64::from(self.keymap.chord().window_ms) * 1000;
483 let mut passed = Vec::new();
484 self.waiting.retain_mut(|w| {
485 let Until::Window {
486 waited_us,
487 counting,
488 } = &mut w.until
489 else {
490 return true;
491 };
492 if *counting {
493 *waited_us = waited_us.saturating_add(dt_us);
494 }
495 *counting = true;
496 if *waited_us < window_us {
497 return true;
498 }
499 passed.push((w.button, core::mem::take(&mut w.actions)));
500 false
501 });
502 let mut actions = Vec::new();
503 for (button, own) in passed {
504 for action in own {
505 self.held.push(Held {
506 input: Input::Pad(button),
507 action,
508 repeats: false,
509 partner: None,
510 });
511 actions.push(action);
512 }
513 }
514 actions
515 }
516}
517
518/// Converts seconds to whole microseconds, rounding to nearest. Rounding
519/// per frame keeps millisecond-exact timings exact despite `f32` error.
520pub(super) fn seconds_to_micros(dt: f32) -> u64 {
521 let Ok(d) = Duration::try_from_secs_f32(dt) else {
522 return 0;
523 };
524 u64::try_from(d.as_nanos().saturating_add(500) / 1000).unwrap_or(u64::MAX)
525}