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 Touch {
44 id: u64,
45 pos: (f32, f32),
46 start_pos: (f32, f32),
47 delta: (f32, f32),
48}
49
50impl Touch {
51 pub fn id(&self) -> u64 {
53 self.id
54 }
55 pub fn pos(&self) -> (f32, f32) {
57 self.pos
58 }
59 pub fn start_pos(&self) -> (f32, f32) {
61 self.start_pos
62 }
63 pub fn delta(&self) -> (f32, f32) {
65 self.delta
66 }
67}
68
69pub struct Gamepad {
72 id: GamepadId,
73 name: String,
74 connected: bool,
75 held: HashSet<Button>,
76 pressed: HashSet<Button>,
77 released: HashSet<Button>,
78 axes: HashMap<Axis, f32>,
79}
80
81impl Gamepad {
82 fn new(id: GamepadId) -> Self {
83 Self {
84 id,
85 name: String::new(),
86 connected: true,
87 held: HashSet::new(),
88 pressed: HashSet::new(),
89 released: HashSet::new(),
90 axes: HashMap::new(),
91 }
92 }
93
94 pub fn id(&self) -> GamepadId {
95 self.id
96 }
97 pub fn name(&self) -> &str {
98 &self.name
99 }
100 pub fn is_connected(&self) -> bool {
101 self.connected
102 }
103 pub fn button_held(&self, b: Button) -> bool {
104 self.held.contains(&b)
105 }
106 pub fn button_pressed(&self, b: Button) -> bool {
107 self.pressed.contains(&b)
108 }
109 pub fn button_released(&self, b: Button) -> bool {
110 self.released.contains(&b)
111 }
112 pub fn any_button_pressed(&self) -> bool {
114 !self.pressed.is_empty()
115 }
116 pub fn axis(&self, axis: Axis) -> f32 {
118 self.axes.get(&axis).copied().unwrap_or(0.0)
119 }
120}
121
122#[derive(Default)]
123pub struct Input {
124 keys_held: HashSet<Key>,
125 keys_pressed: HashSet<Key>,
126 keys_released: HashSet<Key>,
127 mouse_held: HashSet<MouseButton>,
128 mouse_pressed: HashSet<MouseButton>,
129 mouse_released: HashSet<MouseButton>,
130 mouse_pos: (f32, f32),
131 mouse_delta: (f32, f32),
132 scroll: (f32, f32),
133 touches: HashMap<u64, Touch>,
134 touches_started: Vec<Touch>,
135 touches_ended: Vec<Touch>,
136 touch_emulates_mouse: bool,
138 mouse_finger: Option<u64>,
140 gilrs: Option<Gilrs>,
142 gamepads: HashMap<GamepadId, Gamepad>,
143 rumble: Vec<gilrs::ff::Effect>,
145}
146
147impl Input {
148 pub fn new() -> Self {
149 let mut me = Self::default();
150 match Gilrs::new() {
151 Ok(g) => {
152 for (id, gp) in g.gamepads() {
155 let mut pad = Gamepad::new(id);
156 pad.name = gp.name().to_string();
157 me.gamepads.insert(id, pad);
158 }
159 me.gilrs = Some(g);
160 }
161 Err(e) => log::warn!("gamepad backend unavailable, continuing without it: {e}"),
162 }
163 me
164 }
165
166 pub fn gamepads(&self) -> impl Iterator<Item = &Gamepad> {
168 self.gamepads.values().filter(|g| g.connected)
169 }
170
171 pub fn first_gamepad(&self) -> Option<&Gamepad> {
173 self.gamepads.values().find(|g| g.connected)
174 }
175
176 pub fn gamepad(&self, id: GamepadId) -> Option<&Gamepad> {
178 self.gamepads.get(&id).filter(|g| g.connected)
179 }
180
181 pub fn poll_gamepads(&mut self) {
184 let Some(gilrs) = self.gilrs.as_mut() else {
185 return;
186 };
187 while let Some(Event { id, event, .. }) = gilrs.next_event() {
188 use gilrs::EventType::*;
189 let pad = self.gamepads.entry(id).or_insert_with(|| Gamepad::new(id));
190 match event {
191 Connected => {
192 pad.connected = true;
193 pad.name = gilrs.gamepad(id).name().to_string();
194 }
195 Disconnected | Dropped => {
196 pad.connected = false;
197 pad.held.clear();
198 pad.axes.clear();
199 }
200 ButtonPressed(b, _) => {
201 pad.held.insert(b);
202 pad.pressed.insert(b);
203 }
204 ButtonReleased(b, _) => {
205 pad.held.remove(&b);
206 pad.released.insert(b);
207 }
208 AxisChanged(axis, value, _) => {
209 pad.axes.insert(axis, value);
210 }
211 _ => {}
212 }
213 }
214 }
215
216 pub fn set_rumble(&mut self, id: GamepadId, magnitude: f32, duration_ms: u32) {
219 let Some(gilrs) = self.gilrs.as_mut() else {
220 return;
221 };
222 if !gilrs
223 .connected_gamepad(id)
224 .is_some_and(|g| g.is_ff_supported())
225 {
226 return;
227 }
228 let mag = (magnitude.clamp(0.0, 1.0) * f32::from(u16::MAX)) as u16;
229 let effect = EffectBuilder::new()
230 .add_effect(BaseEffect {
231 kind: BaseEffectType::Strong { magnitude: mag },
232 scheduling: Replay {
233 play_for: Ticks::from_ms(duration_ms),
234 ..Default::default()
235 },
236 envelope: Default::default(),
237 })
238 .gamepads(&[id])
239 .finish(gilrs);
240 if let Ok(effect) = effect {
241 let _ = effect.play();
242 self.rumble.push(effect);
245 if self.rumble.len() > 16 {
246 self.rumble.remove(0);
247 }
248 }
249 }
250
251 pub fn key_held(&self, k: Key) -> bool {
252 self.keys_held.contains(&k)
253 }
254 pub fn key_pressed(&self, k: Key) -> bool {
255 self.keys_pressed.contains(&k)
256 }
257 pub fn key_released(&self, k: Key) -> bool {
258 self.keys_released.contains(&k)
259 }
260
261 pub fn any_key_pressed(&self) -> bool {
265 self.keys_pressed.iter().any(|k| !is_modifier(*k))
266 }
267
268 pub fn keys_pressed(&self) -> impl Iterator<Item = Key> + '_ {
271 self.keys_pressed.iter().copied()
272 }
273
274 pub fn any_mouse_pressed(&self) -> bool {
276 !self.mouse_pressed.is_empty()
277 }
278
279 pub fn any_pressed(&self) -> bool {
300 self.any_key_pressed()
301 || self.any_mouse_pressed()
302 || self.gamepads().any(Gamepad::any_button_pressed)
303 || self.any_touch_pressed()
304 }
305
306 pub fn touches(&self) -> impl Iterator<Item = &Touch> {
308 self.touches.values()
309 }
310
311 pub fn touch(&self, id: u64) -> Option<&Touch> {
313 self.touches.get(&id)
314 }
315
316 pub fn touches_started(&self) -> &[Touch] {
319 &self.touches_started
320 }
321
322 pub fn touches_ended(&self) -> &[Touch] {
326 &self.touches_ended
327 }
328
329 pub fn any_touch_pressed(&self) -> bool {
331 !self.touches_started.is_empty()
332 }
333
334 pub fn set_touch_emulates_mouse(&mut self, on: bool) {
347 self.touch_emulates_mouse = on;
348 if !on && self.mouse_finger.take().is_some() {
349 self.mouse_held.remove(&MouseButton::Left);
350 }
351 }
352
353 pub fn touch_emulates_mouse(&self) -> bool {
355 self.touch_emulates_mouse
356 }
357
358 pub fn mouse_held(&self, b: MouseButton) -> bool {
359 self.mouse_held.contains(&b)
360 }
361 pub fn mouse_pressed(&self, b: MouseButton) -> bool {
362 self.mouse_pressed.contains(&b)
363 }
364 pub fn mouse_released(&self, b: MouseButton) -> bool {
365 self.mouse_released.contains(&b)
366 }
367
368 pub fn mouse_pos(&self) -> (f32, f32) {
369 self.mouse_pos
370 }
371 pub fn mouse_delta(&self) -> (f32, f32) {
372 self.mouse_delta
373 }
374 pub fn scroll(&self) -> (f32, f32) {
375 self.scroll
376 }
377
378 pub fn handle_window_event(&mut self, event: &winit::event::WindowEvent) {
379 use winit::event::{ElementState, MouseScrollDelta, WindowEvent};
380 match event {
381 WindowEvent::KeyboardInput { event, .. } => {
382 let winit::keyboard::PhysicalKey::Code(code) = event.physical_key else {
383 return;
384 };
385 match event.state {
386 ElementState::Pressed => {
387 if self.keys_held.insert(code) {
388 self.keys_pressed.insert(code);
389 }
390 }
391 ElementState::Released => {
392 if self.keys_held.remove(&code) {
393 self.keys_released.insert(code);
394 }
395 }
396 }
397 }
398 WindowEvent::MouseInput { state, button, .. } => {
399 let b = MouseButton::from(*button);
400 match state {
401 ElementState::Pressed => {
402 if self.mouse_held.insert(b) {
403 self.mouse_pressed.insert(b);
404 }
405 }
406 ElementState::Released => {
407 if self.mouse_held.remove(&b) {
408 self.mouse_released.insert(b);
409 }
410 }
411 }
412 }
413 WindowEvent::CursorMoved { position, .. } => {
414 let new = (position.x as f32, position.y as f32);
415 self.mouse_delta.0 += new.0 - self.mouse_pos.0;
416 self.mouse_delta.1 += new.1 - self.mouse_pos.1;
417 self.mouse_pos = new;
418 }
419 WindowEvent::MouseWheel { delta, .. } => match delta {
420 MouseScrollDelta::LineDelta(x, y) => {
421 self.scroll.0 += x;
422 self.scroll.1 += y;
423 }
424 MouseScrollDelta::PixelDelta(p) => {
425 self.scroll.0 += p.x as f32;
426 self.scroll.1 += p.y as f32;
427 }
428 },
429 WindowEvent::Touch(t) => {
430 let pos = (t.location.x as f32, t.location.y as f32);
431 self.handle_touch(t.id, t.phase, pos);
432 }
433 WindowEvent::Focused(false) => {
434 self.keys_held.clear();
435 self.mouse_held.clear();
436 self.touches.clear();
437 self.mouse_finger = None;
438 }
439 _ => {}
440 }
441 }
442
443 fn handle_touch(&mut self, id: u64, phase: winit::event::TouchPhase, pos: (f32, f32)) {
444 use winit::event::TouchPhase;
445 if self.touch_emulates_mouse && !cfg!(target_os = "windows") {
446 self.emulate_mouse(id, phase, pos);
447 }
448 match phase {
449 TouchPhase::Started => {
450 let touch = Touch {
451 id,
452 pos,
453 start_pos: pos,
454 delta: (0.0, 0.0),
455 };
456 self.touches.insert(id, touch);
457 self.touches_started.push(touch);
458 }
459 TouchPhase::Moved => {
460 if let Some(touch) = self.touches.get_mut(&id) {
461 touch.delta.0 += pos.0 - touch.pos.0;
462 touch.delta.1 += pos.1 - touch.pos.1;
463 touch.pos = pos;
464 }
465 }
466 TouchPhase::Ended => {
467 if let Some(mut touch) = self.touches.remove(&id) {
468 touch.delta.0 += pos.0 - touch.pos.0;
469 touch.delta.1 += pos.1 - touch.pos.1;
470 touch.pos = pos;
471 self.touches_ended.push(touch);
472 }
473 }
474 TouchPhase::Cancelled => {
475 self.touches.remove(&id);
476 }
477 }
478 }
479
480 fn emulate_mouse(&mut self, id: u64, phase: winit::event::TouchPhase, pos: (f32, f32)) {
482 use winit::event::TouchPhase;
483 match phase {
484 TouchPhase::Started if self.mouse_finger.is_none() => {
485 self.mouse_finger = Some(id);
486 self.mouse_pos = pos;
488 if self.mouse_held.insert(MouseButton::Left) {
489 self.mouse_pressed.insert(MouseButton::Left);
490 }
491 }
492 _ if self.mouse_finger != Some(id) => {}
493 TouchPhase::Started => {}
494 TouchPhase::Moved | TouchPhase::Ended => {
495 self.mouse_delta.0 += pos.0 - self.mouse_pos.0;
496 self.mouse_delta.1 += pos.1 - self.mouse_pos.1;
497 self.mouse_pos = pos;
498 if phase == TouchPhase::Ended {
499 self.mouse_finger = None;
500 if self.mouse_held.remove(&MouseButton::Left) {
501 self.mouse_released.insert(MouseButton::Left);
502 }
503 }
504 }
505 TouchPhase::Cancelled => {
506 self.mouse_finger = None;
507 self.mouse_held.remove(&MouseButton::Left);
508 }
509 }
510 }
511
512 pub fn end_frame(&mut self) {
514 self.keys_pressed.clear();
515 self.keys_released.clear();
516 self.mouse_pressed.clear();
517 self.mouse_released.clear();
518 self.mouse_delta = (0.0, 0.0);
519 self.scroll = (0.0, 0.0);
520 self.touches_started.clear();
521 self.touches_ended.clear();
522 for touch in self.touches.values_mut() {
523 touch.delta = (0.0, 0.0);
524 }
525 for pad in self.gamepads.values_mut() {
526 pad.pressed.clear();
527 pad.released.clear();
528 }
529 }
530}
531
532fn is_modifier(k: Key) -> bool {
534 matches!(
535 k,
536 Key::ShiftLeft
537 | Key::ShiftRight
538 | Key::ControlLeft
539 | Key::ControlRight
540 | Key::AltLeft
541 | Key::AltRight
542 | Key::SuperLeft
543 | Key::SuperRight
544 | Key::Meta
545 | Key::Hyper
546 | Key::Fn
547 | Key::FnLock
548 )
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554
555 #[test]
559 fn nothing_pressed_by_default() {
560 let input = Input::default();
561 assert!(!input.any_key_pressed());
562 assert!(!input.any_mouse_pressed());
563 assert!(!input.any_pressed());
564 assert_eq!(input.keys_pressed().count(), 0);
565 }
566
567 #[test]
568 fn a_key_counts_but_a_lone_modifier_does_not() {
569 let mut input = Input::default();
570 input.keys_pressed.insert(Key::ShiftLeft);
571 assert!(!input.any_key_pressed());
572 assert!(!input.any_pressed());
573 assert_eq!(input.keys_pressed().collect::<Vec<_>>(), [Key::ShiftLeft]);
574
575 input.keys_pressed.insert(Key::KeyX);
576 assert!(input.any_key_pressed());
577 assert!(input.any_pressed());
578 assert_eq!(input.keys_pressed().count(), 2);
579 }
580
581 #[test]
582 fn mouse_buttons_count() {
583 let mut input = Input::default();
584 input.mouse_pressed.insert(MouseButton::Right);
585 assert!(input.any_mouse_pressed());
586 assert!(input.any_pressed());
587 assert!(!input.any_key_pressed());
588 }
589
590 #[test]
591 fn end_frame_clears_the_edges() {
592 let mut input = Input::default();
593 input.keys_pressed.insert(Key::Space);
594 input.mouse_pressed.insert(MouseButton::Left);
595 input.end_frame();
596 assert!(!input.any_pressed());
597 }
598
599 fn touch(input: &mut Input, id: u64, phase: winit::event::TouchPhase, x: f64, y: f64) {
600 input.handle_window_event(&winit::event::WindowEvent::Touch(winit::event::Touch {
601 device_id: winit::event::DeviceId::dummy(),
602 phase,
603 location: winit::dpi::PhysicalPosition::new(x, y),
604 force: None,
605 id,
606 }));
607 }
608
609 #[test]
610 fn a_touch_goes_down_moves_and_lifts() {
611 use winit::event::TouchPhase::*;
612 let mut input = Input::default();
613
614 touch(&mut input, 7, Started, 10.0, 20.0);
615 assert!(input.any_touch_pressed());
616 assert!(input.any_pressed());
617 assert_eq!(input.touches_started().len(), 1);
618 assert_eq!(input.touch(7).unwrap().pos(), (10.0, 20.0));
619 input.end_frame();
620 assert!(!input.any_pressed());
621 assert_eq!(input.touches().count(), 1);
622
623 touch(&mut input, 7, Moved, 15.0, 20.0);
624 touch(&mut input, 7, Moved, 18.0, 24.0);
625 let t = *input.touch(7).unwrap();
626 assert_eq!(t.pos(), (18.0, 24.0));
627 assert_eq!(t.start_pos(), (10.0, 20.0));
628 assert_eq!(t.delta(), (8.0, 4.0));
629 input.end_frame();
630 assert_eq!(input.touch(7).unwrap().delta(), (0.0, 0.0));
631
632 touch(&mut input, 7, Ended, 20.0, 24.0);
633 assert_eq!(input.touches().count(), 0);
634 let ended = input.touches_ended();
635 assert_eq!(ended.len(), 1);
636 assert_eq!(ended[0].pos(), (20.0, 24.0));
637 assert_eq!(ended[0].start_pos(), (10.0, 20.0));
638 input.end_frame();
639 assert!(input.touches_ended().is_empty());
640 }
641
642 #[test]
643 fn a_tap_within_one_frame_is_started_and_ended() {
644 use winit::event::TouchPhase::*;
645 let mut input = Input::default();
646 touch(&mut input, 1, Started, 5.0, 5.0);
647 touch(&mut input, 1, Ended, 5.0, 5.0);
648 assert!(input.any_touch_pressed());
649 assert_eq!(input.touches_started().len(), 1);
650 assert_eq!(input.touches_ended().len(), 1);
651 assert_eq!(input.touches().count(), 0);
652 }
653
654 #[test]
655 fn fingers_are_tracked_separately() {
656 use winit::event::TouchPhase::*;
657 let mut input = Input::default();
658 touch(&mut input, 1, Started, 0.0, 0.0);
659 touch(&mut input, 2, Started, 100.0, 0.0);
660 touch(&mut input, 2, Moved, 110.0, 0.0);
661 assert_eq!(input.touches().count(), 2);
662 assert_eq!(input.touch(1).unwrap().pos(), (0.0, 0.0));
663 assert_eq!(input.touch(2).unwrap().pos(), (110.0, 0.0));
664 }
665
666 #[test]
667 fn a_cancelled_touch_vanishes_without_ending() {
668 use winit::event::TouchPhase::*;
669 let mut input = Input::default();
670 touch(&mut input, 3, Started, 0.0, 0.0);
671 input.end_frame();
672 touch(&mut input, 3, Cancelled, 0.0, 0.0);
673 assert_eq!(input.touches().count(), 0);
674 assert!(input.touches_ended().is_empty());
675 }
676
677 #[test]
678 fn losing_focus_drops_touches() {
679 let mut input = Input::default();
680 touch(&mut input, 4, winit::event::TouchPhase::Started, 0.0, 0.0);
681 input.handle_window_event(&winit::event::WindowEvent::Focused(false));
682 assert_eq!(input.touches().count(), 0);
683 }
684
685 #[test]
686 fn touches_leave_the_mouse_alone_by_default() {
687 use winit::event::TouchPhase::*;
688 let mut input = Input::default();
689 touch(&mut input, 1, Started, 50.0, 60.0);
690 assert_eq!(input.mouse_pos(), (0.0, 0.0));
691 assert!(!input.mouse_held(MouseButton::Left));
692 assert!(!input.any_mouse_pressed());
693 }
694
695 #[test]
697 #[cfg_attr(target_os = "windows", ignore)]
698 fn the_first_finger_drives_the_mouse() {
699 use winit::event::TouchPhase::*;
700 let mut input = Input::default();
701 input.set_touch_emulates_mouse(true);
702
703 touch(&mut input, 1, Started, 50.0, 60.0);
704 assert!(input.mouse_pressed(MouseButton::Left));
705 assert!(input.mouse_held(MouseButton::Left));
706 assert_eq!(input.mouse_pos(), (50.0, 60.0));
707 assert_eq!(input.mouse_delta(), (0.0, 0.0), "landing is not a movement");
708 input.end_frame();
709
710 touch(&mut input, 2, Started, 300.0, 300.0);
712 touch(&mut input, 2, Moved, 310.0, 300.0);
713 assert!(!input.mouse_pressed(MouseButton::Left));
714 assert_eq!(input.mouse_pos(), (50.0, 60.0));
715
716 touch(&mut input, 1, Moved, 55.0, 70.0);
717 assert_eq!(input.mouse_pos(), (55.0, 70.0));
718 assert_eq!(input.mouse_delta(), (5.0, 10.0));
719 input.end_frame();
720
721 touch(&mut input, 1, Ended, 57.0, 70.0);
722 assert!(input.mouse_released(MouseButton::Left));
723 assert!(!input.mouse_held(MouseButton::Left));
724 assert_eq!(input.mouse_pos(), (57.0, 70.0));
725 input.end_frame();
726
727 touch(&mut input, 2, Moved, 320.0, 300.0);
729 assert_eq!(input.mouse_pos(), (57.0, 70.0));
730 touch(&mut input, 3, Started, 100.0, 100.0);
731 assert!(input.mouse_pressed(MouseButton::Left));
732 assert_eq!(input.mouse_pos(), (100.0, 100.0));
733 }
734
735 #[test]
736 #[cfg_attr(target_os = "windows", ignore)]
737 fn a_cancelled_mouse_finger_is_not_a_click() {
738 use winit::event::TouchPhase::*;
739 let mut input = Input::default();
740 input.set_touch_emulates_mouse(true);
741 touch(&mut input, 1, Started, 0.0, 0.0);
742 input.end_frame();
743 touch(&mut input, 1, Cancelled, 0.0, 0.0);
744 assert!(!input.mouse_held(MouseButton::Left));
745 assert!(!input.mouse_released(MouseButton::Left));
746 touch(&mut input, 2, Started, 5.0, 5.0);
748 assert!(input.mouse_pressed(MouseButton::Left));
749 }
750
751 #[test]
752 #[cfg_attr(target_os = "windows", ignore)]
753 fn turning_emulation_off_lets_go_of_the_button() {
754 let mut input = Input::default();
755 input.set_touch_emulates_mouse(true);
756 touch(&mut input, 1, winit::event::TouchPhase::Started, 0.0, 0.0);
757 input.set_touch_emulates_mouse(false);
758 assert!(!input.mouse_held(MouseButton::Left));
759 touch(&mut input, 1, winit::event::TouchPhase::Ended, 0.0, 0.0);
760 assert!(!input.mouse_released(MouseButton::Left));
761 }
762
763 #[test]
764 fn moves_and_ends_for_unknown_fingers_are_ignored() {
765 use winit::event::TouchPhase::*;
766 let mut input = Input::default();
767 touch(&mut input, 9, Moved, 1.0, 1.0);
768 touch(&mut input, 9, Ended, 1.0, 1.0);
769 assert_eq!(input.touches().count(), 0);
770 assert!(input.touches_ended().is_empty());
771 }
772}