shuyi_ui/input.rs
1//! Keyboard and controller input as a game's actions (game ADR-0006, game ADR-0034),
2//! generic over its [`ActionSet`] (tickets 1106 and 1107): a data-driven
3//! [`Keymap`] from chords and controller buttons to actions
4//! ([`keymap`](mod@keymap)), and [`InputState`] ([`state`]), which turns
5//! presses and releases plus frame time into the actions screens see,
6//! including repeat for held repeatable keys and buttons. [`FrameInput`]
7//! ([`frame`]) is one frame's input as a screen sees it.
8//!
9//! The player's own bindings (3 slots per action, per layout, and the
10//! controller buttons) are in [`bindings`]; [`pad`] turns the controllers'
11//! raw state into button presses, and [`crank`] a crank's turns; [`help_keys`]
12//! names keys and buttons in help text.
13//!
14//! **Fixed keys** ([`fixed`], game ADR-0031): plain `Escape` backs out of
15//! capturing a key and plain `Delete` empties a slot on a rebinding
16//! screen, so neither can be bound ([`Chord::is_reserved`]). Which action a
17//! fixed key also triggers in play is the game's
18//! ([`ActionSet::fixed_keys`]). `Shift+Escape` and `Shift+Delete` are
19//! ordinary chords.
20//!
21//! A screen declares the actions it reacts to
22//! ([`Screen::actions`](crate::screen::Screen::actions)); [`ui_actions`]
23//! gives them for an engine screen or widget, which reacts to
24//! [`UiAction`]s. [`fit`] checks that the required ones fit a target's
25//! buttons (ADR-0008 ยง3).
26//!
27//! [`Key`], [`Chord`], [`Button`] and [`Layout`] are the keymap file's
28//! (`shuyi_assets::keymap`) and re-exported here.
29
30use alloc::vec::Vec;
31
32pub mod bindings;
33pub mod crank;
34pub mod fit;
35pub mod fixed;
36pub mod frame;
37pub mod help_keys;
38pub mod keymap;
39pub mod pad;
40pub mod state;
41
42#[cfg(test)]
43mod chord_tests;
44#[cfg(any(test, feature = "toy"))]
45pub mod sample;
46#[cfg(test)]
47mod tests;
48#[cfg(test)]
49mod toy_tests;
50
51pub use bindings::{BindError, ButtonSlots, LayoutBindings, PadBindings, PlayerKeys, Slots};
52pub use crank::Crank;
53pub use fixed::{
54 TextKey, capture_abort_key_name, clear_slot_key_name, is_capture_abort, is_clear_slot,
55 text_key, text_key_names,
56};
57pub use frame::{FrameInput, frame_seconds};
58pub use help_keys::{ARROWS, HelpKeys};
59pub use keymap::{ChordWait, Keymap};
60pub use pad::{ButtonSet, PadId, PadKind, PadState, Pads};
61pub use shuyi_assets::keymap::{
62 ActionSet, Bindings, Button, ButtonChord, Chord, ChordDef, CrankDef, Key, KeymapDef,
63 LAYOUT_NAME_MAX, Layout, LayoutKeys, PadKeys, RepeatDef, SLOTS, StickDef, UiAction,
64};
65pub use state::{Device, Input, InputState, MAX_REPEATS_PER_UPDATE};
66
67/// What the engine's menu-like screens react to: the four directions,
68/// Confirm and Cancel.
69pub const NAVIGATION: &[UiAction] = &[
70 UiAction::Up,
71 UiAction::Down,
72 UiAction::Left,
73 UiAction::Right,
74 UiAction::Confirm,
75 UiAction::Cancel,
76];
77
78/// The game's actions that [`ActionSet::ui`] takes for one of `uis`, in
79/// [`ActionSet::ALL`] order: what an engine screen or widget that reacts
80/// to `uis` declares ([`Screen::actions`](crate::screen::Screen::actions)).
81pub fn ui_actions<A: ActionSet>(uis: &[UiAction]) -> Vec<A> {
82 A::ALL
83 .iter()
84 .copied()
85 .filter(|a| a.ui().is_some_and(|ui| uis.contains(&ui)))
86 .collect()
87}