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/// One finger on a touchscreen. Positions are in physical pixels, the same space as
40/// [`Input::mouse_pos`]. Obtain via [`Input::touches`], [`Input::touches_started`] or
41/// [`Input::touches_ended`].
42#[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    /// Stable for as long as the finger stays down; the platform may reuse it afterwards.
52    pub fn id(&self) -> u64 {
53        self.id
54    }
55    /// Where the finger is (for an ended touch: where it lifted).
56    pub fn pos(&self) -> (f32, f32) {
57        self.pos
58    }
59    /// Where the finger went down. Compare with [`Touch::pos`] to tell a tap from a swipe.
60    pub fn start_pos(&self) -> (f32, f32) {
61        self.start_pos
62    }
63    /// How far the finger moved this frame.
64    pub fn delta(&self) -> (f32, f32) {
65        self.delta
66    }
67}
68
69/// Per-controller state, with the same held / just-pressed / just-released model as the
70/// keyboard. Obtain via [`Input::gamepads`] or [`Input::first_gamepad`].
71pub 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    /// Any button on this controller went down this frame.
113    pub fn any_button_pressed(&self) -> bool {
114        !self.pressed.is_empty()
115    }
116    /// Axis value in `[-1, 1]` (triggers in `[0, 1]`); `0.0` if never reported.
117    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    /// See [`Input::set_touch_emulates_mouse`].
137    touch_emulates_mouse: bool,
138    /// The finger currently acting as the mouse, while emulation is on.
139    mouse_finger: Option<u64>,
140    /// `None` when no gamepad backend could initialize (the toolkit keeps running).
141    gilrs: Option<Gilrs>,
142    gamepads: HashMap<GamepadId, Gamepad>,
143    /// Active rumble effects kept alive for their duration (bounded ring).
144    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                // Seed pads already connected at startup; gilrs only emits `Connected` for
153                // hot-plugs, so without this a present controller is unknown until it moves.
154                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    /// Iterator over currently connected gamepads.
167    pub fn gamepads(&self) -> impl Iterator<Item = &Gamepad> {
168        self.gamepads.values().filter(|g| g.connected)
169    }
170
171    /// The first connected gamepad, if any. Convenient for single-player input.
172    pub fn first_gamepad(&self) -> Option<&Gamepad> {
173        self.gamepads.values().find(|g| g.connected)
174    }
175
176    /// Look up a connected gamepad by id.
177    pub fn gamepad(&self, id: GamepadId) -> Option<&Gamepad> {
178        self.gamepads.get(&id).filter(|g| g.connected)
179    }
180
181    /// Drain pending gamepad events into per-pad state, tracking hot-plug. Call once per
182    /// frame before `update`.
183    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    /// Rumble `id` at `magnitude` (`0..=1`) for `duration_ms`. No-op when the pad has no
217    /// force feedback or no backend is available.
218    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            // Keep the handle alive so the effect is not dropped (and stopped) immediately;
243            // bound the buffer so finished effects are eventually released.
244            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    /// Any key other than a modifier (Shift, Ctrl, Alt, Super, ...) went down this frame: the
262    /// classic "press any key". Modifiers alone don't count, so Alt-Tabbing away or holding
263    /// Shift doesn't skip a screen; [`Input::keys_pressed`] still reports them.
264    pub fn any_key_pressed(&self) -> bool {
265        self.keys_pressed.iter().any(|k| !is_modifier(*k))
266    }
267
268    /// Every key that went down this frame, modifiers included, in no particular order (for
269    /// "press a key to bind it", cheat codes, typing a name).
270    pub fn keys_pressed(&self) -> impl Iterator<Item = Key> + '_ {
271        self.keys_pressed.iter().copied()
272    }
273
274    /// Any mouse button went down this frame.
275    pub fn any_mouse_pressed(&self) -> bool {
276        !self.mouse_pressed.is_empty()
277    }
278
279    /// Anything at all went down this frame: a non-modifier key ([`Input::any_key_pressed`]),
280    /// a mouse button, a button on any connected gamepad, or a finger touching the screen
281    /// ([`Input::any_touch_pressed`]). For "press any key to continue", skipping intros, and
282    /// waking an idle game.
283    ///
284    /// ```
285    /// use game_toolkit_input::Input;
286    ///
287    /// enum Screen { Title, Playing }
288    ///
289    /// fn update(input: &Input, screen: &mut Screen) {
290    ///     if matches!(screen, Screen::Title) && input.any_pressed() {
291    ///         *screen = Screen::Playing;
292    ///     }
293    /// }
294    ///
295    /// let mut screen = Screen::Title;
296    /// update(&Input::default(), &mut screen); // nothing pressed yet
297    /// assert!(matches!(screen, Screen::Title));
298    /// ```
299    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    /// Fingers currently on the screen, in no particular order.
307    pub fn touches(&self) -> impl Iterator<Item = &Touch> {
308        self.touches.values()
309    }
310
311    /// The finger with this [`Touch::id`], if it is still down.
312    pub fn touch(&self, id: u64) -> Option<&Touch> {
313        self.touches.get(&id)
314    }
315
316    /// Fingers that went down this frame, in event order. A tap that starts and ends within
317    /// one frame shows up here and in [`Input::touches_ended`], but not in [`Input::touches`].
318    pub fn touches_started(&self) -> &[Touch] {
319        &self.touches_started
320    }
321
322    /// Fingers that lifted this frame, in event order. Touches the platform cancels (e.g. the
323    /// browser took over the gesture, or the window lost focus) vanish from
324    /// [`Input::touches`] without appearing here, so a cancelled touch never counts as a tap.
325    pub fn touches_ended(&self) -> &[Touch] {
326        &self.touches_ended
327    }
328
329    /// Any finger went down this frame.
330    pub fn any_touch_pressed(&self) -> bool {
331        !self.touches_started.is_empty()
332    }
333
334    /// Let the first finger on a touchscreen act as the mouse, so a game written for the
335    /// mouse also works on a phone. Off by default (also settable through
336    /// `AppConfig::touch_emulates_mouse`).
337    ///
338    /// While on, the first finger down moves [`Input::mouse_pos`] to where it touched (without
339    /// a [`Input::mouse_delta`] jump), presses and holds [`MouseButton::Left`], drags the
340    /// cursor along, and releases the button where it lifts. Other fingers are ignored until
341    /// it lifts. A cancelled touch lets go of the button without a [`Input::mouse_released`]
342    /// edge, so it never counts as a click. [`Input::touches`] and friends keep working.
343    ///
344    /// No effect on Windows: the OS already turns touches into mouse events there, which
345    /// winit passes on, so emulating them again would press twice.
346    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    /// Whether [`Input::set_touch_emulates_mouse`] is on.
354    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    /// Drive the mouse from the first finger; see [`Input::set_touch_emulates_mouse`].
481    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                // Jump without a delta: a finger landing elsewhere isn't a mouse movement.
487                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    /// Call once per frame after `update` to clear edge / delta state.
513    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
532/// Keys that only modify other keys, ignored by [`Input::any_key_pressed`].
533fn 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    // winit's `KeyEvent` can't be built outside winit, so these drive the edge sets directly,
556    // the way `handle_window_event` and `poll_gamepads` fill them.
557
558    #[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    // Windows promotes touches to mouse events itself, so emulation is off there.
696    #[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        // A second finger doesn't take over.
711        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        // Finger 2 is still down, but only a new finger becomes the mouse.
728        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        // The next finger can take over.
747        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}