Skip to main content

spitfire_input/
lib.rs

1use gilrs::{Event as GamepadEvent, EventType as GamepadEventType, Gilrs};
2#[cfg(not(target_arch = "wasm32"))]
3use glutin::event::{ElementState, MouseScrollDelta, TouchPhase, WindowEvent};
4use std::{
5    borrow::Cow,
6    cmp::Ordering,
7    collections::HashMap,
8    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
9};
10#[cfg(target_arch = "wasm32")]
11use winit::event::{ElementState, MouseScrollDelta, TouchPhase, WindowEvent};
12
13pub use gilrs::{Axis as GamepadAxis, Button as GamepadButton, GamepadId};
14#[cfg(not(target_arch = "wasm32"))]
15pub use glutin::event::{MouseButton, VirtualKeyCode};
16#[cfg(target_arch = "wasm32")]
17pub use winit::event::{MouseButton, VirtualKeyCode};
18
19#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
20pub enum InputConsume {
21    #[default]
22    None,
23    Hit,
24    All,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum VirtualAction {
29    KeyButton(VirtualKeyCode),
30    MouseButton(MouseButton),
31    Axis(u32),
32    GamepadButton(GamepadButton),
33    GamepadAxis(GamepadAxis),
34    Touch,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum VirtualAxis {
39    KeyButton(VirtualKeyCode),
40    MousePositionX,
41    MousePositionY,
42    MouseWheelX,
43    MouseWheelY,
44    MouseButton(MouseButton),
45    Axis(u32),
46    GamepadButton(GamepadButton),
47    GamepadAxis(GamepadAxis),
48    TouchX,
49    TouchY,
50}
51
52#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
53pub enum InputAction {
54    #[default]
55    Idle,
56    Pressed,
57    Hold,
58    Released,
59}
60
61impl InputAction {
62    pub fn change(self, hold: bool) -> Self {
63        match (self, hold) {
64            (Self::Idle, true) | (Self::Released, true) => Self::Pressed,
65            (Self::Pressed, false) | (Self::Hold, false) => Self::Released,
66            _ => self,
67        }
68    }
69
70    pub fn update(self) -> Self {
71        match self {
72            Self::Pressed => Self::Hold,
73            Self::Released => Self::Idle,
74            _ => self,
75        }
76    }
77
78    pub fn is_idle(self) -> bool {
79        matches!(self, Self::Idle)
80    }
81
82    pub fn is_pressed(self) -> bool {
83        matches!(self, Self::Pressed)
84    }
85
86    pub fn is_hold(self) -> bool {
87        matches!(self, Self::Hold)
88    }
89
90    pub fn is_released(self) -> bool {
91        matches!(self, Self::Released)
92    }
93
94    pub fn is_up(self) -> bool {
95        matches!(self, Self::Idle | Self::Released)
96    }
97
98    pub fn is_down(self) -> bool {
99        matches!(self, Self::Pressed | Self::Hold)
100    }
101
102    pub fn is_changing(self) -> bool {
103        matches!(self, Self::Pressed | Self::Released)
104    }
105
106    pub fn is_continuing(self) -> bool {
107        matches!(self, Self::Idle | Self::Hold)
108    }
109
110    pub fn to_scalar(self, falsy: f32, truthy: f32) -> f32 {
111        if self.is_down() { truthy } else { falsy }
112    }
113}
114
115#[derive(Debug, Default, Clone, Copy, PartialEq)]
116pub struct InputAxis(pub f32);
117
118impl InputAxis {
119    pub fn threshold(self, value: f32) -> bool {
120        self.0 >= value
121    }
122}
123
124#[derive(Debug, Default, Clone)]
125pub struct InputRef<T: Default>(Arc<RwLock<T>>);
126
127impl<T: Default> InputRef<T> {
128    pub fn new(data: T) -> Self {
129        Self(Arc::new(RwLock::new(data)))
130    }
131
132    pub fn read(&'_ self) -> Option<RwLockReadGuard<'_, T>> {
133        self.0.read().ok()
134    }
135
136    pub fn write(&'_ self) -> Option<RwLockWriteGuard<'_, T>> {
137        self.0.write().ok()
138    }
139
140    pub fn get(&self) -> T
141    where
142        T: Clone,
143    {
144        self.read().map(|value| value.clone()).unwrap_or_default()
145    }
146
147    pub fn set(&self, value: T) {
148        if let Some(mut data) = self.write() {
149            *data = value;
150        }
151    }
152
153    pub fn ptr_eq(&self, other: &Self) -> bool {
154        Arc::ptr_eq(&self.0, &other.0)
155    }
156}
157
158pub type InputActionRef = InputRef<InputAction>;
159pub type InputAxisRef = InputRef<InputAxis>;
160pub type InputCharactersRef = InputRef<InputCharacters>;
161pub type InputMappingRef = InputRef<InputMapping>;
162
163#[derive(Debug, Default, Clone)]
164pub enum InputActionOrAxisRef {
165    #[default]
166    None,
167    Action(InputActionRef),
168    Axis(InputAxisRef),
169}
170
171impl InputActionOrAxisRef {
172    pub fn is_none(&self) -> bool {
173        matches!(self, Self::None)
174    }
175
176    pub fn is_some(&self) -> bool {
177        !self.is_none()
178    }
179
180    pub fn get_scalar(&self, falsy: f32, truthy: f32) -> f32 {
181        match self {
182            Self::None => falsy,
183            Self::Action(action) => action.get().to_scalar(falsy, truthy),
184            Self::Axis(axis) => axis.get().0,
185        }
186    }
187
188    pub fn threshold(&self, value: f32) -> bool {
189        match self {
190            Self::None => false,
191            Self::Action(action) => action.get().is_down(),
192            Self::Axis(axis) => axis.get().threshold(value),
193        }
194    }
195}
196
197impl From<InputActionRef> for InputActionOrAxisRef {
198    fn from(value: InputActionRef) -> Self {
199        Self::Action(value)
200    }
201}
202
203impl From<InputAxisRef> for InputActionOrAxisRef {
204    fn from(value: InputAxisRef) -> Self {
205        Self::Axis(value)
206    }
207}
208
209pub struct InputCombinator<T> {
210    mapper: Box<dyn Fn() -> T + Send + Sync>,
211}
212
213impl<T: Default> Default for InputCombinator<T> {
214    fn default() -> Self {
215        Self::new(|| T::default())
216    }
217}
218
219impl<T> InputCombinator<T> {
220    pub fn new(mapper: impl Fn() -> T + Send + Sync + 'static) -> Self {
221        Self {
222            mapper: Box::new(mapper),
223        }
224    }
225
226    pub fn get(&self) -> T {
227        (self.mapper)()
228    }
229}
230
231#[derive(Default)]
232pub struct CardinalInputCombinator(pub InputCombinator<[f32; 2]>);
233
234impl CardinalInputCombinator {
235    pub fn new(
236        left: impl Into<InputActionOrAxisRef>,
237        right: impl Into<InputActionOrAxisRef>,
238        up: impl Into<InputActionOrAxisRef>,
239        down: impl Into<InputActionOrAxisRef>,
240    ) -> Self {
241        let left = left.into();
242        let right = right.into();
243        let up = up.into();
244        let down = down.into();
245        Self(InputCombinator::new(move || {
246            let left = left.get_scalar(0.0, -1.0);
247            let right = right.get_scalar(0.0, 1.0);
248            let up = up.get_scalar(0.0, -1.0);
249            let down = down.get_scalar(0.0, 1.0);
250            [left + right, up + down]
251        }))
252    }
253
254    pub fn get(&self) -> [f32; 2] {
255        self.0.get()
256    }
257}
258
259#[derive(Default)]
260pub struct DirectionInputCombinator(pub InputCombinator<[f32; 2]>);
261
262impl DirectionInputCombinator {
263    pub fn new(
264        left: InputActionRef,
265        right: InputActionRef,
266        up: InputActionRef,
267        down: InputActionRef,
268        horizontal: InputAxisRef,
269        vertical: InputAxisRef,
270    ) -> Self {
271        Self(InputCombinator::new(move || {
272            let left = left.get().to_scalar(0.0, -1.0);
273            let right = right.get().to_scalar(0.0, 1.0);
274            let up = up.get().to_scalar(0.0, -1.0);
275            let down = down.get().to_scalar(0.0, 1.0);
276            let horizontal = horizontal.get().0;
277            let vertical = vertical.get().0;
278            let x = left + right + horizontal;
279            let y = up + down + vertical;
280            let length = (x * x + y * y).sqrt();
281            if length > 1.0 {
282                [x / length, y / length]
283            } else {
284                [x, y]
285            }
286        }))
287    }
288
289    pub fn get(&self) -> [f32; 2] {
290        self.0.get()
291    }
292}
293
294#[derive(Default)]
295pub struct DualInputCombinator(pub InputCombinator<f32>);
296
297impl DualInputCombinator {
298    pub fn new(
299        negative: impl Into<InputActionOrAxisRef>,
300        positive: impl Into<InputActionOrAxisRef>,
301    ) -> Self {
302        let negative = negative.into();
303        let positive = positive.into();
304        Self(InputCombinator::new(move || {
305            let negative = negative.get_scalar(0.0, -1.0);
306            let positive = positive.get_scalar(0.0, 1.0);
307            negative + positive
308        }))
309    }
310
311    pub fn get(&self) -> f32 {
312        self.0.get()
313    }
314}
315
316pub struct ArrayInputCombinator<const N: usize>(pub InputCombinator<[f32; N]>);
317
318impl<const N: usize> Default for ArrayInputCombinator<N> {
319    fn default() -> Self {
320        Self(InputCombinator::new(|| [0.0; N]))
321    }
322}
323
324impl<const N: usize> ArrayInputCombinator<N> {
325    pub fn new(inputs: [impl Into<InputActionOrAxisRef>; N]) -> Self {
326        let items: [InputActionOrAxisRef; N] = inputs.map(|input| input.into());
327        Self(InputCombinator::new(move || {
328            std::array::from_fn(|index| items[index].get_scalar(0.0, 1.0))
329        }))
330    }
331
332    pub fn get(&self) -> [f32; N] {
333        self.0.get()
334    }
335}
336
337#[derive(Debug, Default, Clone)]
338pub struct InputCharacters {
339    characters: String,
340}
341
342impl InputCharacters {
343    pub fn read(&self) -> &str {
344        &self.characters
345    }
346
347    pub fn write(&mut self) -> &mut String {
348        &mut self.characters
349    }
350
351    pub fn take(&mut self) -> String {
352        std::mem::take(&mut self.characters)
353    }
354}
355
356#[derive(Default, Clone)]
357pub struct InputMapping {
358    pub actions: HashMap<VirtualAction, InputActionRef>,
359    pub axes: HashMap<VirtualAxis, InputAxisRef>,
360    pub consume: InputConsume,
361    pub layer: isize,
362    pub name: Cow<'static, str>,
363    pub gamepad: Option<GamepadId>,
364    pub validator: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
365}
366
367impl InputMapping {
368    pub fn action(mut self, id: VirtualAction, action: InputActionRef) -> Self {
369        self.actions.insert(id, action);
370        self
371    }
372
373    pub fn axis(mut self, id: VirtualAxis, axis: InputAxisRef) -> Self {
374        self.axes.insert(id, axis);
375        self
376    }
377
378    pub fn consume(mut self, consume: InputConsume) -> Self {
379        self.consume = consume;
380        self
381    }
382
383    pub fn layer(mut self, value: isize) -> Self {
384        self.layer = value;
385        self
386    }
387
388    pub fn name(mut self, value: impl Into<Cow<'static, str>>) -> Self {
389        self.name = value.into();
390        self
391    }
392
393    pub fn gamepad(mut self, gamepad: GamepadId) -> Self {
394        self.gamepad = Some(gamepad);
395        self
396    }
397
398    pub fn validator(mut self, validator: impl Fn() -> bool + Send + Sync + 'static) -> Self {
399        self.validator = Some(Arc::new(validator));
400        self
401    }
402}
403
404impl From<InputMapping> for InputMappingRef {
405    fn from(value: InputMapping) -> Self {
406        Self::new(value)
407    }
408}
409
410impl std::fmt::Debug for InputMapping {
411    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412        f.debug_struct("InputMapping")
413            .field("actions", &self.actions.keys().collect::<Vec<_>>())
414            .field("axes", &self.axes.keys().collect::<Vec<_>>())
415            .field("consume", &self.consume)
416            .field("layer", &self.layer)
417            .field("name", &self.name)
418            .field("gamepad", &self.gamepad)
419            .finish_non_exhaustive()
420    }
421}
422
423#[derive(Debug)]
424pub struct InputContext {
425    pub mouse_wheel_line_scale: f32,
426    /// [left, top, right, bottom]
427    pub touch_area_margin: [f64; 4],
428    mappings_stack: Vec<InputMappingRef>,
429    characters: InputCharactersRef,
430    gamepads: Option<Gilrs>,
431    active_touch: Option<u64>,
432    window_size: [f64; 2],
433}
434
435impl Default for InputContext {
436    fn default() -> Self {
437        Self {
438            mouse_wheel_line_scale: Self::default_mouse_wheel_line_scale(),
439            touch_area_margin: Self::default_touch_area_margin(),
440            mappings_stack: Default::default(),
441            characters: Default::default(),
442            gamepads: None,
443            active_touch: None,
444            window_size: Default::default(),
445        }
446    }
447}
448
449impl Clone for InputContext {
450    fn clone(&self) -> Self {
451        Self {
452            mouse_wheel_line_scale: self.mouse_wheel_line_scale,
453            touch_area_margin: self.touch_area_margin,
454            mappings_stack: self.mappings_stack.clone(),
455            characters: self.characters.clone(),
456            gamepads: None,
457            active_touch: self.active_touch,
458            window_size: self.window_size,
459        }
460    }
461}
462
463impl InputContext {
464    fn default_mouse_wheel_line_scale() -> f32 {
465        10.0
466    }
467
468    fn default_touch_area_margin() -> [f64; 4] {
469        [0.0, 0.0, 0.0, 0.0]
470    }
471
472    pub fn with_gamepads(mut self) -> Self {
473        self.gamepads = Gilrs::new().ok();
474        self
475    }
476
477    pub fn with_gamepads_custom(mut self, gamepads: Gilrs) -> Self {
478        self.gamepads = Some(gamepads);
479        self
480    }
481
482    pub fn gamepads(&self) -> Option<&Gilrs> {
483        self.gamepads.as_ref()
484    }
485
486    pub fn gamepads_mut(&mut self) -> Option<&mut Gilrs> {
487        self.gamepads.as_mut()
488    }
489
490    pub fn push_mapping(&mut self, mapping: impl Into<InputMappingRef>) -> InputMappingRef {
491        let mapping = mapping.into();
492        let layer = mapping.read().unwrap().layer;
493        let index = self
494            .mappings_stack
495            .binary_search_by(|mapping| {
496                mapping
497                    .read()
498                    .unwrap()
499                    .layer
500                    .cmp(&layer)
501                    .then(Ordering::Less)
502            })
503            .unwrap_or_else(|index| index);
504        self.mappings_stack.insert(index, mapping.clone());
505        mapping
506    }
507
508    pub fn pop_mapping(&mut self) -> Option<InputMappingRef> {
509        self.mappings_stack.pop()
510    }
511
512    pub fn top_mapping(&self) -> Option<&InputMappingRef> {
513        self.mappings_stack.last()
514    }
515
516    pub fn remove_mapping(&mut self, mapping: &InputMappingRef) -> Option<InputMappingRef> {
517        self.mappings_stack
518            .iter()
519            .position(|m| mapping.ptr_eq(m))
520            .map(|index| self.mappings_stack.remove(index))
521    }
522
523    pub fn stack(&self) -> impl Iterator<Item = &InputMappingRef> {
524        self.mappings_stack.iter()
525    }
526
527    pub fn stack_mut(&mut self) -> impl Iterator<Item = &mut InputMappingRef> {
528        self.mappings_stack.iter_mut()
529    }
530
531    pub fn characters(&self) -> InputCharactersRef {
532        self.characters.clone()
533    }
534
535    pub fn maintain(&mut self) {
536        for mapping in &mut self.mappings_stack {
537            if let Some(mut mapping) = mapping.write() {
538                for action in mapping.actions.values_mut() {
539                    if let Some(mut action) = action.write() {
540                        *action = action.update();
541                    }
542                }
543                for (id, axis) in &mut mapping.axes {
544                    if let VirtualAxis::MouseWheelX | VirtualAxis::MouseWheelY = id
545                        && let Some(mut axis) = axis.write()
546                    {
547                        axis.0 = 0.0;
548                    }
549                }
550            }
551        }
552
553        let validity = self
554            .mappings_stack
555            .iter()
556            .filter(|mapping| {
557                mapping.read().is_some_and(|mapping| {
558                    mapping
559                        .validator
560                        .as_ref()
561                        .is_none_or(|validator| validator())
562                })
563            })
564            .cloned()
565            .collect::<Vec<_>>();
566
567        if let Some(gamepads) = self.gamepads.as_mut() {
568            while let Some(GamepadEvent { id, event, .. }) = gamepads.next_event() {
569                match event {
570                    GamepadEventType::ButtonPressed(info, ..) => {
571                        for mapping in self.mappings_stack.iter().rev() {
572                            if !validity.iter().any(|m| m.ptr_eq(mapping)) {
573                                continue;
574                            }
575                            if let Some(mapping) = mapping.read() {
576                                if !mapping.gamepad.map(|gamepad| gamepad == id).unwrap_or(true) {
577                                    continue;
578                                }
579                                let mut consume = mapping.consume == InputConsume::All;
580                                for (id, data) in &mapping.actions {
581                                    if let VirtualAction::GamepadButton(button) = id
582                                        && *button == info
583                                        && let Some(mut data) = data.write()
584                                    {
585                                        *data = data.change(true);
586                                        if mapping.consume == InputConsume::Hit {
587                                            consume = true;
588                                        }
589                                    }
590                                }
591                                for (id, data) in &mapping.axes {
592                                    if let VirtualAxis::GamepadButton(button) = id
593                                        && *button == info
594                                        && let Some(mut data) = data.write()
595                                    {
596                                        data.0 = 1.0;
597                                        if mapping.consume == InputConsume::Hit {
598                                            consume = true;
599                                        }
600                                    }
601                                }
602                                if consume {
603                                    break;
604                                }
605                            }
606                        }
607                    }
608                    GamepadEventType::ButtonReleased(info, ..) => {
609                        for mapping in self.mappings_stack.iter().rev() {
610                            if !validity.iter().any(|m| m.ptr_eq(mapping)) {
611                                continue;
612                            }
613                            if let Some(mapping) = mapping.read() {
614                                if !mapping.gamepad.map(|gamepad| gamepad == id).unwrap_or(true) {
615                                    continue;
616                                }
617                                let mut consume = mapping.consume == InputConsume::All;
618                                for (id, data) in &mapping.actions {
619                                    if let VirtualAction::GamepadButton(button) = id
620                                        && *button == info
621                                        && let Some(mut data) = data.write()
622                                    {
623                                        *data = data.change(false);
624                                        if mapping.consume == InputConsume::Hit {
625                                            consume = true;
626                                        }
627                                    }
628                                }
629                                for (id, data) in &mapping.axes {
630                                    if let VirtualAxis::GamepadButton(button) = id
631                                        && *button == info
632                                        && let Some(mut data) = data.write()
633                                    {
634                                        data.0 = 0.0;
635                                        if mapping.consume == InputConsume::Hit {
636                                            consume = true;
637                                        }
638                                    }
639                                }
640                                if consume {
641                                    break;
642                                }
643                            }
644                        }
645                    }
646                    GamepadEventType::AxisChanged(info, value, ..) => {
647                        for mapping in self.mappings_stack.iter().rev() {
648                            if !validity.iter().any(|m| m.ptr_eq(mapping)) {
649                                continue;
650                            }
651                            if let Some(mapping) = mapping.read() {
652                                let mut consume = mapping.consume == InputConsume::All;
653                                for (id, data) in &mapping.actions {
654                                    if let VirtualAction::GamepadAxis(axis) = id
655                                        && *axis == info
656                                        && let Some(mut data) = data.write()
657                                    {
658                                        *data = data.change(value > 0.5);
659                                        if mapping.consume == InputConsume::Hit {
660                                            consume = true;
661                                        }
662                                    }
663                                }
664                                for (id, data) in &mapping.axes {
665                                    if let VirtualAxis::GamepadAxis(axis) = id
666                                        && *axis == info
667                                        && let Some(mut data) = data.write()
668                                    {
669                                        data.0 = value;
670                                        if mapping.consume == InputConsume::Hit {
671                                            consume = true;
672                                        }
673                                    }
674                                }
675                                if consume {
676                                    break;
677                                }
678                            }
679                        }
680                    }
681                    _ => {}
682                }
683            }
684            gamepads.inc();
685        }
686    }
687
688    pub fn on_event(&mut self, event: &WindowEvent) {
689        let validity = self
690            .mappings_stack
691            .iter()
692            .filter(|mapping| {
693                mapping.read().is_some_and(|mapping| {
694                    mapping
695                        .validator
696                        .as_ref()
697                        .is_none_or(|validator| validator())
698                })
699            })
700            .cloned()
701            .collect::<Vec<_>>();
702
703        match event {
704            WindowEvent::Resized(size) => {
705                self.window_size = [size.width as _, size.height as _];
706            }
707            WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
708                self.window_size = [new_inner_size.width as _, new_inner_size.height as _];
709            }
710            WindowEvent::ReceivedCharacter(character) => {
711                if let Some(mut characters) = self.characters.write() {
712                    characters.characters.push(*character);
713                }
714            }
715            WindowEvent::KeyboardInput { input, .. } => {
716                if let Some(key) = input.virtual_keycode {
717                    for mapping in self.mappings_stack.iter().rev() {
718                        if !validity.iter().any(|m| m.ptr_eq(mapping)) {
719                            continue;
720                        }
721                        if let Some(mapping) = mapping.read() {
722                            let mut consume = mapping.consume == InputConsume::All;
723                            for (id, data) in &mapping.actions {
724                                if let VirtualAction::KeyButton(button) = id
725                                    && *button == key
726                                    && let Some(mut data) = data.write()
727                                {
728                                    *data = data.change(input.state == ElementState::Pressed);
729                                    if mapping.consume == InputConsume::Hit {
730                                        consume = true;
731                                    }
732                                }
733                            }
734                            for (id, data) in &mapping.axes {
735                                if let VirtualAxis::KeyButton(button) = id
736                                    && *button == key
737                                    && let Some(mut data) = data.write()
738                                {
739                                    data.0 = if input.state == ElementState::Pressed {
740                                        1.0
741                                    } else {
742                                        0.0
743                                    };
744                                    if mapping.consume == InputConsume::Hit {
745                                        consume = true;
746                                    }
747                                }
748                            }
749                            if consume {
750                                break;
751                            }
752                        }
753                    }
754                }
755            }
756            WindowEvent::CursorMoved { position, .. } => {
757                for mapping in self.mappings_stack.iter().rev() {
758                    if !validity.iter().any(|m| m.ptr_eq(mapping)) {
759                        continue;
760                    }
761                    if let Some(mapping) = mapping.read() {
762                        let mut consume = mapping.consume == InputConsume::All;
763                        for (id, data) in &mapping.axes {
764                            match id {
765                                VirtualAxis::MousePositionX => {
766                                    if let Some(mut data) = data.write() {
767                                        data.0 = position.x as _;
768                                        if mapping.consume == InputConsume::Hit {
769                                            consume = true;
770                                        }
771                                    }
772                                }
773                                VirtualAxis::MousePositionY => {
774                                    if let Some(mut data) = data.write() {
775                                        data.0 = position.y as _;
776                                        if mapping.consume == InputConsume::Hit {
777                                            consume = true;
778                                        }
779                                    }
780                                }
781                                _ => {}
782                            }
783                        }
784                        if consume {
785                            break;
786                        }
787                    }
788                }
789            }
790            WindowEvent::MouseWheel { delta, .. } => {
791                for mapping in self.mappings_stack.iter().rev() {
792                    if !validity.iter().any(|m| m.ptr_eq(mapping)) {
793                        continue;
794                    }
795                    if let Some(mapping) = mapping.read() {
796                        let mut consume = mapping.consume == InputConsume::All;
797                        for (id, data) in &mapping.axes {
798                            match id {
799                                VirtualAxis::MouseWheelX => {
800                                    if let Some(mut data) = data.write() {
801                                        data.0 = match delta {
802                                            MouseScrollDelta::LineDelta(x, _) => *x,
803                                            MouseScrollDelta::PixelDelta(pos) => pos.x as _,
804                                        };
805                                        if mapping.consume == InputConsume::Hit {
806                                            consume = true;
807                                        }
808                                    }
809                                }
810                                VirtualAxis::MouseWheelY => {
811                                    if let Some(mut data) = data.write() {
812                                        data.0 = match delta {
813                                            MouseScrollDelta::LineDelta(_, y) => *y,
814                                            MouseScrollDelta::PixelDelta(pos) => pos.y as _,
815                                        };
816                                        if mapping.consume == InputConsume::Hit {
817                                            consume = true;
818                                        }
819                                    }
820                                }
821                                _ => {}
822                            }
823                        }
824                        if consume {
825                            break;
826                        }
827                    }
828                }
829            }
830            WindowEvent::MouseInput { state, button, .. } => {
831                for mapping in self.mappings_stack.iter().rev() {
832                    if !validity.iter().any(|m| m.ptr_eq(mapping)) {
833                        continue;
834                    }
835                    if let Some(mapping) = mapping.read() {
836                        let mut consume = mapping.consume == InputConsume::All;
837                        for (id, data) in &mapping.actions {
838                            if let VirtualAction::MouseButton(btn) = id
839                                && button == btn
840                                && let Some(mut data) = data.write()
841                            {
842                                *data = data.change(*state == ElementState::Pressed);
843                                if mapping.consume == InputConsume::Hit {
844                                    consume = true;
845                                }
846                            }
847                        }
848                        for (id, data) in &mapping.axes {
849                            if let VirtualAxis::MouseButton(btn) = id
850                                && button == btn
851                                && let Some(mut data) = data.write()
852                            {
853                                data.0 = if *state == ElementState::Pressed {
854                                    1.0
855                                } else {
856                                    0.0
857                                };
858                                if mapping.consume == InputConsume::Hit {
859                                    consume = true;
860                                }
861                            }
862                        }
863                        if consume {
864                            break;
865                        }
866                    }
867                }
868            }
869            WindowEvent::AxisMotion { axis, value, .. } => {
870                for mapping in self.mappings_stack.iter().rev() {
871                    if !validity.iter().any(|m| m.ptr_eq(mapping)) {
872                        continue;
873                    }
874                    if let Some(mapping) = mapping.read() {
875                        let mut consume = mapping.consume == InputConsume::All;
876                        for (id, data) in &mapping.actions {
877                            if let VirtualAction::Axis(index) = id
878                                && axis == index
879                                && let Some(mut data) = data.write()
880                            {
881                                *data = data.change(value.abs() > 0.5);
882                                if mapping.consume == InputConsume::Hit {
883                                    consume = true;
884                                }
885                            }
886                        }
887                        for (id, data) in &mapping.axes {
888                            if let VirtualAxis::Axis(index) = id
889                                && axis == index
890                                && let Some(mut data) = data.write()
891                            {
892                                data.0 = *value as _;
893                                if mapping.consume == InputConsume::Hit {
894                                    consume = true;
895                                }
896                            }
897                        }
898                        if consume {
899                            break;
900                        }
901                    }
902                }
903            }
904            WindowEvent::Touch(touch) => {
905                if matches!(touch.phase, TouchPhase::Started | TouchPhase::Moved)
906                    && self.active_touch.is_none()
907                    && touch.location.x >= self.touch_area_margin[0]
908                    && touch.location.y >= self.touch_area_margin[1]
909                    && touch.location.x < self.window_size[0] - self.touch_area_margin[2]
910                    && touch.location.y < self.window_size[1] - self.touch_area_margin[3]
911                {
912                    self.active_touch = Some(touch.id);
913                }
914                if let Some(active_touch) = self.active_touch
915                    && touch.id == active_touch
916                {
917                    for mapping in self.mappings_stack.iter().rev() {
918                        if !validity.iter().any(|m| m.ptr_eq(mapping)) {
919                            continue;
920                        }
921                        if let Some(mapping) = mapping.read() {
922                            let mut consume = mapping.consume == InputConsume::All;
923                            for (id, data) in &mapping.actions {
924                                if let VirtualAction::Touch = id
925                                    && let Some(mut data) = data.write()
926                                {
927                                    *data = data.change(matches!(
928                                        touch.phase,
929                                        TouchPhase::Started | TouchPhase::Moved
930                                    ));
931                                    if mapping.consume == InputConsume::Hit {
932                                        consume = true;
933                                    }
934                                }
935                            }
936                            for (id, data) in &mapping.axes {
937                                match id {
938                                    VirtualAxis::TouchX => {
939                                        if let Some(mut data) = data.write() {
940                                            data.0 = touch.location.x as _;
941                                            if mapping.consume == InputConsume::Hit {
942                                                consume = true;
943                                            }
944                                        }
945                                    }
946                                    VirtualAxis::TouchY => {
947                                        if let Some(mut data) = data.write() {
948                                            data.0 = touch.location.y as _;
949                                            if mapping.consume == InputConsume::Hit {
950                                                consume = true;
951                                            }
952                                        }
953                                    }
954                                    _ => {}
955                                }
956                            }
957                            if consume {
958                                break;
959                            }
960                        }
961                    }
962                    if matches!(touch.phase, TouchPhase::Ended | TouchPhase::Cancelled) {
963                        self.active_touch = None;
964                    }
965                }
966            }
967            _ => {}
968        }
969    }
970}
971
972#[derive(Default)]
973pub enum InputActionDetector {
974    #[default]
975    Listening,
976    Detected(VirtualAction),
977}
978
979impl InputActionDetector {
980    pub fn reset(&mut self) {
981        *self = Self::Listening;
982    }
983
984    pub fn try_consume(&mut self) -> Option<VirtualAction> {
985        if let Self::Detected(action) = self {
986            let action = *action;
987            *self = Self::Listening;
988            Some(action)
989        } else {
990            None
991        }
992    }
993
994    pub fn window_detect(&mut self, _context: &mut InputContext, event: &WindowEvent) {
995        if let Self::Listening = self {
996            match event {
997                WindowEvent::KeyboardInput { input, .. } => {
998                    if let Some(action) = input.virtual_keycode.map(VirtualAction::KeyButton) {
999                        *self = Self::Detected(action);
1000                    }
1001                }
1002                WindowEvent::MouseInput { button, .. } => {
1003                    *self = Self::Detected(VirtualAction::MouseButton(*button));
1004                }
1005                WindowEvent::AxisMotion { axis, .. } => {
1006                    *self = Self::Detected(VirtualAction::Axis(*axis));
1007                }
1008                WindowEvent::Touch(_) => *self = Self::Detected(VirtualAction::Touch),
1009                _ => {}
1010            }
1011        }
1012    }
1013
1014    pub fn gamepad_detect(&mut self, context: &mut InputContext, gamepad_id: Option<GamepadId>) {
1015        if let Self::Listening = self
1016            && let Some(gamepads) = context.gamepads.as_mut()
1017        {
1018            while let Some(event) = gamepads.next_event() {
1019                if let Some(id) = gamepad_id
1020                    && id != event.id
1021                {
1022                    continue;
1023                }
1024                match event.event {
1025                    GamepadEventType::ButtonPressed(info, ..) => {
1026                        *self = Self::Detected(VirtualAction::GamepadButton(info));
1027                        return;
1028                    }
1029                    GamepadEventType::AxisChanged(info, value, ..) if value.abs() > 0.5 => {
1030                        *self = Self::Detected(VirtualAction::GamepadAxis(info));
1031                        return;
1032                    }
1033                    _ => {}
1034                }
1035            }
1036        }
1037    }
1038}
1039
1040#[derive(Default)]
1041pub enum InputAxisDetector {
1042    #[default]
1043    Listening,
1044    TrackingMouse {
1045        x: f64,
1046        y: f64,
1047    },
1048    TrackingScroll {
1049        x: f64,
1050        y: f64,
1051    },
1052    TrackingTouch {
1053        x: f64,
1054        y: f64,
1055    },
1056    Detected(VirtualAxis),
1057}
1058
1059impl InputAxisDetector {
1060    pub fn reset(&mut self) {
1061        *self = Self::Listening;
1062    }
1063
1064    pub fn try_consume(&mut self) -> Option<VirtualAxis> {
1065        if let Self::Detected(axis) = self {
1066            let axis = *axis;
1067            *self = Self::Listening;
1068            Some(axis)
1069        } else {
1070            None
1071        }
1072    }
1073
1074    pub fn window_detect(&mut self, _context: &mut InputContext, event: &WindowEvent) {
1075        match self {
1076            Self::Listening => match event {
1077                WindowEvent::KeyboardInput { input, .. } => {
1078                    if let Some(key) = input.virtual_keycode {
1079                        *self = Self::Detected(VirtualAxis::KeyButton(key));
1080                    }
1081                }
1082                WindowEvent::MouseInput { button, .. } => {
1083                    *self = Self::Detected(VirtualAxis::MouseButton(*button));
1084                }
1085                WindowEvent::CursorMoved { position, .. } => {
1086                    *self = Self::TrackingMouse {
1087                        x: position.x,
1088                        y: position.y,
1089                    };
1090                }
1091                WindowEvent::MouseWheel { delta, .. } => match delta {
1092                    MouseScrollDelta::LineDelta(x, y) => {
1093                        if x.abs() > y.abs() {
1094                            *self = Self::Detected(VirtualAxis::MouseWheelX);
1095                        } else if y.abs() > 0.0 {
1096                            *self = Self::Detected(VirtualAxis::MouseWheelY);
1097                        }
1098                    }
1099                    MouseScrollDelta::PixelDelta(pos) => {
1100                        *self = Self::TrackingScroll {
1101                            x: pos.x as _,
1102                            y: pos.y as _,
1103                        };
1104                    }
1105                },
1106                WindowEvent::AxisMotion { axis, .. } => {
1107                    *self = Self::Detected(VirtualAxis::Axis(*axis));
1108                }
1109                WindowEvent::Touch(touch) => {
1110                    *self = Self::TrackingTouch {
1111                        x: touch.location.x,
1112                        y: touch.location.y,
1113                    };
1114                }
1115                _ => {}
1116            },
1117            Self::TrackingMouse { x, y } => {
1118                if let WindowEvent::CursorMoved { position, .. } = event {
1119                    let delta_x = position.x - *x;
1120                    let delta_y = position.y - *y;
1121                    if delta_x.abs() > delta_y.abs() {
1122                        *self = Self::Detected(VirtualAxis::MousePositionX);
1123                    } else if delta_y.abs() > 0.0 {
1124                        *self = Self::Detected(VirtualAxis::MousePositionY);
1125                    }
1126                }
1127            }
1128            Self::TrackingScroll { x, y } => {
1129                if let WindowEvent::MouseWheel { delta, .. } = event
1130                    && let MouseScrollDelta::PixelDelta(pos) = delta
1131                {
1132                    let delta_x = pos.x - *x;
1133                    let delta_y = pos.y - *y;
1134                    if delta_x.abs() > delta_y.abs() {
1135                        *self = Self::Detected(VirtualAxis::MouseWheelX);
1136                    } else if delta_y.abs() > 0.0 {
1137                        *self = Self::Detected(VirtualAxis::MouseWheelY);
1138                    }
1139                }
1140            }
1141            Self::TrackingTouch { x, y } => {
1142                if let WindowEvent::Touch(touch) = event {
1143                    let delta_x = touch.location.x - *x;
1144                    let delta_y = touch.location.y - *y;
1145                    if delta_x.abs() > delta_y.abs() {
1146                        *self = Self::Detected(VirtualAxis::TouchX);
1147                    } else if delta_y.abs() > 0.0 {
1148                        *self = Self::Detected(VirtualAxis::TouchY);
1149                    }
1150                }
1151            }
1152            _ => {}
1153        }
1154    }
1155
1156    pub fn gamepad_detect(&mut self, context: &mut InputContext, gamepad_id: Option<GamepadId>) {
1157        if let Some(gamepads) = context.gamepads.as_mut() {
1158            while let Some(event) = gamepads.next_event() {
1159                if let Some(id) = gamepad_id
1160                    && id != event.id
1161                {
1162                    continue;
1163                }
1164                match event.event {
1165                    GamepadEventType::ButtonPressed(info, ..) => {
1166                        *self = Self::Detected(VirtualAxis::GamepadButton(info));
1167                    }
1168                    GamepadEventType::AxisChanged(info, value, ..) if value.abs() > 0.5 => {
1169                        *self = Self::Detected(VirtualAxis::GamepadAxis(info));
1170                    }
1171                    _ => {}
1172                }
1173            }
1174        }
1175    }
1176}
1177
1178#[cfg(test)]
1179mod tests {
1180    use super::*;
1181
1182    #[test]
1183    fn test_stack() {
1184        let mut context = InputContext::default();
1185        context.push_mapping(InputMapping::default().name("a").layer(0));
1186        context.push_mapping(InputMapping::default().name("b").layer(0));
1187        context.push_mapping(InputMapping::default().name("c").layer(0));
1188        context.push_mapping(InputMapping::default().name("d").layer(-1));
1189        context.push_mapping(InputMapping::default().name("e").layer(1));
1190        context.push_mapping(InputMapping::default().name("f").layer(-1));
1191        context.push_mapping(InputMapping::default().name("g").layer(1));
1192        context.push_mapping(InputMapping::default().name("h").layer(-2));
1193        context.push_mapping(InputMapping::default().name("i").layer(-2));
1194        context.push_mapping(InputMapping::default().name("j").layer(2));
1195        context.push_mapping(InputMapping::default().name("k").layer(2));
1196
1197        let provided = context
1198            .stack()
1199            .map(|mapping| {
1200                let mapping = mapping.read().unwrap();
1201                (mapping.name.as_ref().to_owned(), mapping.layer)
1202            })
1203            .collect::<Vec<_>>();
1204        assert_eq!(
1205            provided,
1206            vec![
1207                ("h".to_owned(), -2),
1208                ("i".to_owned(), -2),
1209                ("d".to_owned(), -1),
1210                ("f".to_owned(), -1),
1211                ("a".to_owned(), 0),
1212                ("b".to_owned(), 0),
1213                ("c".to_owned(), 0),
1214                ("e".to_owned(), 1),
1215                ("g".to_owned(), 1),
1216                ("j".to_owned(), 2),
1217                ("k".to_owned(), 2),
1218            ]
1219        );
1220    }
1221}