Skip to main content

mirage_engine/input/
mod.rs

1//! Actions: what a player can do, and the controls bound to each one.
2//!
3//! A game names its own actions — `Jump`, `Walk`, `Aim` — in up to three
4//! vocabularies, one per kind: a button is pressed, released or down, an
5//! axis reads one number, an axis2 reads two. [`InputActions`] holds the
6//! three, and [`Game::InputActions`](crate::Game::InputActions) names the
7//! set; [`NoInputActions`] is the set of a game that reads no input, and
8//! [`Key`] is the prototype set that binds each key to itself. Every query
9//! — `down`, `pressed`, `released`, `axis`, `axis2` — takes an action of
10//! that set and no other.
11//!
12//! Each action declares its own default bindings, which may hold a key, a
13//! mouse button, a pad button, a stick or a composite of them. An axis or
14//! axis2 binding holds a deadzone —
15//! [`AxisBinding::DEFAULT_DEADZONE`](crate::AxisBinding::DEFAULT_DEADZONE)
16//! for a pad or joystick control, and zero for the rest — and a scale.
17//! A [`PointerDelta`] lane, a [`WheelDelta`] lane and
18//! [`Axis2Binding::pointer`](crate::Axis2Binding::pointer) read how far
19//! they moved through that scale and nothing clamps what they read; every
20//! other binding reads in its kind's own range.
21//!
22//! A game that rebinds reads [`bindings`](crate::FrameContext::bindings) for
23//! what an action is bound to now, and shows each binding's text.
24//! [`actuated_button`](crate::FrameContext::actuated_button),
25//! [`actuated_axis`](crate::FrameContext::actuated_axis) and
26//! [`actuated_axis2`](crate::FrameContext::actuated_axis2) then report
27//! whatever the player just moved, which
28//! [`rebind`](crate::FrameContext::rebind) takes. The engine keeps every
29//! rebind in the platform's own store, under a name made from the title
30//! [`Config::new`](crate::Config::new) was given.
31//!
32//! [`Cursor`] is what the pointer itself is drawn as, the closed set
33//! [`set_cursor`](crate::FrameContext::set_cursor) takes for one frame; a
34//! frame that sets none draws [`Cursor::Arrow`]. Where the UI sets a cursor
35//! of its own, the UI's is drawn instead. [`Cursor::Held`] draws no pointer
36//! and holds it in place, leaving a [`PointerDelta`] binding its
37//! movement to read and [`pointer`](crate::FrameContext::pointer) the place
38//! it was held at; the frame after it sets another cursor releases it.
39
40pub use action::{
41    InputAction, InputActions, InputAxis2Action, InputAxisAction, InputButtonAction,
42    NoInputActions, NoInputAxes, NoInputAxes2, NoInputButtons,
43};
44pub use binding::{
45    Axis2Binding, AxisBinding, ButtonAxis, ButtonAxis2, ButtonBinding, JoystickControl, Key,
46    MouseButton, Pad, PadAxis, PointerDelta, Stick, WheelDelta,
47};
48pub use cursor::Cursor;
49
50#[cfg(feature = "offscreen")]
51pub use state::Switch;
52
53pub(crate) use pad::Pads;
54pub(crate) use state::{Controls, Devices, WheelRate};
55
56use core::num::NonZeroU32;
57
58use crate::math::Vec2;
59use state::Ticks;
60use table::{Rebound, Table};
61
62/// The game thread's own input: every action's bindings, whether a rebind
63/// changed one since the last flush, the [`Controls`] every query reads
64/// from, and what the ticks read of them.
65pub(crate) struct Queries {
66    table: Table,
67    controls: Controls,
68    ticks: Ticks,
69    dirty: bool,
70}
71
72impl Queries {
73    /// The queries a run starts with: the game's actions at their declared
74    /// bindings, with whatever the store `kept` of an earlier run over
75    /// them, and controls reading nothing.
76    pub(crate) fn new<A: InputActions>(kept: Option<&str>) -> Self {
77        Self {
78            table: Table::new::<A>(kept),
79            controls: Controls::default(),
80            ticks: Ticks::default(),
81            dirty: false,
82        }
83    }
84
85    /// Takes `controls` as what every query reads from, and folds them
86    /// into what the ticks read.
87    pub(crate) fn take(&mut self, controls: Controls) {
88        self.ticks.fold(&controls);
89        self.controls = controls;
90    }
91
92    /// Runs `tick` `count` times, on the edges since the last frame whose
93    /// ticks ran.
94    ///
95    /// Each call reads the same edges; no later tick reads them again.
96    pub(crate) fn ticks(&mut self, count: NonZeroU32, mut tick: impl FnMut(&Ticking<'_>)) {
97        let ticking = Ticking { queries: self };
98        for _ in 0..count.get() {
99            tick(&ticking);
100        }
101    }
102
103    pub(crate) fn down<A: InputButtonAction>(&self, action: A) -> bool {
104        self.controls.frame().down(self.table.resolve(action))
105    }
106
107    pub(crate) fn pressed<A: InputButtonAction>(&self, action: A) -> bool {
108        self.controls.frame().pressed(self.table.resolve(action))
109    }
110
111    pub(crate) fn released<A: InputButtonAction>(&self, action: A) -> bool {
112        self.controls.frame().released(self.table.resolve(action))
113    }
114
115    pub(crate) fn clicks<A: InputButtonAction>(&self, action: A) -> u32 {
116        match self.pressed(action) {
117            true => self.controls.clicks(self.table.resolve(action)),
118            false => 0,
119        }
120    }
121
122    pub(crate) fn axis<A: InputAxisAction>(&self, action: A) -> f32 {
123        self.controls.frame().axis(self.table.resolve(action))
124    }
125
126    pub(crate) fn axis2<A: InputAxis2Action>(&self, action: A) -> Vec2 {
127        self.controls.frame().axis2(self.table.resolve(action))
128    }
129
130    pub(crate) fn pointer(&self) -> Vec2 {
131        self.controls.frame().pointer()
132    }
133
134    pub(crate) fn bindings<A: InputAction>(&self, action: A) -> Vec<A::Binding> {
135        self.table.resolve(action).to_vec()
136    }
137
138    /// Binds `action` to `bindings` and marks the store to be written where
139    /// that is not what the action already read through.
140    pub(crate) fn rebind<A: InputAction>(&mut self, action: A, bindings: Vec<A::Binding>) {
141        match self.table.rebind(action, bindings) {
142            Rebound::Changed => self.dirty = true,
143            Rebound::Unchanged => {}
144        }
145    }
146
147    /// The bindings as the store keeps them where a rebind changed one
148    /// since the last call, for the display thread to write, and nothing at
149    /// all where none did.
150    pub(crate) fn flush(&mut self) -> Option<String> {
151        core::mem::take(&mut self.dirty).then(|| self.table.written())
152    }
153
154    pub(crate) fn actuated_button(&self) -> Option<ButtonBinding> {
155        self.controls.frame().actuated_button()
156    }
157
158    pub(crate) fn actuated_axis(&self) -> Option<AxisBinding> {
159        self.controls.frame().actuated_axis()
160    }
161
162    pub(crate) fn actuated_axis2(&self) -> Option<Axis2Binding> {
163        self.controls.frame().actuated_axis2()
164    }
165
166    /// Whether `action` went down since the last frame whose ticks ran.
167    /// Only [`Ticking`] calls this.
168    fn pressed_over_ticks<A: InputButtonAction>(&self, action: A) -> bool {
169        self.ticks.snapshot().pressed(self.table.resolve(action))
170    }
171
172    /// Whether `action` came up since the last frame whose ticks ran. Only
173    /// [`Ticking`] calls this.
174    fn released_over_ticks<A: InputButtonAction>(&self, action: A) -> bool {
175        self.ticks.snapshot().released(self.table.resolve(action))
176    }
177
178    /// Presses in a row the press these ticks read is the last of. Only
179    /// [`Ticking`] calls this.
180    fn clicks_over_ticks<A: InputButtonAction>(&self, action: A) -> u32 {
181        match self.pressed_over_ticks(action) {
182            true => self.controls.clicks(self.table.resolve(action)),
183            false => 0,
184        }
185    }
186
187    /// Ends the ticks of one frame, so no later tick reads their edges.
188    /// Only dropping a [`Ticking`] calls this.
189    fn ticked(&mut self) {
190        self.ticks.ticked();
191    }
192}
193
194/// The input the ticks of one frame read. [`Queries::ticks`] is the only
195/// source of one.
196///
197/// Dropping it ends those ticks, so no later tick reads an edge these ticks
198/// already read.
199pub(crate) struct Ticking<'a> {
200    queries: &'a mut Queries,
201}
202
203impl Ticking<'_> {
204    pub(crate) fn down<A: InputButtonAction>(&self, action: A) -> bool {
205        self.queries.down(action)
206    }
207
208    pub(crate) fn pressed<A: InputButtonAction>(&self, action: A) -> bool {
209        self.queries.pressed_over_ticks(action)
210    }
211
212    pub(crate) fn released<A: InputButtonAction>(&self, action: A) -> bool {
213        self.queries.released_over_ticks(action)
214    }
215
216    pub(crate) fn clicks<A: InputButtonAction>(&self, action: A) -> u32 {
217        self.queries.clicks_over_ticks(action)
218    }
219
220    pub(crate) fn axis<A: InputAxisAction>(&self, action: A) -> f32 {
221        self.queries.axis(action)
222    }
223
224    pub(crate) fn axis2<A: InputAxis2Action>(&self, action: A) -> Vec2 {
225        self.queries.axis2(action)
226    }
227
228    pub(crate) fn pointer(&self) -> Vec2 {
229        self.queries.pointer()
230    }
231}
232
233impl Drop for Ticking<'_> {
234    fn drop(&mut self) {
235        self.queries.ticked();
236    }
237}
238
239mod action;
240mod binding;
241mod cursor;
242mod pad;
243mod state;
244mod table;
245
246#[cfg(test)]
247mod tests {
248    use core::time::Duration;
249
250    use super::*;
251    use winit::event::{DeviceId, ElementState, WindowEvent};
252
253    /// The interval these tests count a double click by.
254    const DOUBLE_CLICK: Duration = Duration::from_millis(400);
255
256    /// More than one tick in a frame, so a test can check what each tick
257    /// reads.
258    const TICKS: NonZeroU32 = match NonZeroU32::new(3) {
259        Some(ticks) => ticks,
260        None => NonZeroU32::MIN,
261    };
262
263    /// A run reading through the keyboard as its own vocabulary, keeping
264    /// nothing between runs: the devices of its display thread and the
265    /// queries of its game thread.
266    fn run() -> (Devices, Queries) {
267        (
268            Devices::new(Pads::silent(), DOUBLE_CLICK),
269            Queries::new::<Key>(None),
270        )
271    }
272
273    /// The primary mouse button pressed, which is the one control a test can
274    /// press: `winit` keeps a keyboard event's platform field private, so no
275    /// other crate builds one.
276    fn click() -> WindowEvent {
277        WindowEvent::MouseInput {
278            device_id: DeviceId::dummy(),
279            state: ElementState::Pressed,
280            button: winit::event::MouseButton::Left,
281        }
282    }
283
284    /// What each of the `count` ticks of one frame reads for `action`.
285    fn pressed_per_tick(queries: &mut Queries, count: NonZeroU32, action: Key) -> Vec<bool> {
286        let mut read = Vec::new();
287        queries.ticks(count, |ticking| read.push(ticking.pressed(action)));
288        read
289    }
290
291    #[test]
292    fn only_a_rebind_that_changed_something_leaves_the_store_to_write() {
293        let (_, mut queries) = run();
294
295        queries.rebind(Key::Space, vec![Key::Space.into()]);
296        assert!(!queries.dirty, "what it read through already is no change");
297
298        queries.rebind(Key::Space, vec![Key::Enter.into()]);
299        assert!(queries.dirty);
300        assert!(
301            queries.flush().is_some(),
302            "and the text to write is handed out"
303        );
304
305        assert!(
306            queries.flush().is_none(),
307            "so the store is written once, not per call"
308        );
309        assert_eq!(queries.bindings(Key::Space), vec![Key::Enter.into()]);
310    }
311
312    #[test]
313    fn the_queries_answer_from_the_controls_alone_and_count_clicks_across_them() {
314        let (mut devices, mut queries) = run();
315        queries.rebind(Key::Space, vec![MouseButton::Left.into()]);
316        devices.see(&click());
317        queries.take(devices.sample(Duration::ZERO));
318        let first = (queries.pressed(Key::Space), queries.clicks(Key::Space));
319
320        devices.press(MouseButton::Left.into(), false);
321        devices.sample(Duration::ZERO);
322        devices.press(MouseButton::Left.into(), true);
323        queries.take(devices.sample(Duration::from_millis(100)));
324
325        assert_eq!(
326            (queries.pressed(Key::Space), queries.clicks(Key::Space)),
327            (first.0, first.1 + 1),
328            "the same press read through the controls, one click further on"
329        );
330        assert!(
331            queries.pressed_over_ticks(Key::Space),
332            "and the ticks' edge crosses in the same controls"
333        );
334    }
335
336    #[test]
337    fn the_controls_are_plain_data_that_cross_between_threads() {
338        fn crosses<T: Send + Clone>() {}
339        crosses::<Controls>();
340    }
341
342    #[test]
343    fn the_ticks_of_one_frame_read_an_edge_and_the_ticks_after_them_read_none() {
344        let (mut devices, mut queries) = run();
345        queries.rebind(Key::Space, vec![MouseButton::Left.into()]);
346
347        devices.see(&click());
348        queries.take(devices.sample(Duration::ZERO));
349        queries.take(devices.sample(Duration::ZERO));
350
351        assert_eq!(
352            pressed_per_tick(&mut queries, TICKS, Key::Space),
353            [true; 3],
354            "every tick of the frame that reads the press reads it"
355        );
356
357        queries.take(devices.sample(Duration::ZERO));
358        assert_eq!(
359            pressed_per_tick(&mut queries, TICKS, Key::Space),
360            [false; 3],
361            "and the ticks that follow read it no more"
362        );
363    }
364}