1use 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
18const UNMAPPED: usize = 64;
21
22const HELD: f32 = 0.5;
24
25const ACTUATED: f32 = AxisBinding::DEFAULT_DEADZONE;
28
29#[derive(Clone, Copy, Debug, PartialEq)]
32pub(crate) struct WheelRate {
33 lines: f32,
34 pixels: f32,
35}
36
37impl WheelRate {
38 pub(crate) const DESKTOP: Self = Self::per_notch(1.0, 100.0);
43
44 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 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 #[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#[derive(Clone, Copy, Default)]
75pub(crate) struct Controls {
76 frame: Snapshot,
77 tapped: Pressed,
81 clicking: Clicking,
82}
83
84impl Controls {
85 pub(crate) fn frame(&self) -> &Snapshot {
87 &self.frame
88 }
89
90 pub(crate) fn clicks(&self, bindings: &[ButtonBinding]) -> u32 {
93 self.clicking.clicks(bindings)
94 }
95}
96
97#[derive(Default)]
100pub(crate) struct Ticks {
101 snapshot: Snapshot,
102 tapped: Pressed,
103}
104
105impl Ticks {
106 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 pub(crate) fn snapshot(&self) -> &Snapshot {
115 &self.snapshot
116 }
117
118 pub(crate) fn ticked(&mut self) {
121 self.snapshot.before = self.snapshot.now;
122 self.tapped = Pressed::default();
123 }
124}
125
126#[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 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 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 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 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#[derive(Clone, Copy, Default)]
225struct Clicking {
226 control: Option<ButtonBinding>,
227 at: Duration,
228 count: u32,
229}
230
231impl Clicking {
232 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 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#[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 pointer_delta: Vec2,
274 wheel: Vec2,
277}
278
279impl Reading {
280 fn holding(mut self, tapped: Pressed) -> Self {
283 self.pressed.merge(tapped);
284 self
285 }
286
287 pub(crate) fn press_pad(&mut self, button: Pad) {
290 self.pad[button.index()] = true;
291 }
292
293 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 pub(crate) fn press_joystick(&mut self, control: JoystickControl) {
305 self.joystick.push(control, 1.0);
306 }
307
308 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 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#[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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
435pub enum Switch {
436 Key(Key),
438 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#[derive(Clone, Copy)]
457struct Unmapped {
458 controls: [(JoystickControl, f32); UNMAPPED],
459 len: usize,
460}
461
462impl Unmapped {
463 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
499pub(crate) struct Devices {
502 pads: Pads,
503 double_click: Duration,
505 live: Reading,
506 tapped: Pressed,
509 tracked: Option<Vec2>,
512 held: bool,
515 touch: Option<u64>,
518 before: Reading,
521 clicking: Clicking,
522}
523
524impl Devices {
525 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 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 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 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 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 pub(crate) fn move_pointer(&mut self, pixels: Vec2) {
638 self.live.pointer_delta += pixels;
639 }
640
641 #[cfg(feature = "offscreen")]
644 pub(crate) fn turn_wheel(&mut self, notches: Vec2) {
645 self.live.wheel += notches;
646 }
647
648 #[cfg(all(feature = "ui", feature = "offscreen"))]
651 pub(crate) fn holds(&self, control: Switch) -> bool {
652 self.live.pressed.held(control)
653 }
654
655 #[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 #[cfg(all(feature = "ui", feature = "offscreen"))]
680 pub(crate) fn pointing_at(&self) -> Option<Vec2> {
681 self.tracked
682 }
683
684 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 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 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 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 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 fn rolled_away(lines: f32) -> MouseScrollDelta {
783 MouseScrollDelta::LineDelta(0.0, lines)
784 }
785
786 fn tilted_right(lines: f32) -> MouseScrollDelta {
790 MouseScrollDelta::LineDelta(-lines, 0.0)
791 }
792
793 fn scrolled_away(pixels: f64) -> MouseScrollDelta {
796 MouseScrollDelta::PixelDelta(PhysicalPosition::new(0.0, pixels))
797 }
798
799 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 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 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 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}