Skip to main content

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}