Skip to main content

mirage_engine/input/
state.rs

1//! Every control's reading this frame, the frame before, and the last frame
2//! whose ticks ran. A binding resolves against a reading; an edge is
3//! measured between two of them.
4
5use core::time::Duration;
6
7use winit::event::{DeviceEvent, MouseScrollDelta, TouchPhase, WindowEvent};
8use winit::keyboard::PhysicalKey;
9
10use crate::input::binding::{
11    Axis2Binding, Axis2Source, AxisBinding, AxisSource, ButtonBinding, JoystickControl, Key,
12    MouseButton, Pad, PadAxis, PointerDelta, Stick, WheelDelta,
13};
14use crate::input::pad::Pads;
15use crate::math::Vec2;
16use crate::platform::WHEEL_RATE;
17
18/// Controls with no standard layout one reading keeps, over every device it
19/// read them from.
20const UNMAPPED: usize = 64;
21
22/// Level an unmapped control must read at to count as held.
23const HELD: f32 = 0.5;
24
25/// Distance a control moves before capture takes it for the one the player
26/// meant.
27const ACTUATED: f32 = AxisBinding::DEFAULT_DEADZONE;
28
29/// How far a window reports the wheel turning for one notch, in lines and
30/// in pixels. Each platform seam states the rate its own windows count by.
31#[derive(Clone, Copy, Debug, PartialEq)]
32pub(crate) struct WheelRate {
33    lines: f32,
34    pixels: f32,
35}
36
37impl WheelRate {
38    /// What a desktop window counts a notch as: one line, which every
39    /// desktop backend reports for one step of a wheel, and 100 pixels,
40    /// the rate a browser counts by, so a device that reports pixels reads
41    /// the same on both targets.
42    pub(crate) const DESKTOP: Self = Self::per_notch(1.0, 100.0);
43
44    /// One notch as a browser window reports it: three lines when the
45    /// browser counts in lines, and 100 pixels when it counts in pixels.
46    pub(crate) const BROWSER: Self = Self::per_notch(3.0, 100.0);
47
48    const fn per_notch(lines: f32, pixels: f32) -> Self {
49        Self { lines, pixels }
50    }
51
52    /// `delta` as the notches the wheel turned, counted right and away. A
53    /// window counts its sideways lane the other way round, and counts in
54    /// lines or in pixels by the device that turned it.
55    fn notches(self, delta: MouseScrollDelta) -> Vec2 {
56        let (sideways, up, rate) = match delta {
57            MouseScrollDelta::LineDelta(x, y) => (x, y, self.lines),
58            MouseScrollDelta::PixelDelta(pixels) => (pixels.x as f32, pixels.y as f32, self.pixels),
59        };
60        Vec2::new(-sideways, up) / rate
61    }
62
63    /// `turn` as the lines a window reports for it, counted the window's
64    /// own way: what the UI reads behind a window.
65    #[cfg(all(feature = "ui", feature = "offscreen"))]
66    pub(crate) fn lines(self, turn: Vec2) -> Vec2 {
67        Vec2::new(-turn.x, turn.y) * self.lines
68    }
69}
70
71/// The controls as one [`Devices::sample`] closed them: plain data that
72/// crosses from the devices to the game, which reads one frame's queries
73/// from it and folds it into what its ticks read.
74#[derive(Clone, Copy, Default)]
75pub(crate) struct Controls {
76    frame: Snapshot,
77    /// The keys and mouse buttons that went down since the last sample, so
78    /// that a tap made while no ticks ran is still held for the ticks that
79    /// follow.
80    tapped: Pressed,
81    clicking: Clicking,
82}
83
84impl Controls {
85    /// This frame's reading against the frame before.
86    pub(crate) fn frame(&self) -> &Snapshot {
87        &self.frame
88    }
89
90    /// How many presses in a row the last press made: `1` for a press on
91    /// its own, and `0` where the count is on no control of `bindings`.
92    pub(crate) fn clicks(&self, bindings: &[ButtonBinding]) -> u32 {
93        self.clicking.clicks(bindings)
94    }
95}
96
97/// What the ticks read, measured on the game thread alone: this frame's
98/// reading against the last frame whose ticks ran, and the taps since.
99#[derive(Default)]
100pub(crate) struct Ticks {
101    snapshot: Snapshot,
102    tapped: Pressed,
103}
104
105impl Ticks {
106    /// Folds `controls` in: the ticks read its frame, with every tap since
107    /// they last ran held down in it.
108    pub(crate) fn fold(&mut self, controls: &Controls) {
109        self.tapped.merge(controls.tapped);
110        self.snapshot.now = controls.frame.now.holding(self.tapped);
111    }
112
113    /// This frame's reading against the last frame whose ticks ran.
114    pub(crate) fn snapshot(&self) -> &Snapshot {
115        &self.snapshot
116    }
117
118    /// Ends the ticks of one frame: the edges they read are spent, so no
119    /// later tick reads them, and the taps they held are cleared.
120    pub(crate) fn ticked(&mut self) {
121        self.snapshot.before = self.snapshot.now;
122        self.tapped = Pressed::default();
123    }
124}
125
126/// Two readings and the edge between them: this frame against the frame
127/// before, or this frame against the last frame whose ticks ran.
128#[derive(Clone, Copy, Default)]
129pub(crate) struct Snapshot {
130    now: Reading,
131    before: Reading,
132}
133
134impl Snapshot {
135    pub(crate) fn down(&self, bindings: &[ButtonBinding]) -> bool {
136        self.now.any(bindings)
137    }
138
139    pub(crate) fn pressed(&self, bindings: &[ButtonBinding]) -> bool {
140        self.now.any(bindings) && !self.before.any(bindings)
141    }
142
143    pub(crate) fn released(&self, bindings: &[ButtonBinding]) -> bool {
144        !self.now.any(bindings) && self.before.any(bindings)
145    }
146
147    /// Reading of the binding pushed furthest, so that alternatives never
148    /// add up.
149    pub(crate) fn axis(&self, bindings: &[AxisBinding]) -> f32 {
150        bindings.iter().fold(0.0, |most, binding| {
151            let value = self.now.axis(binding);
152            match value.abs() > most.abs() {
153                true => value,
154                false => most,
155            }
156        })
157    }
158
159    pub(crate) fn axis2(&self, bindings: &[Axis2Binding]) -> Vec2 {
160        bindings.iter().fold(Vec2::ZERO, |most, binding| {
161            let value = self.now.axis2(binding);
162            match value.length_squared() > most.length_squared() {
163                true => value,
164                false => most,
165            }
166        })
167    }
168
169    pub(crate) fn pointer(&self) -> Vec2 {
170        self.now.pointer
171    }
172
173    /// The button control the player pressed this frame, out of every device
174    /// there is.
175    pub(crate) fn actuated_button(&self) -> Option<ButtonBinding> {
176        let keys = Key::ALL.iter().copied().map(ButtonBinding::Key);
177        let mouse = MouseButton::ALL.iter().copied().map(ButtonBinding::Mouse);
178        let pad = Pad::ALL.iter().copied().map(ButtonBinding::Pad);
179        let joystick = self
180            .now
181            .joystick
182            .iter()
183            .map(|(control, _)| ButtonBinding::Joystick(control));
184
185        keys.chain(mouse)
186            .chain(pad)
187            .chain(joystick)
188            .find(|&binding| self.now.held(binding) && !self.before.held(binding))
189    }
190
191    /// The analog control the player pushed this frame, at the knobs a
192    /// binding starts with; a pointer or wheel is never captured, because the
193    /// smallest nudge would take it.
194    pub(crate) fn actuated_axis(&self) -> Option<AxisBinding> {
195        let pad = PadAxis::ALL.iter().copied().map(AxisBinding::pad);
196        let joystick = self
197            .now
198            .joystick_axes
199            .iter()
200            .map(|(control, _)| AxisBinding::joystick(control));
201
202        pad.chain(joystick).find(|binding| {
203            self.now.axis(binding).abs() > ACTUATED && self.before.axis(binding).abs() <= ACTUATED
204        })
205    }
206
207    /// The stick the player pushed this frame, at the knobs a binding starts
208    /// with; the pointer and the wheel are never captured, as for an axis.
209    pub(crate) fn actuated_axis2(&self) -> Option<Axis2Binding> {
210        Stick::ALL
211            .iter()
212            .copied()
213            .map(Axis2Binding::stick)
214            .find(|binding| {
215                self.now.axis2(binding).length() > ACTUATED
216                    && self.before.axis2(binding).length() <= ACTUATED
217            })
218    }
219}
220
221/// The presses one control has taken in a row: which control, when the
222/// last of them landed on the run's own clock, and how many landed each
223/// within one double click interval of the one before it.
224#[derive(Clone, Copy, Default)]
225struct Clicking {
226    control: Option<ButtonBinding>,
227    at: Duration,
228    count: u32,
229}
230
231impl Clicking {
232    /// Counts `pressed` at `at`: the same control again within `within` of
233    /// the last press counts on from it, and anything else starts the count
234    /// again.
235    fn press(&mut self, pressed: Option<ButtonBinding>, at: Duration, within: Duration) {
236        let Some(control) = pressed else {
237            return;
238        };
239        let again = self.control == Some(control) && at.saturating_sub(self.at) <= within;
240
241        self.count = match again {
242            true => self.count + 1,
243            false => 1,
244        };
245        self.control = Some(control);
246        self.at = at;
247    }
248
249    /// The presses in a row, where the last of them was one of `bindings`,
250    /// and `0` where the count is on another control.
251    fn clicks(&self, bindings: &[ButtonBinding]) -> u32 {
252        match self
253            .control
254            .is_some_and(|control| bindings.contains(&control))
255        {
256            true => self.count,
257            false => 0,
258        }
259    }
260}
261
262/// Every control's reading at one instant.
263#[derive(Clone, Copy)]
264pub(crate) struct Reading {
265    pressed: Pressed,
266    pad: [bool; Pad::COUNT],
267    pad_axes: [f32; PadAxis::COUNT],
268    joystick: Unmapped,
269    joystick_axes: Unmapped,
270    pointer: Vec2,
271    /// Distance the pointer moved since the last snapshot, counted right
272    /// and up.
273    pointer_delta: Vec2,
274    /// Notches the wheel turned since the last snapshot, counted right and
275    /// away.
276    wheel: Vec2,
277}
278
279impl Reading {
280    /// This reading with `tapped` held down in it, so that a control
281    /// pressed and released between two readings is down in one of them.
282    fn holding(mut self, tapped: Pressed) -> Self {
283        self.pressed.merge(tapped);
284        self
285    }
286
287    /// Presses a button of the standard gamepad layout; every device feeds
288    /// one player, so any of them holding it is enough.
289    pub(crate) fn press_pad(&mut self, button: Pad) {
290        self.pad[button.index()] = true;
291    }
292
293    /// Pushes a lane of the standard gamepad layout; the device pushing it
294    /// furthest is the one that counts.
295    pub(crate) fn push_pad(&mut self, axis: PadAxis, value: f32) {
296        let lane = &mut self.pad_axes[axis.index()];
297        if value.abs() > lane.abs() {
298            *lane = value.clamp(-1.0, 1.0);
299        }
300    }
301
302    /// Presses a button of a device with no standard layout, by the number
303    /// its platform reports for it.
304    pub(crate) fn press_joystick(&mut self, control: JoystickControl) {
305        self.joystick.push(control, 1.0);
306    }
307
308    /// Pushes an axis of a device with no standard layout.
309    pub(crate) fn push_joystick(&mut self, control: JoystickControl, value: f32) {
310        self.joystick_axes.push(control, value.clamp(-1.0, 1.0));
311    }
312
313    /// Drops what the pads read last frame, which are polled rather than
314    /// evented and so are read whole each time.
315    fn forget_pads(&mut self) {
316        self.pad = [false; Pad::COUNT];
317        self.pad_axes = [0.0; PadAxis::COUNT];
318        self.joystick = Unmapped::default();
319        self.joystick_axes = Unmapped::default();
320    }
321
322    fn any(&self, bindings: &[ButtonBinding]) -> bool {
323        bindings.iter().any(|&binding| self.held(binding))
324    }
325
326    fn held(&self, binding: ButtonBinding) -> bool {
327        match binding {
328            ButtonBinding::Key(key) => self.pressed.keys[key.index()],
329            ButtonBinding::Mouse(button) => self.pressed.mouse[button.index()],
330            ButtonBinding::Pad(button) => self.pad[button.index()],
331            ButtonBinding::Joystick(control) => self.joystick.value(control) > HELD,
332        }
333    }
334
335    fn axis(&self, binding: &AxisBinding) -> f32 {
336        let raw = match binding.source {
337            AxisSource::Pad(axis) => self.pad_axes[axis.index()],
338            AxisSource::Joystick(control) => self.joystick_axes.value(control),
339            AxisSource::Pointer(lane) => match lane {
340                PointerDelta::Sideways => self.pointer_delta.x,
341                PointerDelta::Up => self.pointer_delta.y,
342            },
343            AxisSource::Wheel(lane) => match lane {
344                WheelDelta::Sideways => self.wheel.x,
345                WheelDelta::Up => self.wheel.y,
346            },
347            AxisSource::Buttons { negative, positive } => {
348                weigh(self.held(positive)) - weigh(self.held(negative))
349            }
350        };
351        binding.resolve(raw)
352    }
353
354    fn axis2(&self, binding: &Axis2Binding) -> Vec2 {
355        let raw = match binding.source {
356            Axis2Source::Stick(stick) => {
357                let (x, y) = stick.lanes();
358                Vec2::new(self.pad_axes[x.index()], self.pad_axes[y.index()])
359            }
360            Axis2Source::Pointer => self.pointer_delta,
361            Axis2Source::Wheel => self.wheel,
362            Axis2Source::Buttons {
363                left,
364                right,
365                down,
366                up,
367            } => Vec2::new(
368                weigh(self.held(right)) - weigh(self.held(left)),
369                weigh(self.held(up)) - weigh(self.held(down)),
370            ),
371        };
372        binding.resolve(raw)
373    }
374}
375
376impl Default for Reading {
377    fn default() -> Self {
378        Self {
379            pressed: Pressed::default(),
380            pad: [false; Pad::COUNT],
381            pad_axes: [0.0; PadAxis::COUNT],
382            joystick: Unmapped::default(),
383            joystick_axes: Unmapped::default(),
384            pointer: Vec2::ZERO,
385            pointer_delta: Vec2::ZERO,
386            wheel: Vec2::ZERO,
387        }
388    }
389}
390
391/// The keys and mouse buttons the window's events report as down.
392#[derive(Clone, Copy)]
393struct Pressed {
394    keys: [bool; Key::COUNT],
395    mouse: [bool; MouseButton::COUNT],
396}
397
398impl Pressed {
399    #[cfg(all(feature = "ui", feature = "offscreen"))]
400    fn held(&self, control: Switch) -> bool {
401        match control {
402            Switch::Key(key) => self.keys[key.index()],
403            Switch::Mouse(button) => self.mouse[button.index()],
404        }
405    }
406
407    fn at(&mut self, control: Switch) -> &mut bool {
408        match control {
409            Switch::Key(key) => &mut self.keys[key.index()],
410            Switch::Mouse(button) => &mut self.mouse[button.index()],
411        }
412    }
413
414    fn merge(&mut self, other: Self) {
415        for (held, tapped) in self.keys.iter_mut().zip(other.keys) {
416            *held |= tapped;
417        }
418        for (held, tapped) in self.mouse.iter_mut().zip(other.mouse) {
419            *held |= tapped;
420        }
421    }
422}
423
424impl Default for Pressed {
425    fn default() -> Self {
426        Self {
427            keys: [false; Key::COUNT],
428            mouse: [false; MouseButton::COUNT],
429        }
430    }
431}
432
433/// A control the window sends events for, rather than one that is polled.
434#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
435pub enum Switch {
436    /// A key, by physical position.
437    Key(Key),
438    /// A mouse button, which the first touch of a touch screen also presses.
439    Mouse(MouseButton),
440}
441
442impl From<Key> for Switch {
443    fn from(key: Key) -> Self {
444        Self::Key(key)
445    }
446}
447
448impl From<MouseButton> for Switch {
449    fn from(button: MouseButton) -> Self {
450        Self::Mouse(button)
451    }
452}
453
454/// The controls of devices with no standard layout, by the number their
455/// platform reports for them, kept in that order so that capture is repeatable.
456#[derive(Clone, Copy)]
457struct Unmapped {
458    controls: [(JoystickControl, f32); UNMAPPED],
459    len: usize,
460}
461
462impl Unmapped {
463    /// Keeps the strongest reading for `control`, so that many devices
464    /// merge into the one player they all feed.
465    fn push(&mut self, control: JoystickControl, value: f32) {
466        match self.controls[..self.len].binary_search_by_key(&control, |&(id, _)| id) {
467            Ok(at) if value.abs() > self.controls[at].1.abs() => self.controls[at].1 = value,
468            Ok(_) => {}
469            Err(at) if self.len < UNMAPPED => {
470                self.controls[at..=self.len].rotate_right(1);
471                self.controls[at] = (control, value);
472                self.len += 1;
473            }
474            Err(_) => {}
475        }
476    }
477
478    fn value(&self, control: JoystickControl) -> f32 {
479        match self.controls[..self.len].binary_search_by_key(&control, |&(id, _)| id) {
480            Ok(at) => self.controls[at].1,
481            Err(_) => 0.0,
482        }
483    }
484
485    fn iter(&self) -> impl Iterator<Item = (JoystickControl, f32)> + '_ {
486        self.controls[..self.len].iter().copied()
487    }
488}
489
490impl Default for Unmapped {
491    fn default() -> Self {
492        Self {
493            controls: [(JoystickControl::new(0), 0.0); UNMAPPED],
494            len: 0,
495        }
496    }
497}
498
499/// Turns the window's events and the pads' readings into one [`Controls`]
500/// per drawn frame.
501pub(crate) struct Devices {
502    pads: Pads,
503    /// How long after a press a second one still counts as a double click.
504    double_click: Duration,
505    live: Reading,
506    /// The keys and mouse buttons that went down since the last sample, so
507    /// that a tap between two frames is still held for one of them.
508    tapped: Pressed,
509    /// Position the pointer was last seen at, which is what its movement
510    /// is measured from.
511    tracked: Option<Vec2>,
512    /// Whether the window holds the pointer in place, which leaves the
513    /// device's own movement the one thing that moves it.
514    held: bool,
515    /// The touch used as the pointer, out of however many the screen
516    /// reports.
517    touch: Option<u64>,
518    /// The reading the last sample closed with, which the next one measures
519    /// its edges from.
520    before: Reading,
521    clicking: Clicking,
522}
523
524impl Devices {
525    /// Devices reading nothing yet, with `pads` polled at every sample and a
526    /// press within `double_click` of the last one counted as one more click.
527    pub(crate) fn new(pads: Pads, double_click: Duration) -> Self {
528        Self {
529            pads,
530            double_click,
531            live: Reading::default(),
532            tapped: Pressed::default(),
533            tracked: None,
534            held: false,
535            touch: None,
536            before: Reading::default(),
537            clicking: Clicking::default(),
538        }
539    }
540
541    /// Takes what `event` reports about the controls; the engine never keeps
542    /// it from the game because the UI claimed it.
543    pub(crate) fn see(&mut self, event: &WindowEvent) {
544        match event {
545            WindowEvent::KeyboardInput { event, .. } => {
546                let PhysicalKey::Code(code) = event.physical_key else {
547                    return;
548                };
549                let Some(key) = Key::from_code(code) else {
550                    return;
551                };
552                self.press(Switch::Key(key), event.state.is_pressed());
553            }
554            WindowEvent::MouseInput { button, state, .. } => {
555                let Some(button) = MouseButton::from_winit(*button) else {
556                    return;
557                };
558                self.press(Switch::Mouse(button), state.is_pressed());
559            }
560            WindowEvent::CursorMoved { position, .. } => {
561                self.point_at(Vec2::new(position.x as f32, position.y as f32));
562            }
563            WindowEvent::MouseWheel { delta, .. } => {
564                self.live.wheel += WHEEL_RATE.notches(*delta);
565            }
566            WindowEvent::Touch(touch) => self.touch(touch),
567            WindowEvent::Focused(false) => {
568                self.live.pressed = Pressed::default();
569                self.touch = None;
570                self.hold_pointer(false);
571            }
572            _ => {}
573        }
574    }
575
576    /// Takes the movement `event` reports of a device itself, which is the
577    /// one thing a held pointer moves by; the window reports no place for
578    /// one.
579    pub(crate) fn see_device(&mut self, event: &DeviceEvent) {
580        let DeviceEvent::MouseMotion { delta: (x, y) } = event else {
581            return;
582        };
583        if self.held {
584            self.move_pointer(Vec2::new(*x as f32, -(*y as f32)));
585        }
586    }
587
588    /// Holds the pointer in place, so that it moves by what the device
589    /// reports alone, or releases it.
590    ///
591    /// A released pointer measures its next movement from wherever the
592    /// window reports it next, however far the hold left it from there. A
593    /// window loses its hold as it loses focus.
594    pub(crate) fn hold_pointer(&mut self, held: bool) {
595        if core::mem::replace(&mut self.held, held) != held && !held {
596            self.tracked = None;
597        }
598    }
599
600    /// Ends the events of one frame and starts the next: reads the pads,
601    /// closes this frame's controls at `at` on its driver's clock, counts a
602    /// press landing within the double click interval of the last one as
603    /// one more click — the first press edge in the order keys, mouse, pad,
604    /// joystick where a frame holds more than one — and clears what only
605    /// lasts a frame.
606    pub(crate) fn sample(&mut self, at: Duration) -> Controls {
607        self.live.forget_pads();
608        self.pads.poll(&mut self.live);
609
610        let tapped = core::mem::take(&mut self.tapped);
611        let now = self.live.holding(tapped);
612        let frame = Snapshot {
613            before: core::mem::replace(&mut self.before, now),
614            now,
615        };
616        self.clicking
617            .press(frame.actuated_button(), at, self.double_click);
618
619        self.live.pointer_delta = Vec2::ZERO;
620        self.live.wheel = Vec2::ZERO;
621        Controls {
622            frame,
623            tapped,
624            clicking: self.clicking,
625        }
626    }
627
628    pub(crate) fn press(&mut self, control: Switch, down: bool) {
629        *self.live.pressed.at(control) = down;
630        if down {
631            *self.tapped.at(control) = true;
632        }
633    }
634
635    /// Moves the pointer by `pixels`, counted right and up, and places it
636    /// nowhere: what a held pointer and a session both move by.
637    pub(crate) fn move_pointer(&mut self, pixels: Vec2) {
638        self.live.pointer_delta += pixels;
639    }
640
641    /// Turns the wheel by `notches`, counted right and away, in place of a
642    /// window's event; only a session calls this.
643    #[cfg(feature = "offscreen")]
644    pub(crate) fn turn_wheel(&mut self, notches: Vec2) {
645        self.live.wheel += notches;
646    }
647
648    /// Whether `control` is held as of now, before any snapshot closes;
649    /// only a session reads this, for the keys it passes to the UI.
650    #[cfg(all(feature = "ui", feature = "offscreen"))]
651    pub(crate) fn holds(&self, control: Switch) -> bool {
652        self.live.pressed.held(control)
653    }
654
655    /// The `Shift`, `Control` and `Alt` keys held as of now, before any
656    /// snapshot closes, as the UI reads them, with `Control` as its command
657    /// key.
658    ///
659    /// A window on `macOS` takes its command key from `Super`, which [`Key`]
660    /// has no position for, so a session's `Control` is what a command reads
661    /// there.
662    #[cfg(all(feature = "ui", feature = "offscreen"))]
663    pub(crate) fn modifiers(&self) -> egui::Modifiers {
664        let either = |left, right| self.holds(Switch::Key(left)) || self.holds(Switch::Key(right));
665        let ctrl = either(Key::LeftControl, Key::RightControl);
666
667        egui::Modifiers {
668            alt: either(Key::LeftAlt, Key::RightAlt),
669            ctrl,
670            shift: either(Key::LeftShift, Key::RightShift),
671            mac_cmd: false,
672            command: ctrl,
673        }
674    }
675
676    /// Where the pointer has been placed, which is where a session's next
677    /// press lands, or `None` where nothing has placed it yet; only a
678    /// session reads this.
679    #[cfg(all(feature = "ui", feature = "offscreen"))]
680    pub(crate) fn pointing_at(&self) -> Option<Vec2> {
681        self.tracked
682    }
683
684    /// Places the pointer at `position` and counts how far it moved to get
685    /// there; a held pointer stays where it is instead.
686    pub(crate) fn point_at(&mut self, position: Vec2) {
687        if self.held {
688            return;
689        }
690        if let Some(from) = self.tracked {
691            self.live.pointer_delta += Vec2::new(position.x - from.x, from.y - position.y);
692        }
693        self.tracked = Some(position);
694        self.live.pointer = position;
695    }
696
697    /// The first touch is the pointer: where it lands and the primary mouse
698    /// button, so a tap is a click wherever one would be.
699    fn touch(&mut self, touch: &winit::event::Touch) {
700        let at = Vec2::new(touch.location.x as f32, touch.location.y as f32);
701        let primary = Switch::Mouse(MouseButton::Left);
702
703        match touch.phase {
704            TouchPhase::Started if self.touch.is_none() => {
705                self.touch = Some(touch.id);
706                self.tracked = None;
707                self.point_at(at);
708                self.press(primary, true);
709            }
710            TouchPhase::Moved if self.touch == Some(touch.id) => self.point_at(at),
711            TouchPhase::Ended | TouchPhase::Cancelled if self.touch == Some(touch.id) => {
712                self.touch = None;
713                self.press(primary, false);
714            }
715            _ => {}
716        }
717    }
718}
719
720fn weigh(held: bool) -> f32 {
721    match held {
722        true => 1.0,
723        false => 0.0,
724    }
725}
726
727#[cfg(test)]
728mod tests {
729    use super::*;
730    use winit::dpi::PhysicalPosition;
731    use winit::event::{DeviceId, ElementState, Touch};
732
733    use crate::input::binding::{ButtonAxis, ButtonAxis2};
734    use crate::platform::Platform;
735
736    /// The interval these tests count a second press by.
737    const WITHIN: Duration = Duration::from_millis(400);
738
739    const JUMP: [ButtonBinding; 2] = [
740        ButtonBinding::Key(Key::Space),
741        ButtonBinding::Pad(Pad::South),
742    ];
743
744    /// A reading holding whatever a test placed in it.
745    fn reading(fill: impl FnOnce(&mut Reading)) -> Reading {
746        let mut reading = Reading::default();
747        fill(&mut reading);
748        reading
749    }
750
751    fn snapshot(before: Reading, now: Reading) -> Snapshot {
752        Snapshot { now, before }
753    }
754
755    // winit 0.30 keeps `KeyEvent`'s platform field private, so no crate can
756    // build one; the keyboard is covered through its mapping and its state.
757    fn click(state: ElementState) -> WindowEvent {
758        WindowEvent::MouseInput {
759            device_id: DeviceId::dummy(),
760            state,
761            button: winit::event::MouseButton::Left,
762        }
763    }
764
765    fn cursor_at(x: f64, y: f64) -> WindowEvent {
766        WindowEvent::CursorMoved {
767            device_id: DeviceId::dummy(),
768            position: PhysicalPosition::new(x, y),
769        }
770    }
771
772    fn wheel(delta: MouseScrollDelta) -> WindowEvent {
773        WindowEvent::MouseWheel {
774            device_id: DeviceId::dummy(),
775            delta,
776            phase: TouchPhase::Moved,
777        }
778    }
779
780    /// What a window reports for a wheel turned `lines` away from the
781    /// player: winit counts a turn away up.
782    fn rolled_away(lines: f32) -> MouseScrollDelta {
783        MouseScrollDelta::LineDelta(0.0, lines)
784    }
785
786    /// What a window reports for a wheel tilted `lines` to the right:
787    /// winit counts a tilt to the right down, the way X11 numbers its two
788    /// buttons for it.
789    fn tilted_right(lines: f32) -> MouseScrollDelta {
790        MouseScrollDelta::LineDelta(-lines, 0.0)
791    }
792
793    /// What a window reports for a device that counts in pixels, turned
794    /// `pixels` away from the player.
795    fn scrolled_away(pixels: f64) -> MouseScrollDelta {
796        MouseScrollDelta::PixelDelta(PhysicalPosition::new(0.0, pixels))
797    }
798
799    /// A device's own report that it moved, counted right and down as a
800    /// window counts a position.
801    fn motion(x: f64, y: f64) -> DeviceEvent {
802        DeviceEvent::MouseMotion { delta: (x, y) }
803    }
804
805    fn touch_at(phase: TouchPhase, id: u64, x: f64, y: f64) -> WindowEvent {
806        WindowEvent::Touch(Touch {
807            device_id: DeviceId::dummy(),
808            phase,
809            location: PhysicalPosition::new(x, y),
810            force: None,
811            id,
812        })
813    }
814
815    /// One frame of `events`, over the devices `before` them, as the
816    /// controls it closes with.
817    fn seen(devices: &mut Devices, events: &[WindowEvent]) -> Controls {
818        for event in events {
819            devices.see(event);
820        }
821        devices.sample(Duration::ZERO)
822    }
823
824    /// Presses `control` and releases it over two frames, the first of them
825    /// at `at`, and returns the presses in a row that press was the last of,
826    /// read through `control` itself.
827    fn clicked(devices: &mut Devices, control: Switch, at: Duration) -> u32 {
828        devices.press(control, true);
829        let clicks = devices.sample(at).clicks(&[bound(control)]);
830
831        devices.press(control, false);
832        devices.sample(at);
833        clicks
834    }
835
836    /// `control` as the binding an action reads it through.
837    fn bound(control: Switch) -> ButtonBinding {
838        match control {
839            Switch::Key(key) => ButtonBinding::Key(key),
840            Switch::Mouse(button) => ButtonBinding::Mouse(button),
841        }
842    }
843
844    #[test]
845    fn presses_of_one_control_within_the_interval_count_up_and_start_again_past_it() {
846        let mut devices = Devices::new(Pads::silent(), WITHIN);
847        let left = Switch::Mouse(MouseButton::Left);
848
849        assert_eq!(clicked(&mut devices, left, Duration::ZERO), 1, "one press");
850        assert_eq!(
851            clicked(&mut devices, left, WITHIN),
852            2,
853            "a second inside the interval is a double click"
854        );
855        assert_eq!(
856            clicked(&mut devices, left, WITHIN * 2 + Duration::from_millis(1)),
857            1,
858            "and one a millisecond past it starts a run of its own"
859        );
860    }
861
862    #[test]
863    fn a_control_pressed_beside_another_leaves_each_action_the_count_of_its_own() {
864        let mut devices = Devices::new(Pads::silent(), WITHIN);
865        let left = Switch::Mouse(MouseButton::Left);
866        let space = Switch::Key(Key::Space);
867        let selecting = [bound(left)];
868        let jumping = [bound(space)];
869
870        clicked(&mut devices, left, Duration::ZERO);
871        assert_eq!(
872            clicked(&mut devices, left, Duration::from_millis(100)),
873            2,
874            "the button has a count of two"
875        );
876
877        devices.press(left, true);
878        devices.press(space, true);
879        let sample = devices.sample(Duration::from_millis(200));
880
881        assert_eq!(sample.clicks(&jumping), 1, "the key counts its own press");
882        assert_eq!(
883            sample.clicks(&selecting),
884            0,
885            "and the button reads none of the key's count"
886        );
887
888        devices.press(left, false);
889        devices.press(space, false);
890        devices.sample(Duration::from_millis(250));
891        devices.press(left, true);
892        let sample = devices.sample(Duration::from_millis(300));
893
894        assert_eq!(
895            sample.clicks(&selecting),
896            1,
897            "the button counts its own press again"
898        );
899        assert_eq!(
900            sample.clicks(&jumping),
901            0,
902            "and the key reads none of the button's count"
903        );
904    }
905
906    #[test]
907    fn a_press_of_another_control_starts_the_count_again() {
908        let mut devices = Devices::new(Pads::silent(), WITHIN);
909        let left = Switch::Mouse(MouseButton::Left);
910        let right = Switch::Mouse(MouseButton::Right);
911
912        assert_eq!(clicked(&mut devices, left, Duration::ZERO), 1);
913        assert_eq!(clicked(&mut devices, left, Duration::from_millis(100)), 2);
914        assert_eq!(
915            clicked(&mut devices, right, Duration::from_millis(200)),
916            1,
917            "another control counts as one press, however soon it is pressed"
918        );
919        assert_eq!(
920            clicked(&mut devices, left, Duration::from_millis(300)),
921            1,
922            "and the one before it starts over too"
923        );
924    }
925
926    #[test]
927    fn an_edge_belongs_to_the_frame_the_control_changed_in() {
928        let mut devices = Devices::new(Pads::silent(), WITHIN);
929
930        let held = [ButtonBinding::Mouse(MouseButton::Left)];
931        let pressed = seen(&mut devices, &[click(ElementState::Pressed)]);
932        assert!(pressed.frame().down(&held) && pressed.frame().pressed(&held));
933
934        let still = seen(&mut devices, &[]);
935        assert!(still.frame().down(&held), "still held across frames");
936        assert!(!still.frame().pressed(&held), "the edge is spent");
937
938        let released = seen(&mut devices, &[click(ElementState::Released)]);
939        assert!(!released.frame().down(&held) && released.frame().released(&held));
940
941        assert!(!seen(&mut devices, &[]).frame().released(&held));
942    }
943
944    #[test]
945    fn a_frame_that_runs_no_ticks_keeps_its_edges_for_the_ticks_after_it() {
946        let mut devices = Devices::new(Pads::silent(), WITHIN);
947        let mut ticks = Ticks::default();
948        let held = [ButtonBinding::Mouse(MouseButton::Left)];
949
950        ticks.fold(&seen(&mut devices, &[click(ElementState::Pressed)]));
951        let over = seen(&mut devices, &[]);
952        ticks.fold(&over);
953        assert!(
954            !over.frame().pressed(&held),
955            "the frame that saw the press is over"
956        );
957        assert!(
958            ticks.snapshot().pressed(&held),
959            "and no tick has read it yet"
960        );
961
962        ticks.ticked();
963        assert!(
964            !ticks.snapshot().pressed(&held),
965            "the ticks that read it are the only ones that do"
966        );
967
968        ticks.fold(&seen(&mut devices, &[click(ElementState::Released)]));
969        ticks.fold(&seen(&mut devices, &[]));
970        assert!(
971            ticks.snapshot().released(&held),
972            "and coming up waits for the ticks the same way"
973        );
974
975        ticks.ticked();
976        ticks.fold(&seen(&mut devices, &[]));
977        assert!(!ticks.snapshot().released(&held));
978    }
979
980    #[test]
981    fn a_tap_made_while_no_ticks_ran_is_still_seen_by_the_ticks_after_it() {
982        let mut devices = Devices::new(Pads::silent(), WITHIN);
983        let mut ticks = Ticks::default();
984        let held = [ButtonBinding::Mouse(MouseButton::Left)];
985
986        ticks.fold(&seen(
987            &mut devices,
988            &[click(ElementState::Pressed), click(ElementState::Released)],
989        ));
990        ticks.fold(&seen(&mut devices, &[]));
991        assert!(ticks.snapshot().pressed(&held), "the press is not lost");
992
993        ticks.ticked();
994        ticks.fold(&seen(&mut devices, &[]));
995        assert!(ticks.snapshot().released(&held), "and comes back up");
996    }
997
998    #[test]
999    fn a_tap_between_two_frames_is_still_seen_by_one_of_them() {
1000        let mut devices = Devices::new(Pads::silent(), WITHIN);
1001
1002        let held = [ButtonBinding::Mouse(MouseButton::Left)];
1003        let tapped = seen(
1004            &mut devices,
1005            &[click(ElementState::Pressed), click(ElementState::Released)],
1006        );
1007        assert!(tapped.frame().pressed(&held), "the press is not lost");
1008
1009        assert!(
1010            seen(&mut devices, &[]).frame().released(&held),
1011            "and comes back up"
1012        );
1013    }
1014
1015    #[test]
1016    fn one_action_over_two_controls_takes_one_edge_at_a_time() {
1017        let held = snapshot(
1018            reading(|reading| reading.press_pad(Pad::South)),
1019            reading(|reading| {
1020                reading.press_pad(Pad::South);
1021                reading.pressed.keys[Key::Space.index()] = true;
1022            }),
1023        );
1024
1025        assert!(held.down(&JUMP));
1026        assert!(
1027            !held.pressed(&JUMP),
1028            "the second control joins an action already down"
1029        );
1030    }
1031
1032    #[test]
1033    fn the_pointer_reports_where_it_is_and_how_far_it_moved() {
1034        let mut devices = Devices::new(Pads::silent(), WITHIN);
1035
1036        let placed = seen(&mut devices, &[cursor_at(10.0, 20.0)]);
1037        assert_eq!(placed.frame().pointer(), Vec2::new(10.0, 20.0));
1038
1039        let sideways = [AxisBinding::pointer_delta(PointerDelta::Sideways)];
1040        let upward = [AxisBinding::pointer_delta(PointerDelta::Up)];
1041        let moved = seen(&mut devices, &[cursor_at(14.0, 18.0)]);
1042        assert_eq!(moved.frame().axis(&sideways), 4.0);
1043        assert!(moved.frame().axis(&upward) > 0.0, "up the screen counts up");
1044
1045        let still = seen(&mut devices, &[]);
1046        assert_eq!(still.frame().pointer(), Vec2::new(14.0, 18.0));
1047        assert_eq!(still.frame().axis(&sideways), 0.0, "movement is spent");
1048    }
1049
1050    #[test]
1051    fn a_pointer_lane_reads_the_distance_it_moved_while_a_pad_lane_stops_at_its_own_end() {
1052        let mut devices = Devices::new(Pads::silent(), WITHIN);
1053        let look = [AxisBinding::pointer_delta(PointerDelta::Sideways).scale(0.01)];
1054
1055        seen(&mut devices, &[cursor_at(0.0, 0.0)]);
1056        let moved = seen(&mut devices, &[cursor_at(500.0, 0.0)]);
1057        assert_eq!(
1058            moved.frame().axis(&look),
1059            5.0,
1060            "five hundred pixels at a hundredth each"
1061        );
1062
1063        let pushed = snapshot(
1064            Reading::default(),
1065            reading(|reading| reading.push_pad(PadAxis::LeftX, 1.0)),
1066        );
1067        let lane = [AxisBinding::pad(PadAxis::LeftX).scale(4.0)];
1068        assert_eq!(pushed.axis(&lane), 1.0, "and a pad lane reads one at most");
1069    }
1070
1071    #[test]
1072    fn an_action_bound_to_a_stick_and_the_pointer_reads_the_one_pushed_furthest() {
1073        let bindings = [
1074            Axis2Binding::stick(Stick::Left).deadzone(0.0),
1075            Axis2Binding::pointer().scale(0.01),
1076        ];
1077        let nudged = snapshot(
1078            Reading::default(),
1079            reading(|reading| {
1080                reading.push_pad(PadAxis::LeftX, 0.5);
1081                reading.pointer_delta = Vec2::new(20.0, 0.0);
1082            }),
1083        );
1084        assert_eq!(
1085            nudged.axis2(&bindings),
1086            Vec2::new(0.5, 0.0),
1087            "the stick, over a pointer barely moved"
1088        );
1089
1090        let swept = snapshot(
1091            Reading::default(),
1092            reading(|reading| {
1093                reading.push_pad(PadAxis::LeftX, 1.0);
1094                reading.pointer_delta = Vec2::new(500.0, 0.0);
1095            }),
1096        );
1097        assert_eq!(
1098            swept.axis2(&bindings),
1099            Vec2::new(5.0, 0.0),
1100            "and the pointer, which reaches past the stick's own end"
1101        );
1102    }
1103
1104    #[test]
1105    fn a_held_pointer_reads_the_movement_its_device_reports_as_a_window_pointer_reads_its_own() {
1106        let sideways = [AxisBinding::pointer_delta(PointerDelta::Sideways)];
1107        let upward = [AxisBinding::pointer_delta(PointerDelta::Up)];
1108
1109        let mut window = Devices::new(Pads::silent(), WITHIN);
1110        seen(&mut window, &[cursor_at(10.0, 20.0)]);
1111        let placed = seen(&mut window, &[cursor_at(10.5, 19.5)]);
1112
1113        let mut devices = Devices::new(Pads::silent(), WITHIN);
1114        devices.hold_pointer(true);
1115        devices.see_device(&motion(0.5, -0.5));
1116        let held = devices.sample(Duration::ZERO);
1117
1118        assert_eq!(held.frame().axis(&sideways), placed.frame().axis(&sideways));
1119        assert_eq!(held.frame().axis(&upward), placed.frame().axis(&upward));
1120        assert!(held.frame().axis(&upward) > 0.0, "up the screen counts up");
1121    }
1122
1123    #[test]
1124    fn a_held_pointer_counts_its_movement_once_and_stays_where_it_was_held() {
1125        let mut devices = Devices::new(Pads::silent(), WITHIN);
1126        seen(&mut devices, &[cursor_at(10.0, 20.0)]);
1127        devices.hold_pointer(true);
1128
1129        devices.see_device(&motion(0.5, 0.0));
1130        devices.see(&cursor_at(14.0, 20.0));
1131        let held = devices.sample(Duration::ZERO);
1132
1133        let sideways = [AxisBinding::pointer_delta(PointerDelta::Sideways)];
1134        assert_eq!(
1135            held.frame().axis(&sideways),
1136            0.5,
1137            "the device's own movement, and not the window's report over it"
1138        );
1139        assert_eq!(
1140            held.frame().pointer(),
1141            Vec2::new(10.0, 20.0),
1142            "and the place it was held at"
1143        );
1144    }
1145
1146    #[test]
1147    fn a_released_pointer_measures_its_next_movement_from_where_the_window_reports_it() {
1148        let mut devices = Devices::new(Pads::silent(), WITHIN);
1149        let sideways = [AxisBinding::pointer_delta(PointerDelta::Sideways)];
1150
1151        seen(&mut devices, &[cursor_at(10.0, 20.0)]);
1152        devices.hold_pointer(true);
1153        seen(&mut devices, &[]);
1154        devices.hold_pointer(false);
1155
1156        let placed = seen(&mut devices, &[cursor_at(400.0, 20.0)]);
1157        assert_eq!(
1158            placed.frame().axis(&sideways),
1159            0.0,
1160            "however far the hold left it from there"
1161        );
1162
1163        devices.hold_pointer(false);
1164        let moved = seen(&mut devices, &[cursor_at(400.5, 20.0)]);
1165        assert_eq!(
1166            moved.frame().axis(&sideways),
1167            0.5,
1168            "and it moves from there, however often the release is set"
1169        );
1170    }
1171
1172    #[test]
1173    fn a_window_that_loses_focus_loses_its_hold_on_the_pointer() {
1174        let mut devices = Devices::new(Pads::silent(), WITHIN);
1175        let sideways = [AxisBinding::pointer_delta(PointerDelta::Sideways)];
1176
1177        seen(&mut devices, &[cursor_at(10.0, 20.0)]);
1178        devices.hold_pointer(true);
1179        seen(&mut devices, &[WindowEvent::Focused(false)]);
1180
1181        let placed = seen(&mut devices, &[cursor_at(400.0, 20.0)]);
1182        assert_eq!(
1183            placed.frame().pointer(),
1184            Vec2::new(400.0, 20.0),
1185            "the window places the pointer again"
1186        );
1187
1188        let moved = seen(&mut devices, &[cursor_at(400.5, 20.0)]);
1189        assert_eq!(
1190            moved.frame().axis(&sideways),
1191            0.5,
1192            "and its movement reads through again"
1193        );
1194    }
1195
1196    #[test]
1197    fn a_pointer_nothing_holds_reads_none_of_the_movement_its_device_reports() {
1198        let mut devices = Devices::new(Pads::silent(), WITHIN);
1199        seen(&mut devices, &[cursor_at(10.0, 20.0)]);
1200
1201        devices.see_device(&motion(0.5, 0.0));
1202        devices.see(&cursor_at(10.5, 20.0));
1203        let moved = devices.sample(Duration::ZERO);
1204
1205        let sideways = [AxisBinding::pointer_delta(PointerDelta::Sideways)];
1206        assert_eq!(moved.frame().axis(&sideways), 0.5, "the window's alone");
1207    }
1208
1209    #[test]
1210    fn the_first_touch_is_the_pointer_and_its_primary_button() {
1211        let mut devices = Devices::new(Pads::silent(), WITHIN);
1212        let held = [ButtonBinding::Mouse(MouseButton::Left)];
1213
1214        let touched = seen(
1215            &mut devices,
1216            &[touch_at(TouchPhase::Started, 1, 40.0, 60.0)],
1217        );
1218        assert_eq!(touched.frame().pointer(), Vec2::new(40.0, 60.0));
1219        assert!(touched.frame().pressed(&held));
1220
1221        let moved = seen(
1222            &mut devices,
1223            &[
1224                touch_at(TouchPhase::Started, 2, 0.0, 0.0),
1225                touch_at(TouchPhase::Moved, 1, 44.0, 60.0),
1226            ],
1227        );
1228        assert_eq!(
1229            moved.frame().pointer(),
1230            Vec2::new(44.0, 60.0),
1231            "a second touch is not the pointer"
1232        );
1233
1234        let ended = seen(&mut devices, &[touch_at(TouchPhase::Ended, 1, 44.0, 60.0)]);
1235        assert!(ended.frame().released(&held));
1236    }
1237
1238    #[test]
1239    fn a_window_that_loses_focus_cannot_leave_a_control_stuck() {
1240        let mut devices = Devices::new(Pads::silent(), WITHIN);
1241        seen(&mut devices, &[click(ElementState::Pressed)]);
1242
1243        let unfocused = seen(&mut devices, &[WindowEvent::Focused(false)]);
1244
1245        let held = [ButtonBinding::Mouse(MouseButton::Left)];
1246        assert!(!unfocused.frame().down(&held));
1247    }
1248
1249    #[test]
1250    fn two_buttons_make_an_axis_and_four_make_a_vector() {
1251        let walking = reading(|reading| {
1252            reading.pressed.keys[Key::D.index()] = true;
1253            reading.pressed.keys[Key::W.index()] = true;
1254        });
1255        let snapshot = snapshot(Reading::default(), walking);
1256
1257        assert_eq!(
1258            snapshot.axis(&[AxisBinding::from(ButtonAxis {
1259                negative: Key::A,
1260                positive: Key::D
1261            })]),
1262            1.0
1263        );
1264        assert_eq!(
1265            snapshot.axis(&[AxisBinding::from(ButtonAxis {
1266                negative: Key::D,
1267                positive: Key::A
1268            })]),
1269            -1.0
1270        );
1271
1272        let wasd = [Axis2Binding::from(ButtonAxis2 {
1273            left: Key::A,
1274            right: Key::D,
1275            down: Key::S,
1276            up: Key::W,
1277        })];
1278        let walk = snapshot.axis2(&wasd);
1279        assert!((walk.length() - 1.0).abs() < 1e-6, "{walk} is one long");
1280        assert!(walk.x > 0.0 && walk.y > 0.0, "{walk} points up and right");
1281    }
1282
1283    #[test]
1284    fn the_control_pushed_furthest_is_the_one_an_action_reads() {
1285        let leaning = reading(|reading| {
1286            reading.push_pad(PadAxis::LeftX, 0.5);
1287            reading.pressed.keys[Key::A.index()] = true;
1288        });
1289        let snapshot = snapshot(Reading::default(), leaning);
1290
1291        let bindings = [
1292            AxisBinding::pad(PadAxis::LeftX).deadzone(0.0),
1293            AxisBinding::from(ButtonAxis {
1294                negative: Key::A,
1295                positive: Key::D,
1296            }),
1297        ];
1298        assert_eq!(snapshot.axis(&bindings), -1.0, "the key is pushed further");
1299        assert_eq!(
1300            snapshot.axis(&bindings[..1]),
1301            0.5,
1302            "on its own the stick is"
1303        );
1304    }
1305
1306    #[test]
1307    fn a_pad_lane_reads_the_device_pushing_it_furthest() {
1308        let both = reading(|reading| {
1309            reading.push_pad(PadAxis::LeftX, 0.4);
1310            reading.push_pad(PadAxis::LeftX, -0.9);
1311            reading.push_pad(PadAxis::LeftX, 0.2);
1312            reading.push_joystick(JoystickControl::new(3), 0.6);
1313            reading.push_joystick(JoystickControl::new(3), 0.1);
1314        });
1315
1316        assert_eq!(both.pad_axes[PadAxis::LeftX.index()], -0.9);
1317        assert_eq!(both.joystick_axes.value(JoystickControl::new(3)), 0.6);
1318        assert_eq!(
1319            both.joystick_axes.value(JoystickControl::new(4)),
1320            0.0,
1321            "and nothing for the rest"
1322        );
1323    }
1324
1325    #[test]
1326    fn capture_answers_with_the_control_the_player_just_moved() {
1327        let quiet = Reading::default();
1328        let pushed = reading(|reading| {
1329            reading.press_pad(Pad::Start);
1330            reading.push_pad(PadAxis::RightX, 0.8);
1331            reading.push_joystick(JoystickControl::new(11), 0.9);
1332        });
1333        let snapshot = snapshot(quiet, pushed);
1334
1335        assert_eq!(
1336            snapshot.actuated_button(),
1337            Some(ButtonBinding::Pad(Pad::Start))
1338        );
1339        assert_eq!(
1340            snapshot.actuated_axis(),
1341            Some(AxisBinding::pad(PadAxis::RightX))
1342        );
1343        assert_eq!(
1344            snapshot.actuated_axis2(),
1345            Some(Axis2Binding::stick(Stick::Right))
1346        );
1347
1348        let still = Snapshot {
1349            before: pushed,
1350            now: pushed,
1351        };
1352        assert_eq!(still.actuated_button(), None, "a held control is not new");
1353        assert_eq!(still.actuated_axis(), None);
1354        assert_eq!(still.actuated_axis2(), None);
1355    }
1356
1357    #[test]
1358    fn capture_is_deaf_to_a_control_that_has_barely_moved() {
1359        let nudged = reading(|reading| reading.push_pad(PadAxis::LeftY, ACTUATED));
1360        let snapshot = snapshot(Reading::default(), nudged);
1361
1362        assert_eq!(snapshot.actuated_axis(), None);
1363        assert_eq!(snapshot.actuated_axis2(), None);
1364    }
1365
1366    #[test]
1367    fn one_notch_reads_as_one_however_the_platform_counts_a_turn() {
1368        let browser = Platform::Browser.wheel_rate();
1369        let desktop = Platform::Desktop.wheel_rate();
1370
1371        assert_eq!(
1372            browser.notches(rolled_away(3.0)),
1373            Vec2::new(0.0, 1.0),
1374            "three lines are a notch in a browser"
1375        );
1376        assert_eq!(
1377            browser.notches(scrolled_away(100.0)),
1378            Vec2::new(0.0, 1.0),
1379            "and so are 100 pixels"
1380        );
1381        assert_eq!(
1382            desktop.notches(rolled_away(1.0)),
1383            Vec2::new(0.0, 1.0),
1384            "one line is a notch on the desktop"
1385        );
1386        assert_eq!(
1387            desktop.notches(scrolled_away(100.0)),
1388            Vec2::new(0.0, 1.0),
1389            "and 100 pixels are one there too"
1390        );
1391    }
1392
1393    #[test]
1394    fn a_roll_away_and_a_tilt_to_the_right_each_read_positive() {
1395        let rate = Platform::Desktop.wheel_rate();
1396
1397        assert_eq!(rate.notches(rolled_away(1.0)), Vec2::new(0.0, 1.0));
1398        assert_eq!(rate.notches(tilted_right(1.0)), Vec2::new(1.0, 0.0));
1399        assert_eq!(
1400            rate.notches(rolled_away(-1.0)),
1401            Vec2::new(0.0, -1.0),
1402            "and a roll toward the player reads the other way round"
1403        );
1404    }
1405
1406    #[test]
1407    fn a_tilt_reaches_the_sideways_lane_and_a_roll_the_upward_one() {
1408        let mut devices = Devices::new(Pads::silent(), WITHIN);
1409        let sideways = [AxisBinding::wheel(WheelDelta::Sideways)];
1410        let upward = [AxisBinding::wheel(WheelDelta::Up)];
1411
1412        let tilted = seen(&mut devices, &[wheel(tilted_right(1.0))]);
1413        assert_eq!(tilted.frame().axis(&sideways), 1.0);
1414        assert_eq!(tilted.frame().axis(&upward), 0.0, "and nothing rolled");
1415
1416        let rolled = seen(&mut devices, &[wheel(rolled_away(1.0))]);
1417        assert_eq!(rolled.frame().axis(&upward), 1.0);
1418        assert_eq!(rolled.frame().axis(&sideways), 0.0, "nothing tilted");
1419
1420        let still = seen(&mut devices, &[]);
1421        assert_eq!(
1422            still.frame().axis(&upward),
1423            0.0,
1424            "and a turn lasts one reading"
1425        );
1426    }
1427
1428    #[test]
1429    fn the_wheel_reads_as_one_vector_with_the_tilt_as_x_and_the_roll_as_y() {
1430        let mut devices = Devices::new(Pads::silent(), WITHIN);
1431        let wheel_vector = [Axis2Binding::wheel()];
1432
1433        let turned = seen(
1434            &mut devices,
1435            &[wheel(tilted_right(1.0)), wheel(rolled_away(3.0))],
1436        );
1437        assert_eq!(turned.frame().axis2(&wheel_vector), Vec2::new(1.0, 3.0));
1438        assert_eq!(
1439            turned.frame().actuated_axis2(),
1440            None,
1441            "and a turn that far is never captured"
1442        );
1443
1444        let still = seen(&mut devices, &[]);
1445        assert_eq!(
1446            still.frame().axis2(&wheel_vector),
1447            Vec2::ZERO,
1448            "a turn lasts one reading"
1449        );
1450    }
1451
1452    #[test]
1453    fn a_pointer_is_never_captured_however_far_it_is_moved() {
1454        let mut devices = Devices::new(Pads::silent(), WITHIN);
1455        seen(&mut devices, &[cursor_at(0.0, 0.0)]);
1456        let moved = seen(&mut devices, &[cursor_at(400.0, 400.0)]);
1457
1458        assert_eq!(moved.frame().actuated_axis(), None);
1459        assert_eq!(moved.frame().actuated_axis2(), None);
1460    }
1461}