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