Skip to main content

game_toolkit_input/
lib.rs

1//! Input subsystem: keyboard + mouse + gamepads + touch, with held / just-pressed /
2//! just-released semantics.
3//!
4//! Call [`Input::handle_window_event`] for every `winit::event::WindowEvent` and
5//! [`Input::poll_gamepads`] once per frame before `update`, then [`Input::end_frame`] once
6//! per frame after `update` to clear edge state.
7
8#![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/// Maps window positions (physical pixels) to the space the game draws in, e.g. a
40/// virtual-resolution canvas scaled into the window: `game = (window - origin) * scale`.
41/// The toolkit sets it each frame from the renderer; see [`Input::set_pointer_mapping`].
42#[derive(Copy, Clone, Debug, PartialEq)]
43pub struct PointerMapping {
44    /// Window position of the game space's origin.
45    pub origin: (f32, f32),
46    /// Game pixels per window pixel.
47    pub scale: (f32, f32),
48}
49
50impl PointerMapping {
51    /// Map a window position to game space.
52    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    /// Map a movement in window pixels to game space.
60    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/// One finger on a touchscreen. Positions are in the same space as [`Input::mouse_pos`]:
66/// physical window pixels, or canvas pixels with a virtual resolution (more precisely, mapped
67/// by the [`PointerMapping`] in effect when the event arrived). Obtain via [`Input::touches`], [`Input::touches_started`] or
68/// [`Input::touches_ended`].
69#[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    /// Stable for as long as the finger stays down; the platform may reuse it afterwards.
79    pub fn id(&self) -> u64 {
80        self.id
81    }
82    /// Where the finger is (for an ended touch: where it lifted).
83    pub fn pos(&self) -> (f32, f32) {
84        self.pos
85    }
86    /// Where the finger went down. Compare with [`Touch::pos`] to tell a tap from a swipe.
87    pub fn start_pos(&self) -> (f32, f32) {
88        self.start_pos
89    }
90    /// How far the finger moved this frame.
91    pub fn delta(&self) -> (f32, f32) {
92        self.delta
93    }
94}
95
96/// Per-controller state, with the same held / just-pressed / just-released model as the
97/// keyboard. Obtain via [`Input::gamepads`] or [`Input::first_gamepad`].
98pub 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    /// Any button on this controller went down this frame.
140    pub fn any_button_pressed(&self) -> bool {
141        !self.pressed.is_empty()
142    }
143    /// Axis value in `[-1, 1]` (triggers in `[0, 1]`); `0.0` if never reported.
144    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    /// See [`Input::set_touch_emulates_mouse`].
164    touch_emulates_mouse: bool,
165    /// The finger currently acting as the mouse, while emulation is on.
166    mouse_finger: Option<u64>,
167    /// See [`Input::set_pointer_mapping`].
168    pointer_mapping: Option<PointerMapping>,
169    /// `None` when no gamepad backend could initialize (the toolkit keeps running).
170    gilrs: Option<Gilrs>,
171    gamepads: HashMap<GamepadId, Gamepad>,
172    /// Active rumble effects kept alive for their duration (bounded ring).
173    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                // Seed pads already connected at startup; gilrs only emits `Connected` for
182                // hot-plugs, so without this a present controller is unknown until it moves.
183                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    /// Iterator over currently connected gamepads.
196    pub fn gamepads(&self) -> impl Iterator<Item = &Gamepad> {
197        self.gamepads.values().filter(|g| g.connected)
198    }
199
200    /// The first connected gamepad, if any. Convenient for single-player input.
201    pub fn first_gamepad(&self) -> Option<&Gamepad> {
202        self.gamepads.values().find(|g| g.connected)
203    }
204
205    /// Look up a connected gamepad by id.
206    pub fn gamepad(&self, id: GamepadId) -> Option<&Gamepad> {
207        self.gamepads.get(&id).filter(|g| g.connected)
208    }
209
210    /// Drain pending gamepad events into per-pad state, tracking hot-plug. Call once per
211    /// frame before `update`.
212    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    /// Rumble `id` at `magnitude` (`0..=1`) for `duration_ms`. No-op when the pad has no
246    /// force feedback or no backend is available.
247    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            // Keep the handle alive so the effect is not dropped (and stopped) immediately;
272            // bound the buffer so finished effects are eventually released.
273            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    /// Any key other than a modifier (Shift, Ctrl, Alt, Super, ...) went down this frame: the
291    /// classic "press any key". Modifiers alone don't count, so Alt-Tabbing away or holding
292    /// Shift doesn't skip a screen; [`Input::keys_pressed`] still reports them.
293    pub fn any_key_pressed(&self) -> bool {
294        self.keys_pressed.iter().any(|k| !is_modifier(*k))
295    }
296
297    /// Every key that went down this frame, modifiers included, in no particular order (for
298    /// "press a key to bind it", cheat codes, typing a name).
299    pub fn keys_pressed(&self) -> impl Iterator<Item = Key> + '_ {
300        self.keys_pressed.iter().copied()
301    }
302
303    /// Any mouse button went down this frame.
304    pub fn any_mouse_pressed(&self) -> bool {
305        !self.mouse_pressed.is_empty()
306    }
307
308    /// Anything at all went down this frame: a non-modifier key ([`Input::any_key_pressed`]),
309    /// a mouse button, a button on any connected gamepad, or a finger touching the screen
310    /// ([`Input::any_touch_pressed`]). For "press any key to continue", skipping intros, and
311    /// waking an idle game.
312    ///
313    /// ```
314    /// use game_toolkit_input::Input;
315    ///
316    /// enum Screen { Title, Playing }
317    ///
318    /// fn update(input: &Input, screen: &mut Screen) {
319    ///     if matches!(screen, Screen::Title) && input.any_pressed() {
320    ///         *screen = Screen::Playing;
321    ///     }
322    /// }
323    ///
324    /// let mut screen = Screen::Title;
325    /// update(&Input::default(), &mut screen); // nothing pressed yet
326    /// assert!(matches!(screen, Screen::Title));
327    /// ```
328    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    /// Fingers currently on the screen, in no particular order.
336    pub fn touches(&self) -> impl Iterator<Item = &Touch> {
337        self.touches.values()
338    }
339
340    /// The finger with this [`Touch::id`], if it is still down.
341    pub fn touch(&self, id: u64) -> Option<&Touch> {
342        self.touches.get(&id)
343    }
344
345    /// Fingers that went down this frame, in event order. A tap that starts and ends within
346    /// one frame shows up here and in [`Input::touches_ended`], but not in [`Input::touches`].
347    pub fn touches_started(&self) -> &[Touch] {
348        &self.touches_started
349    }
350
351    /// Fingers that lifted this frame, in event order. Touches the platform cancels (e.g. the
352    /// browser took over the gesture, or the window lost focus) vanish from
353    /// [`Input::touches`] without appearing here, so a cancelled touch never counts as a tap.
354    pub fn touches_ended(&self) -> &[Touch] {
355        &self.touches_ended
356    }
357
358    /// Any finger went down this frame.
359    pub fn any_touch_pressed(&self) -> bool {
360        !self.touches_started.is_empty()
361    }
362
363    /// Let the first finger on a touchscreen act as the mouse, so a game written for the
364    /// mouse also works on a phone. Off by default (also settable through
365    /// `AppConfig::touch_emulates_mouse`).
366    ///
367    /// While on, the first finger down moves [`Input::mouse_pos`] to where it touched (without
368    /// a [`Input::mouse_delta`] jump), presses and holds [`MouseButton::Left`], drags the
369    /// cursor along, and releases the button where it lifts. Other fingers are ignored until
370    /// it lifts. A cancelled touch lets go of the button without a [`Input::mouse_released`]
371    /// edge, so it never counts as a click. [`Input::touches`] and friends keep working.
372    ///
373    /// No effect on Windows: the OS already turns touches into mouse events there, which
374    /// winit passes on, so emulating them again would press twice.
375    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    /// Whether [`Input::set_touch_emulates_mouse`] is on.
383    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    /// Cursor position: physical window pixels, or canvas pixels when a virtual resolution
398    /// is set (outside `0..size` while the cursor is over the border). See
399    /// [`Input::mouse_pos_window`] for the raw window position.
400    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    /// Cursor position in physical window pixels, ignoring any virtual resolution; for
407    /// overlays drawn onto the window itself.
408    pub fn mouse_pos_window(&self) -> (f32, f32) {
409        self.mouse_pos
410    }
411    /// How far the cursor moved this frame, in the same space as [`Input::mouse_pos`].
412    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    /// Report the mouse and touches in another space than window pixels: with `Some`,
420    /// [`Input::mouse_pos`], [`Input::mouse_delta`] and new [`Touch`] positions are mapped
421    /// through it. The toolkit calls this every frame with the virtual-resolution canvas's
422    /// placement, so games don't need to; `None` (the default) reports window pixels.
423    pub fn set_pointer_mapping(&mut self, mapping: Option<PointerMapping>) {
424        self.pointer_mapping = mapping;
425    }
426
427    /// The mapping set by [`Input::set_pointer_mapping`].
428    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        // The emulated mouse keeps window pixels like the real one (mapped when read).
503        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    /// Drive the mouse from the first finger; see [`Input::set_touch_emulates_mouse`].
543    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                // Jump without a delta: a finger landing elsewhere isn't a mouse movement.
549                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    /// Call once per frame after `update` to clear edge / delta state.
575    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
594/// Keys that only modify other keys, ignored by [`Input::any_key_pressed`].
595fn 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    // winit's `KeyEvent` can't be built outside winit, so these drive the edge sets directly,
618    // the way `handle_window_event` and `poll_gamepads` fill them.
619
620    #[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    // Windows promotes touches to mouse events itself, so emulation is off there.
758    #[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        // A second finger doesn't take over.
773        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        // Finger 2 is still down, but only a new finger becomes the mouse.
790        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        // The next finger can take over.
809        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}