1#![forbid(unsafe_code)]
9
10use std::collections::{HashMap, HashSet};
11
12pub use winit::keyboard::KeyCode as Key;
13
14use gilrs::ff::{BaseEffect, BaseEffectType, EffectBuilder, Replay, Ticks};
15pub use gilrs::{Axis, Button, GamepadId};
16use gilrs::{Event, Gilrs};
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
19pub enum MouseButton {
20 Left,
21 Right,
22 Middle,
23 Other(u16),
24}
25
26impl From<winit::event::MouseButton> for MouseButton {
27 fn from(b: winit::event::MouseButton) -> Self {
28 match b {
29 winit::event::MouseButton::Left => MouseButton::Left,
30 winit::event::MouseButton::Right => MouseButton::Right,
31 winit::event::MouseButton::Middle => MouseButton::Middle,
32 winit::event::MouseButton::Back => MouseButton::Other(3),
33 winit::event::MouseButton::Forward => MouseButton::Other(4),
34 winit::event::MouseButton::Other(n) => MouseButton::Other(n),
35 }
36 }
37}
38
39#[derive(Copy, Clone, Debug, PartialEq)]
43pub struct PointerMapping {
44 pub origin: (f32, f32),
46 pub scale: (f32, f32),
48}
49
50impl PointerMapping {
51 pub fn apply(&self, pos: (f32, f32)) -> (f32, f32) {
53 (
54 (pos.0 - self.origin.0) * self.scale.0,
55 (pos.1 - self.origin.1) * self.scale.1,
56 )
57 }
58
59 pub fn apply_delta(&self, delta: (f32, f32)) -> (f32, f32) {
61 (delta.0 * self.scale.0, delta.1 * self.scale.1)
62 }
63}
64
65#[derive(Copy, Clone, Debug, PartialEq)]
70pub struct Touch {
71 id: u64,
72 pos: (f32, f32),
73 start_pos: (f32, f32),
74 delta: (f32, f32),
75}
76
77impl Touch {
78 pub fn id(&self) -> u64 {
80 self.id
81 }
82 pub fn pos(&self) -> (f32, f32) {
84 self.pos
85 }
86 pub fn start_pos(&self) -> (f32, f32) {
88 self.start_pos
89 }
90 pub fn delta(&self) -> (f32, f32) {
92 self.delta
93 }
94}
95
96pub struct Gamepad {
99 id: GamepadId,
100 name: String,
101 connected: bool,
102 held: HashSet<Button>,
103 pressed: HashSet<Button>,
104 released: HashSet<Button>,
105 axes: HashMap<Axis, f32>,
106}
107
108impl Gamepad {
109 fn new(id: GamepadId) -> Self {
110 Self {
111 id,
112 name: String::new(),
113 connected: true,
114 held: HashSet::new(),
115 pressed: HashSet::new(),
116 released: HashSet::new(),
117 axes: HashMap::new(),
118 }
119 }
120
121 pub fn id(&self) -> GamepadId {
122 self.id
123 }
124 pub fn name(&self) -> &str {
125 &self.name
126 }
127 pub fn is_connected(&self) -> bool {
128 self.connected
129 }
130 pub fn button_held(&self, b: Button) -> bool {
131 self.held.contains(&b)
132 }
133 pub fn button_pressed(&self, b: Button) -> bool {
134 self.pressed.contains(&b)
135 }
136 pub fn button_released(&self, b: Button) -> bool {
137 self.released.contains(&b)
138 }
139 pub fn any_button_pressed(&self) -> bool {
141 !self.pressed.is_empty()
142 }
143 pub fn axis(&self, axis: Axis) -> f32 {
145 self.axes.get(&axis).copied().unwrap_or(0.0)
146 }
147}
148
149#[derive(Default)]
150pub struct Input {
151 keys_held: HashSet<Key>,
152 keys_pressed: HashSet<Key>,
153 keys_released: HashSet<Key>,
154 mouse_held: HashSet<MouseButton>,
155 mouse_pressed: HashSet<MouseButton>,
156 mouse_released: HashSet<MouseButton>,
157 mouse_pos: (f32, f32),
158 mouse_delta: (f32, f32),
159 scroll: (f32, f32),
160 touches: HashMap<u64, Touch>,
161 touches_started: Vec<Touch>,
162 touches_ended: Vec<Touch>,
163 touch_emulates_mouse: bool,
165 mouse_finger: Option<u64>,
167 pointer_mapping: Option<PointerMapping>,
169 gilrs: Option<Gilrs>,
171 gamepads: HashMap<GamepadId, Gamepad>,
172 rumble: Vec<gilrs::ff::Effect>,
174}
175
176impl Input {
177 pub fn new() -> Self {
178 let mut me = Self::default();
179 match Gilrs::new() {
180 Ok(g) => {
181 for (id, gp) in g.gamepads() {
184 let mut pad = Gamepad::new(id);
185 pad.name = gp.name().to_string();
186 me.gamepads.insert(id, pad);
187 }
188 me.gilrs = Some(g);
189 }
190 Err(e) => log::warn!("gamepad backend unavailable, continuing without it: {e}"),
191 }
192 me
193 }
194
195 pub fn gamepads(&self) -> impl Iterator<Item = &Gamepad> {
197 self.gamepads.values().filter(|g| g.connected)
198 }
199
200 pub fn first_gamepad(&self) -> Option<&Gamepad> {
202 self.gamepads.values().find(|g| g.connected)
203 }
204
205 pub fn gamepad(&self, id: GamepadId) -> Option<&Gamepad> {
207 self.gamepads.get(&id).filter(|g| g.connected)
208 }
209
210 pub fn poll_gamepads(&mut self) {
213 let Some(gilrs) = self.gilrs.as_mut() else {
214 return;
215 };
216 while let Some(Event { id, event, .. }) = gilrs.next_event() {
217 use gilrs::EventType::*;
218 let pad = self.gamepads.entry(id).or_insert_with(|| Gamepad::new(id));
219 match event {
220 Connected => {
221 pad.connected = true;
222 pad.name = gilrs.gamepad(id).name().to_string();
223 }
224 Disconnected | Dropped => {
225 pad.connected = false;
226 pad.held.clear();
227 pad.axes.clear();
228 }
229 ButtonPressed(b, _) => {
230 pad.held.insert(b);
231 pad.pressed.insert(b);
232 }
233 ButtonReleased(b, _) => {
234 pad.held.remove(&b);
235 pad.released.insert(b);
236 }
237 AxisChanged(axis, value, _) => {
238 pad.axes.insert(axis, value);
239 }
240 _ => {}
241 }
242 }
243 }
244
245 pub fn set_rumble(&mut self, id: GamepadId, magnitude: f32, duration_ms: u32) {
248 let Some(gilrs) = self.gilrs.as_mut() else {
249 return;
250 };
251 if !gilrs
252 .connected_gamepad(id)
253 .is_some_and(|g| g.is_ff_supported())
254 {
255 return;
256 }
257 let mag = (magnitude.clamp(0.0, 1.0) * f32::from(u16::MAX)) as u16;
258 let effect = EffectBuilder::new()
259 .add_effect(BaseEffect {
260 kind: BaseEffectType::Strong { magnitude: mag },
261 scheduling: Replay {
262 play_for: Ticks::from_ms(duration_ms),
263 ..Default::default()
264 },
265 envelope: Default::default(),
266 })
267 .gamepads(&[id])
268 .finish(gilrs);
269 if let Ok(effect) = effect {
270 let _ = effect.play();
271 self.rumble.push(effect);
274 if self.rumble.len() > 16 {
275 self.rumble.remove(0);
276 }
277 }
278 }
279
280 pub fn key_held(&self, k: Key) -> bool {
281 self.keys_held.contains(&k)
282 }
283 pub fn key_pressed(&self, k: Key) -> bool {
284 self.keys_pressed.contains(&k)
285 }
286 pub fn key_released(&self, k: Key) -> bool {
287 self.keys_released.contains(&k)
288 }
289
290 pub fn any_key_pressed(&self) -> bool {
294 self.keys_pressed.iter().any(|k| !is_modifier(*k))
295 }
296
297 pub fn keys_pressed(&self) -> impl Iterator<Item = Key> + '_ {
300 self.keys_pressed.iter().copied()
301 }
302
303 pub fn any_mouse_pressed(&self) -> bool {
305 !self.mouse_pressed.is_empty()
306 }
307
308 pub fn any_pressed(&self) -> bool {
329 self.any_key_pressed()
330 || self.any_mouse_pressed()
331 || self.gamepads().any(Gamepad::any_button_pressed)
332 || self.any_touch_pressed()
333 }
334
335 pub fn touches(&self) -> impl Iterator<Item = &Touch> {
337 self.touches.values()
338 }
339
340 pub fn touch(&self, id: u64) -> Option<&Touch> {
342 self.touches.get(&id)
343 }
344
345 pub fn touches_started(&self) -> &[Touch] {
348 &self.touches_started
349 }
350
351 pub fn touches_ended(&self) -> &[Touch] {
355 &self.touches_ended
356 }
357
358 pub fn any_touch_pressed(&self) -> bool {
360 !self.touches_started.is_empty()
361 }
362
363 pub fn set_touch_emulates_mouse(&mut self, on: bool) {
376 self.touch_emulates_mouse = on;
377 if !on && self.mouse_finger.take().is_some() {
378 self.mouse_held.remove(&MouseButton::Left);
379 }
380 }
381
382 pub fn touch_emulates_mouse(&self) -> bool {
384 self.touch_emulates_mouse
385 }
386
387 pub fn mouse_held(&self, b: MouseButton) -> bool {
388 self.mouse_held.contains(&b)
389 }
390 pub fn mouse_pressed(&self, b: MouseButton) -> bool {
391 self.mouse_pressed.contains(&b)
392 }
393 pub fn mouse_released(&self, b: MouseButton) -> bool {
394 self.mouse_released.contains(&b)
395 }
396
397 pub fn mouse_pos(&self) -> (f32, f32) {
401 match &self.pointer_mapping {
402 Some(m) => m.apply(self.mouse_pos),
403 None => self.mouse_pos,
404 }
405 }
406 pub fn mouse_pos_window(&self) -> (f32, f32) {
409 self.mouse_pos
410 }
411 pub fn mouse_delta(&self) -> (f32, f32) {
413 match &self.pointer_mapping {
414 Some(m) => m.apply_delta(self.mouse_delta),
415 None => self.mouse_delta,
416 }
417 }
418
419 pub fn set_pointer_mapping(&mut self, mapping: Option<PointerMapping>) {
424 self.pointer_mapping = mapping;
425 }
426
427 pub fn pointer_mapping(&self) -> Option<PointerMapping> {
429 self.pointer_mapping
430 }
431 pub fn scroll(&self) -> (f32, f32) {
432 self.scroll
433 }
434
435 pub fn handle_window_event(&mut self, event: &winit::event::WindowEvent) {
436 use winit::event::{ElementState, MouseScrollDelta, WindowEvent};
437 match event {
438 WindowEvent::KeyboardInput { event, .. } => {
439 let winit::keyboard::PhysicalKey::Code(code) = event.physical_key else {
440 return;
441 };
442 match event.state {
443 ElementState::Pressed => {
444 if self.keys_held.insert(code) {
445 self.keys_pressed.insert(code);
446 }
447 }
448 ElementState::Released => {
449 if self.keys_held.remove(&code) {
450 self.keys_released.insert(code);
451 }
452 }
453 }
454 }
455 WindowEvent::MouseInput { state, button, .. } => {
456 let b = MouseButton::from(*button);
457 match state {
458 ElementState::Pressed => {
459 if self.mouse_held.insert(b) {
460 self.mouse_pressed.insert(b);
461 }
462 }
463 ElementState::Released => {
464 if self.mouse_held.remove(&b) {
465 self.mouse_released.insert(b);
466 }
467 }
468 }
469 }
470 WindowEvent::CursorMoved { position, .. } => {
471 let new = (position.x as f32, position.y as f32);
472 self.mouse_delta.0 += new.0 - self.mouse_pos.0;
473 self.mouse_delta.1 += new.1 - self.mouse_pos.1;
474 self.mouse_pos = new;
475 }
476 WindowEvent::MouseWheel { delta, .. } => match delta {
477 MouseScrollDelta::LineDelta(x, y) => {
478 self.scroll.0 += x;
479 self.scroll.1 += y;
480 }
481 MouseScrollDelta::PixelDelta(p) => {
482 self.scroll.0 += p.x as f32;
483 self.scroll.1 += p.y as f32;
484 }
485 },
486 WindowEvent::Touch(t) => {
487 let pos = (t.location.x as f32, t.location.y as f32);
488 self.handle_touch(t.id, t.phase, pos);
489 }
490 WindowEvent::Focused(false) => {
491 self.keys_held.clear();
492 self.mouse_held.clear();
493 self.touches.clear();
494 self.mouse_finger = None;
495 }
496 _ => {}
497 }
498 }
499
500 fn handle_touch(&mut self, id: u64, phase: winit::event::TouchPhase, pos: (f32, f32)) {
501 use winit::event::TouchPhase;
502 if self.touch_emulates_mouse && !cfg!(target_os = "windows") {
504 self.emulate_mouse(id, phase, pos);
505 }
506 let pos = match &self.pointer_mapping {
507 Some(m) => m.apply(pos),
508 None => pos,
509 };
510 match phase {
511 TouchPhase::Started => {
512 let touch = Touch {
513 id,
514 pos,
515 start_pos: pos,
516 delta: (0.0, 0.0),
517 };
518 self.touches.insert(id, touch);
519 self.touches_started.push(touch);
520 }
521 TouchPhase::Moved => {
522 if let Some(touch) = self.touches.get_mut(&id) {
523 touch.delta.0 += pos.0 - touch.pos.0;
524 touch.delta.1 += pos.1 - touch.pos.1;
525 touch.pos = pos;
526 }
527 }
528 TouchPhase::Ended => {
529 if let Some(mut touch) = self.touches.remove(&id) {
530 touch.delta.0 += pos.0 - touch.pos.0;
531 touch.delta.1 += pos.1 - touch.pos.1;
532 touch.pos = pos;
533 self.touches_ended.push(touch);
534 }
535 }
536 TouchPhase::Cancelled => {
537 self.touches.remove(&id);
538 }
539 }
540 }
541
542 fn emulate_mouse(&mut self, id: u64, phase: winit::event::TouchPhase, pos: (f32, f32)) {
544 use winit::event::TouchPhase;
545 match phase {
546 TouchPhase::Started if self.mouse_finger.is_none() => {
547 self.mouse_finger = Some(id);
548 self.mouse_pos = pos;
550 if self.mouse_held.insert(MouseButton::Left) {
551 self.mouse_pressed.insert(MouseButton::Left);
552 }
553 }
554 _ if self.mouse_finger != Some(id) => {}
555 TouchPhase::Started => {}
556 TouchPhase::Moved | TouchPhase::Ended => {
557 self.mouse_delta.0 += pos.0 - self.mouse_pos.0;
558 self.mouse_delta.1 += pos.1 - self.mouse_pos.1;
559 self.mouse_pos = pos;
560 if phase == TouchPhase::Ended {
561 self.mouse_finger = None;
562 if self.mouse_held.remove(&MouseButton::Left) {
563 self.mouse_released.insert(MouseButton::Left);
564 }
565 }
566 }
567 TouchPhase::Cancelled => {
568 self.mouse_finger = None;
569 self.mouse_held.remove(&MouseButton::Left);
570 }
571 }
572 }
573
574 pub fn end_frame(&mut self) {
576 self.keys_pressed.clear();
577 self.keys_released.clear();
578 self.mouse_pressed.clear();
579 self.mouse_released.clear();
580 self.mouse_delta = (0.0, 0.0);
581 self.scroll = (0.0, 0.0);
582 self.touches_started.clear();
583 self.touches_ended.clear();
584 for touch in self.touches.values_mut() {
585 touch.delta = (0.0, 0.0);
586 }
587 for pad in self.gamepads.values_mut() {
588 pad.pressed.clear();
589 pad.released.clear();
590 }
591 }
592}
593
594fn is_modifier(k: Key) -> bool {
596 matches!(
597 k,
598 Key::ShiftLeft
599 | Key::ShiftRight
600 | Key::ControlLeft
601 | Key::ControlRight
602 | Key::AltLeft
603 | Key::AltRight
604 | Key::SuperLeft
605 | Key::SuperRight
606 | Key::Meta
607 | Key::Hyper
608 | Key::Fn
609 | Key::FnLock
610 )
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[test]
621 fn nothing_pressed_by_default() {
622 let input = Input::default();
623 assert!(!input.any_key_pressed());
624 assert!(!input.any_mouse_pressed());
625 assert!(!input.any_pressed());
626 assert_eq!(input.keys_pressed().count(), 0);
627 }
628
629 #[test]
630 fn a_key_counts_but_a_lone_modifier_does_not() {
631 let mut input = Input::default();
632 input.keys_pressed.insert(Key::ShiftLeft);
633 assert!(!input.any_key_pressed());
634 assert!(!input.any_pressed());
635 assert_eq!(input.keys_pressed().collect::<Vec<_>>(), [Key::ShiftLeft]);
636
637 input.keys_pressed.insert(Key::KeyX);
638 assert!(input.any_key_pressed());
639 assert!(input.any_pressed());
640 assert_eq!(input.keys_pressed().count(), 2);
641 }
642
643 #[test]
644 fn mouse_buttons_count() {
645 let mut input = Input::default();
646 input.mouse_pressed.insert(MouseButton::Right);
647 assert!(input.any_mouse_pressed());
648 assert!(input.any_pressed());
649 assert!(!input.any_key_pressed());
650 }
651
652 #[test]
653 fn end_frame_clears_the_edges() {
654 let mut input = Input::default();
655 input.keys_pressed.insert(Key::Space);
656 input.mouse_pressed.insert(MouseButton::Left);
657 input.end_frame();
658 assert!(!input.any_pressed());
659 }
660
661 fn touch(input: &mut Input, id: u64, phase: winit::event::TouchPhase, x: f64, y: f64) {
662 input.handle_window_event(&winit::event::WindowEvent::Touch(winit::event::Touch {
663 device_id: winit::event::DeviceId::dummy(),
664 phase,
665 location: winit::dpi::PhysicalPosition::new(x, y),
666 force: None,
667 id,
668 }));
669 }
670
671 #[test]
672 fn a_touch_goes_down_moves_and_lifts() {
673 use winit::event::TouchPhase::*;
674 let mut input = Input::default();
675
676 touch(&mut input, 7, Started, 10.0, 20.0);
677 assert!(input.any_touch_pressed());
678 assert!(input.any_pressed());
679 assert_eq!(input.touches_started().len(), 1);
680 assert_eq!(input.touch(7).unwrap().pos(), (10.0, 20.0));
681 input.end_frame();
682 assert!(!input.any_pressed());
683 assert_eq!(input.touches().count(), 1);
684
685 touch(&mut input, 7, Moved, 15.0, 20.0);
686 touch(&mut input, 7, Moved, 18.0, 24.0);
687 let t = *input.touch(7).unwrap();
688 assert_eq!(t.pos(), (18.0, 24.0));
689 assert_eq!(t.start_pos(), (10.0, 20.0));
690 assert_eq!(t.delta(), (8.0, 4.0));
691 input.end_frame();
692 assert_eq!(input.touch(7).unwrap().delta(), (0.0, 0.0));
693
694 touch(&mut input, 7, Ended, 20.0, 24.0);
695 assert_eq!(input.touches().count(), 0);
696 let ended = input.touches_ended();
697 assert_eq!(ended.len(), 1);
698 assert_eq!(ended[0].pos(), (20.0, 24.0));
699 assert_eq!(ended[0].start_pos(), (10.0, 20.0));
700 input.end_frame();
701 assert!(input.touches_ended().is_empty());
702 }
703
704 #[test]
705 fn a_tap_within_one_frame_is_started_and_ended() {
706 use winit::event::TouchPhase::*;
707 let mut input = Input::default();
708 touch(&mut input, 1, Started, 5.0, 5.0);
709 touch(&mut input, 1, Ended, 5.0, 5.0);
710 assert!(input.any_touch_pressed());
711 assert_eq!(input.touches_started().len(), 1);
712 assert_eq!(input.touches_ended().len(), 1);
713 assert_eq!(input.touches().count(), 0);
714 }
715
716 #[test]
717 fn fingers_are_tracked_separately() {
718 use winit::event::TouchPhase::*;
719 let mut input = Input::default();
720 touch(&mut input, 1, Started, 0.0, 0.0);
721 touch(&mut input, 2, Started, 100.0, 0.0);
722 touch(&mut input, 2, Moved, 110.0, 0.0);
723 assert_eq!(input.touches().count(), 2);
724 assert_eq!(input.touch(1).unwrap().pos(), (0.0, 0.0));
725 assert_eq!(input.touch(2).unwrap().pos(), (110.0, 0.0));
726 }
727
728 #[test]
729 fn a_cancelled_touch_vanishes_without_ending() {
730 use winit::event::TouchPhase::*;
731 let mut input = Input::default();
732 touch(&mut input, 3, Started, 0.0, 0.0);
733 input.end_frame();
734 touch(&mut input, 3, Cancelled, 0.0, 0.0);
735 assert_eq!(input.touches().count(), 0);
736 assert!(input.touches_ended().is_empty());
737 }
738
739 #[test]
740 fn losing_focus_drops_touches() {
741 let mut input = Input::default();
742 touch(&mut input, 4, winit::event::TouchPhase::Started, 0.0, 0.0);
743 input.handle_window_event(&winit::event::WindowEvent::Focused(false));
744 assert_eq!(input.touches().count(), 0);
745 }
746
747 #[test]
748 fn touches_leave_the_mouse_alone_by_default() {
749 use winit::event::TouchPhase::*;
750 let mut input = Input::default();
751 touch(&mut input, 1, Started, 50.0, 60.0);
752 assert_eq!(input.mouse_pos(), (0.0, 0.0));
753 assert!(!input.mouse_held(MouseButton::Left));
754 assert!(!input.any_mouse_pressed());
755 }
756
757 #[test]
759 #[cfg_attr(target_os = "windows", ignore)]
760 fn the_first_finger_drives_the_mouse() {
761 use winit::event::TouchPhase::*;
762 let mut input = Input::default();
763 input.set_touch_emulates_mouse(true);
764
765 touch(&mut input, 1, Started, 50.0, 60.0);
766 assert!(input.mouse_pressed(MouseButton::Left));
767 assert!(input.mouse_held(MouseButton::Left));
768 assert_eq!(input.mouse_pos(), (50.0, 60.0));
769 assert_eq!(input.mouse_delta(), (0.0, 0.0), "landing is not a movement");
770 input.end_frame();
771
772 touch(&mut input, 2, Started, 300.0, 300.0);
774 touch(&mut input, 2, Moved, 310.0, 300.0);
775 assert!(!input.mouse_pressed(MouseButton::Left));
776 assert_eq!(input.mouse_pos(), (50.0, 60.0));
777
778 touch(&mut input, 1, Moved, 55.0, 70.0);
779 assert_eq!(input.mouse_pos(), (55.0, 70.0));
780 assert_eq!(input.mouse_delta(), (5.0, 10.0));
781 input.end_frame();
782
783 touch(&mut input, 1, Ended, 57.0, 70.0);
784 assert!(input.mouse_released(MouseButton::Left));
785 assert!(!input.mouse_held(MouseButton::Left));
786 assert_eq!(input.mouse_pos(), (57.0, 70.0));
787 input.end_frame();
788
789 touch(&mut input, 2, Moved, 320.0, 300.0);
791 assert_eq!(input.mouse_pos(), (57.0, 70.0));
792 touch(&mut input, 3, Started, 100.0, 100.0);
793 assert!(input.mouse_pressed(MouseButton::Left));
794 assert_eq!(input.mouse_pos(), (100.0, 100.0));
795 }
796
797 #[test]
798 #[cfg_attr(target_os = "windows", ignore)]
799 fn a_cancelled_mouse_finger_is_not_a_click() {
800 use winit::event::TouchPhase::*;
801 let mut input = Input::default();
802 input.set_touch_emulates_mouse(true);
803 touch(&mut input, 1, Started, 0.0, 0.0);
804 input.end_frame();
805 touch(&mut input, 1, Cancelled, 0.0, 0.0);
806 assert!(!input.mouse_held(MouseButton::Left));
807 assert!(!input.mouse_released(MouseButton::Left));
808 touch(&mut input, 2, Started, 5.0, 5.0);
810 assert!(input.mouse_pressed(MouseButton::Left));
811 }
812
813 #[test]
814 #[cfg_attr(target_os = "windows", ignore)]
815 fn turning_emulation_off_lets_go_of_the_button() {
816 let mut input = Input::default();
817 input.set_touch_emulates_mouse(true);
818 touch(&mut input, 1, winit::event::TouchPhase::Started, 0.0, 0.0);
819 input.set_touch_emulates_mouse(false);
820 assert!(!input.mouse_held(MouseButton::Left));
821 touch(&mut input, 1, winit::event::TouchPhase::Ended, 0.0, 0.0);
822 assert!(!input.mouse_released(MouseButton::Left));
823 }
824
825 #[test]
826 fn moves_and_ends_for_unknown_fingers_are_ignored() {
827 use winit::event::TouchPhase::*;
828 let mut input = Input::default();
829 touch(&mut input, 9, Moved, 1.0, 1.0);
830 touch(&mut input, 9, Ended, 1.0, 1.0);
831 assert_eq!(input.touches().count(), 0);
832 assert!(input.touches_ended().is_empty());
833 }
834
835 #[test]
836 fn pointer_mapping_maps_mouse_and_touch() {
837 use winit::event::TouchPhase;
838 let mut input = Input::default();
839 input.set_pointer_mapping(Some(PointerMapping {
840 origin: (20.0, 80.0),
841 scale: (1.0 / 3.0, 1.0 / 3.0),
842 }));
843 input.mouse_pos = (23.0, 86.0);
844 input.mouse_delta = (6.0, -3.0);
845 assert_eq!(input.mouse_pos(), (1.0, 2.0));
846 assert_eq!(input.mouse_pos_window(), (23.0, 86.0));
847 assert_eq!(input.mouse_delta(), (2.0, -1.0));
848 input.handle_touch(7, TouchPhase::Started, (50.0, 80.0));
849 assert_eq!(input.touch(7).map(Touch::pos), Some((10.0, 0.0)));
850 input.set_pointer_mapping(None);
851 assert_eq!(input.mouse_pos(), (23.0, 86.0));
852 }
853}