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 pub touch_area_margin: [f64; 4],
390 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}