spitfire_input/
lib.rs

1use gilrs::{Event as GamepadEvent, EventType as GamepadEventType, Gilrs};
2#[cfg(not(target_arch = "wasm32"))]
3use glutin::event::{ElementState, MouseScrollDelta, WindowEvent};
4use std::{
5    borrow::Cow,
6    cmp::Ordering,
7    collections::HashMap,
8    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
9};
10use typid::ID;
11#[cfg(target_arch = "wasm32")]
12use winit::event::{ElementState, MouseScrollDelta, 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}
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}
49
50#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
51pub enum InputAction {
52    #[default]
53    Idle,
54    Pressed,
55    Hold,
56    Released,
57}
58
59impl InputAction {
60    pub fn change(self, hold: bool) -> Self {
61        match (self, hold) {
62            (Self::Idle, true) | (Self::Released, true) => Self::Pressed,
63            (Self::Pressed, true) => Self::Hold,
64            (Self::Pressed, false) | (Self::Hold, false) => Self::Released,
65            (Self::Released, false) => Self::Idle,
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
154pub type InputActionRef = InputRef<InputAction>;
155pub type InputAxisRef = InputRef<InputAxis>;
156pub type InputCharactersRef = InputRef<InputCharacters>;
157pub type InputMappingRef = InputRef<InputMapping>;
158
159#[derive(Debug, Default, Clone)]
160pub enum InputActionOrAxisRef {
161    #[default]
162    None,
163    Action(InputActionRef),
164    Axis(InputAxisRef),
165}
166
167impl InputActionOrAxisRef {
168    pub fn is_none(&self) -> bool {
169        matches!(self, Self::None)
170    }
171
172    pub fn is_some(&self) -> bool {
173        !self.is_none()
174    }
175
176    pub fn get_scalar(&self, falsy: f32, truthy: f32) -> f32 {
177        match self {
178            Self::None => falsy,
179            Self::Action(action) => action.get().to_scalar(falsy, truthy),
180            Self::Axis(axis) => axis.get().0,
181        }
182    }
183
184    pub fn threshold(&self, value: f32) -> bool {
185        match self {
186            Self::None => false,
187            Self::Action(action) => action.get().is_down(),
188            Self::Axis(axis) => axis.get().threshold(value),
189        }
190    }
191}
192
193impl From<InputActionRef> for InputActionOrAxisRef {
194    fn from(value: InputActionRef) -> Self {
195        Self::Action(value)
196    }
197}
198
199impl From<InputAxisRef> for InputActionOrAxisRef {
200    fn from(value: InputAxisRef) -> Self {
201        Self::Axis(value)
202    }
203}
204
205pub struct InputCombinator<T> {
206    mapper: Box<dyn Fn() -> T + Send + Sync>,
207}
208
209impl<T: Default> Default for InputCombinator<T> {
210    fn default() -> Self {
211        Self::new(|| T::default())
212    }
213}
214
215impl<T> InputCombinator<T> {
216    pub fn new(mapper: impl Fn() -> T + Send + Sync + 'static) -> Self {
217        Self {
218            mapper: Box::new(mapper),
219        }
220    }
221
222    pub fn get(&self) -> T {
223        (self.mapper)()
224    }
225}
226
227#[derive(Default)]
228pub struct CardinalInputCombinator(InputCombinator<[f32; 2]>);
229
230impl CardinalInputCombinator {
231    pub fn new(
232        left: impl Into<InputActionOrAxisRef>,
233        right: impl Into<InputActionOrAxisRef>,
234        up: impl Into<InputActionOrAxisRef>,
235        down: impl Into<InputActionOrAxisRef>,
236    ) -> Self {
237        let left = left.into();
238        let right = right.into();
239        let up = up.into();
240        let down = down.into();
241        Self(InputCombinator::new(move || {
242            let left = left.get_scalar(0.0, -1.0);
243            let right = right.get_scalar(0.0, 1.0);
244            let up = up.get_scalar(0.0, -1.0);
245            let down = down.get_scalar(0.0, 1.0);
246            [left + right, up + down]
247        }))
248    }
249
250    pub fn get(&self) -> [f32; 2] {
251        self.0.get()
252    }
253}
254
255#[derive(Default)]
256pub struct DualInputCombinator(InputCombinator<f32>);
257
258impl DualInputCombinator {
259    pub fn new(
260        negative: impl Into<InputActionOrAxisRef>,
261        positive: impl Into<InputActionOrAxisRef>,
262    ) -> Self {
263        let negative = negative.into();
264        let positive = positive.into();
265        Self(InputCombinator::new(move || {
266            let negative = negative.get_scalar(0.0, -1.0);
267            let positive = positive.get_scalar(0.0, 1.0);
268            negative + positive
269        }))
270    }
271
272    pub fn get(&self) -> f32 {
273        self.0.get()
274    }
275}
276
277pub struct ArrayInputCombinator<const N: usize>(InputCombinator<[f32; N]>);
278
279impl<const N: usize> Default for ArrayInputCombinator<N> {
280    fn default() -> Self {
281        Self(InputCombinator::new(|| [0.0; N]))
282    }
283}
284
285impl<const N: usize> ArrayInputCombinator<N> {
286    pub fn new(inputs: [impl Into<InputActionOrAxisRef>; N]) -> Self {
287        let items: [InputActionOrAxisRef; N] = inputs.map(|input| input.into());
288        Self(InputCombinator::new(move || {
289            std::array::from_fn(|index| items[index].get_scalar(0.0, 1.0))
290        }))
291    }
292
293    pub fn get(&self) -> [f32; N] {
294        self.0.get()
295    }
296}
297
298#[derive(Debug, Default, Clone)]
299pub struct InputCharacters {
300    characters: String,
301}
302
303impl InputCharacters {
304    pub fn read(&self) -> &str {
305        &self.characters
306    }
307
308    pub fn write(&mut self) -> &mut String {
309        &mut self.characters
310    }
311
312    pub fn take(&mut self) -> String {
313        std::mem::take(&mut self.characters)
314    }
315}
316
317#[derive(Debug, Default, Clone)]
318pub struct InputMapping {
319    pub actions: HashMap<VirtualAction, InputActionRef>,
320    pub axes: HashMap<VirtualAxis, InputAxisRef>,
321    pub consume: InputConsume,
322    pub layer: isize,
323    pub name: Cow<'static, str>,
324    pub gamepad: Option<GamepadId>,
325}
326
327impl InputMapping {
328    pub fn action(mut self, id: VirtualAction, action: InputActionRef) -> Self {
329        self.actions.insert(id, action);
330        self
331    }
332
333    pub fn axis(mut self, id: VirtualAxis, axis: InputAxisRef) -> Self {
334        self.axes.insert(id, axis);
335        self
336    }
337
338    pub fn consume(mut self, consume: InputConsume) -> Self {
339        self.consume = consume;
340        self
341    }
342
343    pub fn layer(mut self, value: isize) -> Self {
344        self.layer = value;
345        self
346    }
347
348    pub fn name(mut self, value: impl Into<Cow<'static, str>>) -> Self {
349        self.name = value.into();
350        self
351    }
352
353    pub fn gamepad(mut self, gamepad: GamepadId) -> Self {
354        self.gamepad = Some(gamepad);
355        self
356    }
357}
358
359impl From<InputMapping> for InputMappingRef {
360    fn from(value: InputMapping) -> Self {
361        Self::new(value)
362    }
363}
364
365#[derive(Debug)]
366pub struct InputContext {
367    pub mouse_wheel_line_scale: f32,
368    /// [(id, mapping)]
369    mappings_stack: Vec<(ID<InputMapping>, InputMappingRef)>,
370    characters: InputCharactersRef,
371    gamepads: Option<Gilrs>,
372}
373
374impl Default for InputContext {
375    fn default() -> Self {
376        Self {
377            mouse_wheel_line_scale: Self::default_mouse_wheel_line_scale(),
378            mappings_stack: Default::default(),
379            characters: Default::default(),
380            gamepads: None,
381        }
382    }
383}
384
385impl Clone for InputContext {
386    fn clone(&self) -> Self {
387        Self {
388            mouse_wheel_line_scale: self.mouse_wheel_line_scale,
389            mappings_stack: self.mappings_stack.clone(),
390            characters: self.characters.clone(),
391            gamepads: None,
392        }
393    }
394}
395
396impl InputContext {
397    fn default_mouse_wheel_line_scale() -> f32 {
398        10.0
399    }
400
401    pub fn with_gamepads(mut self) -> Self {
402        self.gamepads = Gilrs::new().ok();
403        self
404    }
405
406    pub fn with_gamepads_custom(mut self, gamepads: Gilrs) -> Self {
407        self.gamepads = Some(gamepads);
408        self
409    }
410
411    pub fn gamepads(&self) -> Option<&Gilrs> {
412        self.gamepads.as_ref()
413    }
414
415    pub fn gamepads_mut(&mut self) -> Option<&mut Gilrs> {
416        self.gamepads.as_mut()
417    }
418
419    pub fn push_mapping(&mut self, mapping: impl Into<InputMappingRef>) -> ID<InputMapping> {
420        let mapping = mapping.into();
421        let id = ID::default();
422        let layer = mapping.read().unwrap().layer;
423        let index = self
424            .mappings_stack
425            .binary_search_by(|(_, mapping)| {
426                mapping
427                    .read()
428                    .unwrap()
429                    .layer
430                    .cmp(&layer)
431                    .then(Ordering::Less)
432            })
433            .unwrap_or_else(|index| index);
434        self.mappings_stack.insert(index, (id, mapping));
435        id
436    }
437
438    pub fn pop_mapping(&mut self) -> Option<InputMappingRef> {
439        self.mappings_stack.pop().map(|(_, mapping)| mapping)
440    }
441
442    pub fn top_mapping(&self) -> Option<&InputMappingRef> {
443        self.mappings_stack.last().map(|(_, mapping)| mapping)
444    }
445
446    pub fn remove_mapping(&mut self, id: ID<InputMapping>) -> Option<InputMappingRef> {
447        self.mappings_stack
448            .iter()
449            .position(|(mid, _)| mid == &id)
450            .map(|index| self.mappings_stack.remove(index).1)
451    }
452
453    pub fn mapping(&self, id: ID<InputMapping>) -> Option<RwLockReadGuard<InputMapping>> {
454        self.mappings_stack
455            .iter()
456            .find(|(mid, _)| mid == &id)
457            .and_then(|(_, mapping)| mapping.read())
458    }
459
460    pub fn stack(&self) -> impl Iterator<Item = &InputMappingRef> {
461        self.mappings_stack.iter().map(|(_, mapping)| mapping)
462    }
463
464    pub fn characters(&self) -> InputCharactersRef {
465        self.characters.clone()
466    }
467
468    pub fn maintain(&mut self) {
469        if let Some(gamepads) = self.gamepads.as_mut() {
470            while let Some(GamepadEvent { id, event, .. }) = gamepads.next_event() {
471                match event {
472                    GamepadEventType::ButtonPressed(info, ..) => {
473                        for (_, mapping) in self.mappings_stack.iter().rev() {
474                            if let Some(mapping) = mapping.read() {
475                                if !mapping.gamepad.map(|gamepad| gamepad == id).unwrap_or(true) {
476                                    continue;
477                                }
478                                let mut consume = mapping.consume == InputConsume::All;
479                                for (id, data) in &mapping.actions {
480                                    if let VirtualAction::GamepadButton(button) = id {
481                                        if *button == info {
482                                            if let Some(mut data) = data.write() {
483                                                *data = data.change(true);
484                                                if mapping.consume == InputConsume::Hit {
485                                                    consume = true;
486                                                }
487                                            }
488                                        }
489                                    }
490                                }
491                                for (id, data) in &mapping.axes {
492                                    if let VirtualAxis::GamepadButton(button) = id {
493                                        if *button == info {
494                                            if let Some(mut data) = data.write() {
495                                                data.0 = 1.0;
496                                                if mapping.consume == InputConsume::Hit {
497                                                    consume = true;
498                                                }
499                                            }
500                                        }
501                                    }
502                                }
503                                if consume {
504                                    break;
505                                }
506                            }
507                        }
508                    }
509                    GamepadEventType::ButtonReleased(info, ..) => {
510                        for (_, mapping) in self.mappings_stack.iter().rev() {
511                            if let Some(mapping) = mapping.read() {
512                                if !mapping.gamepad.map(|gamepad| gamepad == id).unwrap_or(true) {
513                                    continue;
514                                }
515                                let mut consume = mapping.consume == InputConsume::All;
516                                for (id, data) in &mapping.actions {
517                                    if let VirtualAction::GamepadButton(button) = id {
518                                        if *button == info {
519                                            if let Some(mut data) = data.write() {
520                                                *data = data.change(false);
521                                                if mapping.consume == InputConsume::Hit {
522                                                    consume = true;
523                                                }
524                                            }
525                                        }
526                                    }
527                                }
528                                for (id, data) in &mapping.axes {
529                                    if let VirtualAxis::GamepadButton(button) = id {
530                                        if *button == info {
531                                            if let Some(mut data) = data.write() {
532                                                data.0 = 0.0;
533                                                if mapping.consume == InputConsume::Hit {
534                                                    consume = true;
535                                                }
536                                            }
537                                        }
538                                    }
539                                }
540                                if consume {
541                                    break;
542                                }
543                            }
544                        }
545                    }
546                    GamepadEventType::AxisChanged(info, value, ..) => {
547                        for (_, mapping) in self.mappings_stack.iter().rev() {
548                            if let Some(mapping) = mapping.read() {
549                                let mut consume = mapping.consume == InputConsume::All;
550                                for (id, data) in &mapping.actions {
551                                    if let VirtualAction::GamepadAxis(axis) = id {
552                                        if *axis == info {
553                                            if let Some(mut data) = data.write() {
554                                                *data = data.change(value > 0.5);
555                                                if mapping.consume == InputConsume::Hit {
556                                                    consume = true;
557                                                }
558                                            }
559                                        }
560                                    }
561                                }
562                                for (id, data) in &mapping.axes {
563                                    if let VirtualAxis::GamepadAxis(axis) = id {
564                                        if *axis == info {
565                                            if let Some(mut data) = data.write() {
566                                                data.0 = value;
567                                                if mapping.consume == InputConsume::Hit {
568                                                    consume = true;
569                                                }
570                                            }
571                                        }
572                                    }
573                                }
574                                if consume {
575                                    break;
576                                }
577                            }
578                        }
579                    }
580                    _ => {}
581                }
582            }
583            gamepads.inc();
584        }
585        for (_, mapping) in &mut self.mappings_stack {
586            if let Some(mut mapping) = mapping.write() {
587                for action in mapping.actions.values_mut() {
588                    if let Some(mut action) = action.write() {
589                        *action = action.update();
590                    }
591                }
592                for (id, axis) in &mut mapping.axes {
593                    if let VirtualAxis::MouseWheelX | VirtualAxis::MouseWheelY = id {
594                        if let Some(mut axis) = axis.write() {
595                            axis.0 = 0.0;
596                        }
597                    }
598                }
599            }
600        }
601    }
602
603    pub fn on_event(&mut self, event: &WindowEvent) {
604        match event {
605            WindowEvent::ReceivedCharacter(character) => {
606                if let Some(mut characters) = self.characters.write() {
607                    characters.characters.push(*character);
608                }
609            }
610            WindowEvent::KeyboardInput { input, .. } => {
611                if let Some(key) = input.virtual_keycode {
612                    for (_, mapping) in self.mappings_stack.iter().rev() {
613                        if let Some(mapping) = mapping.read() {
614                            let mut consume = mapping.consume == InputConsume::All;
615                            for (id, data) in &mapping.actions {
616                                if let VirtualAction::KeyButton(button) = id {
617                                    if *button == key {
618                                        if let Some(mut data) = data.write() {
619                                            *data =
620                                                data.change(input.state == ElementState::Pressed);
621                                            if mapping.consume == InputConsume::Hit {
622                                                consume = true;
623                                            }
624                                        }
625                                    }
626                                }
627                            }
628                            for (id, data) in &mapping.axes {
629                                if let VirtualAxis::KeyButton(button) = id {
630                                    if *button == key {
631                                        if let Some(mut data) = data.write() {
632                                            data.0 = if input.state == ElementState::Pressed {
633                                                1.0
634                                            } else {
635                                                0.0
636                                            };
637                                            if mapping.consume == InputConsume::Hit {
638                                                consume = true;
639                                            }
640                                        }
641                                    }
642                                }
643                            }
644                            if consume {
645                                break;
646                            }
647                        }
648                    }
649                }
650            }
651            WindowEvent::CursorMoved { position, .. } => {
652                for (_, mapping) in self.mappings_stack.iter().rev() {
653                    if let Some(mapping) = mapping.read() {
654                        let mut consume = mapping.consume == InputConsume::All;
655                        for (id, data) in &mapping.axes {
656                            match id {
657                                VirtualAxis::MousePositionX => {
658                                    if let Some(mut data) = data.write() {
659                                        data.0 = position.x as _;
660                                        if mapping.consume == InputConsume::Hit {
661                                            consume = true;
662                                        }
663                                    }
664                                }
665                                VirtualAxis::MousePositionY => {
666                                    if let Some(mut data) = data.write() {
667                                        data.0 = position.y as _;
668                                        if mapping.consume == InputConsume::Hit {
669                                            consume = true;
670                                        }
671                                    }
672                                }
673                                _ => {}
674                            }
675                        }
676                        if consume {
677                            break;
678                        }
679                    }
680                }
681            }
682            WindowEvent::MouseWheel { delta, .. } => {
683                for (_, mapping) in self.mappings_stack.iter().rev() {
684                    if let Some(mapping) = mapping.read() {
685                        let mut consume = mapping.consume == InputConsume::All;
686                        for (id, data) in &mapping.axes {
687                            match id {
688                                VirtualAxis::MouseWheelX => {
689                                    if let Some(mut data) = data.write() {
690                                        data.0 = match delta {
691                                            MouseScrollDelta::LineDelta(x, _) => *x,
692                                            MouseScrollDelta::PixelDelta(pos) => pos.x as _,
693                                        };
694                                        if mapping.consume == InputConsume::Hit {
695                                            consume = true;
696                                        }
697                                    }
698                                }
699                                VirtualAxis::MouseWheelY => {
700                                    if let Some(mut data) = data.write() {
701                                        data.0 = match delta {
702                                            MouseScrollDelta::LineDelta(_, y) => *y,
703                                            MouseScrollDelta::PixelDelta(pos) => pos.y as _,
704                                        };
705                                        if mapping.consume == InputConsume::Hit {
706                                            consume = true;
707                                        }
708                                    }
709                                }
710                                _ => {}
711                            }
712                        }
713                        if consume {
714                            break;
715                        }
716                    }
717                }
718            }
719            WindowEvent::MouseInput { state, button, .. } => {
720                for (_, mapping) in self.mappings_stack.iter().rev() {
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::MouseButton(btn) = id {
725                                if button == btn {
726                                    if let Some(mut data) = data.write() {
727                                        *data = data.change(*state == ElementState::Pressed);
728                                        if mapping.consume == InputConsume::Hit {
729                                            consume = true;
730                                        }
731                                    }
732                                }
733                            }
734                        }
735                        for (id, data) in &mapping.axes {
736                            if let VirtualAxis::MouseButton(btn) = id {
737                                if button == btn {
738                                    if let Some(mut data) = data.write() {
739                                        data.0 = if *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                            }
750                        }
751                        if consume {
752                            break;
753                        }
754                    }
755                }
756            }
757            WindowEvent::AxisMotion { axis, value, .. } => {
758                for (_, mapping) in self.mappings_stack.iter().rev() {
759                    if let Some(mapping) = mapping.read() {
760                        let mut consume = mapping.consume == InputConsume::All;
761                        for (id, data) in &mapping.actions {
762                            if let VirtualAction::Axis(index) = id {
763                                if axis == index {
764                                    if let Some(mut data) = data.write() {
765                                        *data = data.change(value.abs() > 0.5);
766                                        if mapping.consume == InputConsume::Hit {
767                                            consume = true;
768                                        }
769                                    }
770                                }
771                            }
772                        }
773                        for (id, data) in &mapping.axes {
774                            if let VirtualAxis::Axis(index) = id {
775                                if axis == index {
776                                    if let Some(mut data) = data.write() {
777                                        data.0 = *value as _;
778                                        if mapping.consume == InputConsume::Hit {
779                                            consume = true;
780                                        }
781                                    }
782                                }
783                            }
784                        }
785                        if consume {
786                            break;
787                        }
788                    }
789                }
790            }
791            _ => {}
792        }
793    }
794}
795
796#[cfg(test)]
797mod tests {
798    use super::*;
799
800    #[test]
801    fn test_stack() {
802        let mut context = InputContext::default();
803        context.push_mapping(InputMapping::default().name("a").layer(0));
804        context.push_mapping(InputMapping::default().name("b").layer(0));
805        context.push_mapping(InputMapping::default().name("c").layer(0));
806        context.push_mapping(InputMapping::default().name("d").layer(-1));
807        context.push_mapping(InputMapping::default().name("e").layer(1));
808        context.push_mapping(InputMapping::default().name("f").layer(-1));
809        context.push_mapping(InputMapping::default().name("g").layer(1));
810        context.push_mapping(InputMapping::default().name("h").layer(-2));
811        context.push_mapping(InputMapping::default().name("i").layer(-2));
812        context.push_mapping(InputMapping::default().name("j").layer(2));
813        context.push_mapping(InputMapping::default().name("k").layer(2));
814
815        let provided = context
816            .stack()
817            .map(|mapping| {
818                let mapping = mapping.read().unwrap();
819                (mapping.name.as_ref().to_owned(), mapping.layer)
820            })
821            .collect::<Vec<_>>();
822        assert_eq!(
823            provided,
824            vec![
825                ("h".to_owned(), -2),
826                ("i".to_owned(), -2),
827                ("d".to_owned(), -1),
828                ("f".to_owned(), -1),
829                ("a".to_owned(), 0),
830                ("b".to_owned(), 0),
831                ("c".to_owned(), 0),
832                ("e".to_owned(), 1),
833                ("g".to_owned(), 1),
834                ("j".to_owned(), 2),
835                ("k".to_owned(), 2),
836            ]
837        );
838    }
839}