bevy_enhanced_input/context/
input_reader.rs

1use alloc::vec::Vec;
2use core::{any::TypeId, hash::Hash, iter, mem};
3
4use bevy::{
5    ecs::{schedule::ScheduleLabel, system::SystemParam},
6    input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll},
7    platform::collections::HashSet,
8    prelude::*,
9    utils::TypeIdMap,
10};
11use log::{debug, trace};
12
13use crate::prelude::*;
14
15pub(crate) fn update_pending(mut reader: InputReader) {
16    reader.update_pending();
17}
18
19/// Input state for actions.
20///
21/// Actions can read binding values and optionally consume them without affecting Bevy input resources.
22#[derive(SystemParam)]
23pub(crate) struct InputReader<'w, 's> {
24    keys: Option<Res<'w, ButtonInput<KeyCode>>>,
25    mouse_buttons: Option<Res<'w, ButtonInput<MouseButton>>>,
26    mouse_motion: Option<Res<'w, AccumulatedMouseMotion>>,
27    mouse_scroll: Option<Res<'w, AccumulatedMouseScroll>>,
28    gamepads: Query<'w, 's, &'static Gamepad>,
29    action_sources: Res<'w, ActionSources>,
30    consumed: ResMut<'w, ConsumedInputs>,
31    pending: ResMut<'w, PendingBindings>,
32    gamepad_device: Local<'s, GamepadDevice>,
33    skip_ignore_check: Local<'s, bool>,
34}
35
36impl InputReader<'_, '_> {
37    /// Updates list of inputs that are waiting for reset.
38    pub(crate) fn update_pending(&mut self) {
39        // Updated before context-consumed inputs,
40        // which may still reference inputs added to the pending.
41        *self.skip_ignore_check = true;
42
43        // Temporary take the original value to avoid issues with the borrow checker.
44        let mut pending = mem::take(&mut *self.pending);
45        pending.ignored.clear();
46        pending.bindings.retain(|&binding| {
47            if self.value(binding).as_bool() {
48                pending.ignored.add(binding, *self.gamepad_device);
49                true
50            } else {
51                trace!("'{binding}' reset and no longer ignored");
52                false
53            }
54        });
55        *self.pending = pending;
56
57        *self.skip_ignore_check = false
58    }
59
60    /// Clears all consumed values from the given schedule.
61    pub(crate) fn clear_consumed<S: ScheduleLabel>(&mut self) {
62        self.consumed.entry(TypeId::of::<S>()).or_default().clear();
63    }
64
65    /// Assigns a gamepad from which [`Self::value`] should read input.
66    pub(crate) fn set_gamepad(&mut self, gamepad: impl Into<GamepadDevice>) {
67        *self.gamepad_device = gamepad.into();
68    }
69
70    /// Returns the [`ActionValue`] for the given [`Binding`].
71    ///
72    /// See also [`Self::consume`] and [`Self::set_gamepad`].
73    pub(crate) fn value(&self, binding: impl Into<Binding>) -> ActionValue {
74        let binding = binding.into();
75        match binding {
76            Binding::Keyboard { key, mod_keys } => {
77                let pressed = self.action_sources.keyboard
78                    && self.keys.as_ref().is_some_and(|k| k.pressed(key))
79                    && self.mod_keys_pressed(mod_keys)
80                    && !self.ignored(binding);
81
82                pressed.into()
83            }
84            Binding::MouseButton { button, mod_keys } => {
85                let pressed = self.action_sources.mouse_buttons
86                    && self
87                        .mouse_buttons
88                        .as_ref()
89                        .is_some_and(|b| b.pressed(button))
90                    && self.mod_keys_pressed(mod_keys)
91                    && !self.ignored(binding);
92
93                pressed.into()
94            }
95            Binding::MouseMotion { mod_keys } => {
96                if !self.action_sources.mouse_motion
97                    || !self.mod_keys_pressed(mod_keys)
98                    || self.ignored(binding)
99                {
100                    return Vec2::ZERO.into();
101                }
102
103                self.mouse_motion
104                    .as_ref()
105                    .map(|m| m.delta)
106                    .unwrap_or_default()
107                    .into()
108            }
109            Binding::MouseWheel { mod_keys } => {
110                if !self.action_sources.mouse_wheel
111                    || !self.mod_keys_pressed(mod_keys)
112                    || self.ignored(binding)
113                {
114                    return Vec2::ZERO.into();
115                }
116
117                self.mouse_scroll
118                    .as_ref()
119                    .map(|s| s.delta)
120                    .unwrap_or_default()
121                    .into()
122            }
123            Binding::GamepadButton(button) => {
124                if !self.action_sources.gamepad_button || self.ignored(binding) {
125                    return 0.0.into();
126                }
127
128                let value = match *self.gamepad_device {
129                    GamepadDevice::Any => self
130                        .gamepads
131                        .iter()
132                        .filter_map(|gamepad| gamepad.get(button))
133                        .find(|&value| value != 0.0),
134                    GamepadDevice::Single(entity) => self
135                        .gamepads
136                        .get(entity)
137                        .ok()
138                        .and_then(|gamepad| gamepad.get(button)),
139                    GamepadDevice::None => return 0.0.into(),
140                };
141
142                value.unwrap_or_default().into()
143            }
144            Binding::GamepadAxis(axis) => {
145                if !self.action_sources.gamepad_axis || self.ignored(binding) {
146                    return 0.0.into();
147                }
148
149                let value = match *self.gamepad_device {
150                    GamepadDevice::Any => self
151                        .gamepads
152                        .iter()
153                        .filter_map(|gamepad| gamepad.get_unclamped(axis))
154                        .reduce(|acc, v| acc + v),
155                    GamepadDevice::Single(entity) => self
156                        .gamepads
157                        .get(entity)
158                        .ok()
159                        .and_then(|gamepad| gamepad.get(axis)),
160                    GamepadDevice::None => return 0.0.into(),
161                };
162
163                let value = value.unwrap_or_default();
164                value.into()
165            }
166            Binding::AnyKey => {
167                if self.ignored(Binding::AnyKey) {
168                    return false.into();
169                }
170
171                if self.action_sources.keyboard
172                    && self
173                        .keys
174                        .iter()
175                        .flat_map(|k| k.get_pressed())
176                        .any(|&k| !self.ignored(k))
177                {
178                    return true.into();
179                }
180
181                if self.action_sources.mouse_buttons
182                    && self
183                        .mouse_buttons
184                        .iter()
185                        .flat_map(|b| b.get_pressed())
186                        .any(|&b| !self.ignored(b))
187                {
188                    return true.into();
189                }
190
191                if self.action_sources.gamepad_button {
192                    match *self.gamepad_device {
193                        GamepadDevice::Single(entity) => {
194                            if let Ok(gamepad) = self.gamepads.get(entity)
195                                && gamepad.get_pressed().any(|&b| !self.ignored(b))
196                            {
197                                return true.into();
198                            }
199                        }
200                        GamepadDevice::Any => {
201                            for gamepad in &self.gamepads {
202                                if gamepad.get_pressed().any(|&b| !self.ignored(b)) {
203                                    return true.into();
204                                }
205                            }
206                        }
207                        GamepadDevice::None => (),
208                    };
209                }
210
211                false.into()
212            }
213            Binding::None => false.into(),
214        }
215    }
216
217    fn mod_keys_pressed(&self, mod_keys: ModKeys) -> bool {
218        if !mod_keys.is_empty() && !self.action_sources.keyboard {
219            return false;
220        }
221
222        for keys in mod_keys.iter_keys() {
223            if self.keys.as_ref().is_none_or(|k| !k.any_pressed(keys)) {
224                return false;
225            }
226        }
227
228        true
229    }
230
231    fn ignored(&self, binding: impl Into<Binding>) -> bool {
232        if *self.skip_ignore_check {
233            return false;
234        }
235
236        let keys_ignored =
237            self.pending.ignored.any_key || self.consumed.values().any(|ignored| ignored.any_key);
238        let mut iter = iter::once(&self.pending.ignored).chain(self.consumed.values());
239        match binding.into() {
240            Binding::Keyboard { key, mod_keys } => {
241                iter.any(|i| i.keys.contains(&key) || i.mod_keys.intersects(mod_keys))
242                    || keys_ignored
243            }
244            Binding::MouseButton { button, mod_keys } => {
245                iter.any(|i| i.mouse_buttons.contains(&button) || i.mod_keys.intersects(mod_keys))
246                    || keys_ignored
247            }
248            Binding::MouseMotion { mod_keys } => {
249                iter.any(|inputs| inputs.mouse_motion || inputs.mod_keys.intersects(mod_keys))
250            }
251            Binding::MouseWheel { mod_keys } => {
252                iter.any(|inputs| inputs.mouse_wheel || inputs.mod_keys.intersects(mod_keys))
253            }
254            Binding::GamepadButton(button) => {
255                let input = GamepadInput {
256                    gamepad: *self.gamepad_device,
257                    input: button,
258                };
259                iter.any(|inputs| inputs.gamepad_buttons.contains(&input)) || keys_ignored
260            }
261            Binding::GamepadAxis(axis) => {
262                let input = GamepadInput {
263                    gamepad: *self.gamepad_device,
264                    input: axis,
265                };
266                iter.any(|inputs| inputs.gamepad_axes.contains(&input))
267            }
268            Binding::AnyKey => keys_ignored,
269            Binding::None => false,
270        }
271    }
272
273    /// Consumes the binding input, making it unavailable for [`Self::value`].
274    ///
275    /// Clears for this schedule with [`Self::clear_consumed`].
276    pub(crate) fn consume<S: ScheduleLabel>(&mut self, binding: impl Into<Binding>) {
277        self.consumed
278            .entry(TypeId::of::<S>())
279            .or_default()
280            .add(binding.into(), *self.gamepad_device);
281    }
282}
283
284/// Configures which input sources are visible to actions.
285///
286/// Defaults to `true` for all values.
287///
288/// Could be used to prevent actions from being triggered
289/// while interacting with the UI.
290///
291/// # Examples
292///
293/// Disables mouse buttons for actions when the cursor hovers a node with
294/// an `Interaction` component. It's a required component for `Button`,
295/// but you can add it to any UI node to disable specific actions on hover.
296///
297/// ```
298/// use bevy::prelude::*;
299/// use bevy_enhanced_input::prelude::*;
300///
301/// # let mut app = App::new();
302/// app.add_systems(PreUpdate, disable_mouse.before(EnhancedInputSet::Update));
303///
304/// fn disable_mouse(
305///     mut action_sources: ResMut<ActionSources>,
306///     interactions: Query<&Interaction>,
307/// ) {
308///     let mouse_unused = interactions.iter().all(|&interaction| interaction == Interaction::None);
309///     action_sources.mouse_buttons = mouse_unused;
310///     action_sources.mouse_wheel = mouse_unused;
311/// }
312/// ```
313#[derive(Resource, Reflect)]
314pub struct ActionSources {
315    pub keyboard: bool,
316    pub mouse_buttons: bool,
317    pub mouse_motion: bool,
318    pub mouse_wheel: bool,
319    pub gamepad_button: bool,
320    pub gamepad_axis: bool,
321}
322
323impl Default for ActionSources {
324    fn default() -> Self {
325        Self {
326            keyboard: true,
327            mouse_buttons: true,
328            mouse_motion: true,
329            mouse_wheel: true,
330            gamepad_button: true,
331            gamepad_axis: true,
332        }
333    }
334}
335
336/// Inputs consumed by actions in each schedule.
337///
338/// These inputs will be ignored by [`InputReader::value`] in all schedules.
339/// When a schedule runs, previously consumed inputs in it will be cleared.
340///
341/// This allows schedules like [`FixedPreUpdate`], which may run multiple times per frame,
342/// to correctly handle inputs it consumes itself, while still treating inputs consumed in
343/// [`PreUpdate`] as already consumed for all runs within the same frame.
344#[derive(Resource, Default, Deref, DerefMut)]
345pub(crate) struct ConsumedInputs(TypeIdMap<IgnoredInputs>);
346
347/// Bindings from actions with [`ActionSettings::require_reset`] enabled that were removed.
348///
349/// Their inputs will be ignored by [`InputReader::value`] until they become inactive.
350/// Once inactive, they will be automatically removed and no longer ignored.
351#[derive(Resource, Default)]
352pub(crate) struct PendingBindings {
353    bindings: Vec<Binding>,
354
355    /// Computed from [`Self::bindings`].
356    ignored: IgnoredInputs,
357}
358
359impl PendingBindings {
360    pub(crate) fn extend(&mut self, iter: impl Iterator<Item = Binding>) {
361        self.bindings
362            .extend(iter.inspect(|binding| debug!("ignoring '{binding}' until reset")));
363    }
364}
365
366#[derive(Default)]
367pub(crate) struct IgnoredInputs {
368    keys: HashSet<KeyCode>,
369    mod_keys: ModKeys,
370    mouse_buttons: HashSet<MouseButton>,
371    mouse_motion: bool,
372    mouse_wheel: bool,
373    gamepad_buttons: HashSet<GamepadInput<GamepadButton>>,
374    gamepad_axes: HashSet<GamepadInput<GamepadAxis>>,
375    any_key: bool,
376}
377
378impl IgnoredInputs {
379    fn add(&mut self, binding: Binding, gamepad: GamepadDevice) {
380        match binding {
381            Binding::Keyboard { key, mod_keys } => {
382                self.keys.insert(key);
383                self.mod_keys.insert(mod_keys);
384            }
385            Binding::MouseButton { button, mod_keys } => {
386                self.mouse_buttons.insert(button);
387                self.mod_keys.insert(mod_keys);
388            }
389            Binding::MouseMotion { mod_keys } => {
390                self.mouse_motion = true;
391                self.mod_keys.insert(mod_keys);
392            }
393            Binding::MouseWheel { mod_keys } => {
394                self.mouse_wheel = true;
395                self.mod_keys.insert(mod_keys);
396            }
397            Binding::GamepadButton(button) => {
398                let input = GamepadInput {
399                    gamepad,
400                    input: button,
401                };
402
403                self.gamepad_buttons.insert(input);
404            }
405            Binding::GamepadAxis(axis) => {
406                let input = GamepadInput {
407                    gamepad,
408                    input: axis,
409                };
410
411                self.gamepad_axes.insert(input);
412            }
413            Binding::AnyKey => self.any_key = true,
414            Binding::None => (),
415        }
416    }
417
418    fn clear(&mut self) {
419        self.keys.clear();
420        self.mod_keys = ModKeys::empty();
421        self.mouse_buttons.clear();
422        self.mouse_motion = false;
423        self.mouse_wheel = false;
424        self.gamepad_buttons.clear();
425        self.gamepad_axes.clear();
426        self.any_key = false;
427    }
428}
429
430/// Input and associated device.
431#[derive(Hash, PartialEq, Eq)]
432struct GamepadInput<T: Hash + Eq> {
433    gamepad: GamepadDevice,
434    input: T,
435}
436
437#[cfg(test)]
438mod tests {
439    use bevy::{
440        ecs::system::SystemState,
441        input::mouse::{MouseMotion, MouseScrollUnit, MouseWheel},
442    };
443
444    use super::*;
445
446    #[test]
447    fn keyboard() {
448        let (mut world, mut state) = init_world();
449
450        let key = KeyCode::Space;
451        world.resource_mut::<ButtonInput<KeyCode>>().press(key);
452
453        let mut reader = state.get_mut(&mut world);
454        assert_eq!(reader.value(key), true.into());
455        assert_eq!(reader.value(Binding::AnyKey), true.into());
456        assert_eq!(reader.value(KeyCode::Escape), false.into());
457        assert_eq!(reader.value(key.with_mod_keys(ModKeys::ALT)), false.into());
458
459        reader.consume::<PreUpdate>(key);
460        assert_eq!(reader.value(key), false.into());
461        assert_eq!(reader.value(Binding::AnyKey), false.into());
462    }
463
464    #[test]
465    fn mouse_button() {
466        let (mut world, mut state) = init_world();
467
468        let button = MouseButton::Left;
469        world
470            .resource_mut::<ButtonInput<MouseButton>>()
471            .press(button);
472
473        let mut reader = state.get_mut(&mut world);
474        assert_eq!(reader.value(button), true.into());
475        assert_eq!(reader.value(Binding::AnyKey), true.into());
476        assert_eq!(reader.value(MouseButton::Right), false.into());
477        assert_eq!(
478            reader.value(button.with_mod_keys(ModKeys::CONTROL)),
479            false.into()
480        );
481
482        reader.consume::<PreUpdate>(button);
483        assert_eq!(reader.value(button), false.into());
484        assert_eq!(reader.value(Binding::AnyKey), false.into());
485    }
486
487    #[test]
488    fn mouse_motion() {
489        let (mut world, mut state) = init_world();
490
491        let value = Vec2::ONE;
492        world.insert_resource(AccumulatedMouseMotion { delta: value });
493
494        let binding = Binding::mouse_motion();
495        let mut reader = state.get_mut(&mut world);
496        reader.clear_consumed::<PreUpdate>();
497        assert_eq!(reader.value(binding), value.into());
498        assert_eq!(
499            reader.value(binding.with_mod_keys(ModKeys::SHIFT)),
500            Vec2::ZERO.into()
501        );
502
503        reader.consume::<PreUpdate>(binding);
504        assert_eq!(reader.value(binding), Vec2::ZERO.into());
505    }
506
507    #[test]
508    fn mouse_wheel() {
509        let (mut world, mut state) = init_world();
510
511        let value = Vec2::ONE;
512        world.insert_resource(AccumulatedMouseScroll {
513            unit: MouseScrollUnit::Line,
514            delta: value,
515        });
516
517        let binding = Binding::mouse_wheel();
518        let mut reader = state.get_mut(&mut world);
519        reader.clear_consumed::<PreUpdate>();
520        assert_eq!(reader.value(binding), value.into());
521        assert_eq!(
522            reader.value(binding.with_mod_keys(ModKeys::SUPER)),
523            Vec2::ZERO.into()
524        );
525
526        reader.consume::<PreUpdate>(binding);
527        assert_eq!(reader.value(binding), Vec2::ZERO.into());
528    }
529
530    #[test]
531    fn gamepad_button() {
532        let (mut world, mut state) = init_world();
533
534        let value = 1.0;
535        let button1 = GamepadButton::South;
536        let mut gamepad1 = Gamepad::default();
537        gamepad1.analog_mut().set(button1, value);
538        gamepad1.digital_mut().press(button1);
539        let gamepad_entity = world.spawn(gamepad1).id();
540
541        let button2 = GamepadButton::East;
542        let mut gamepad2 = Gamepad::default();
543        gamepad2.analog_mut().set(button2, value);
544        gamepad2.digital_mut().press(button2);
545        world.spawn(gamepad2);
546
547        let mut reader = state.get_mut(&mut world);
548        reader.set_gamepad(gamepad_entity);
549        assert_eq!(reader.value(button1), value.into());
550        assert_eq!(
551            reader.value(button2),
552            0.0.into(),
553            "should read only from `{gamepad_entity:?}`"
554        );
555        assert_eq!(reader.value(Binding::AnyKey), true.into());
556        assert_eq!(reader.value(GamepadButton::North), 0.0.into());
557
558        reader.consume::<PreUpdate>(button1);
559        assert_eq!(reader.value(button1), 0.0.into());
560        assert_eq!(reader.value(Binding::AnyKey), false.into());
561    }
562
563    #[test]
564    fn any_gamepad_button() {
565        let (mut world, mut state) = init_world();
566
567        let value = 1.0;
568        let button1 = GamepadButton::South;
569        let mut gamepad1 = Gamepad::default();
570        gamepad1.analog_mut().set(button1, value);
571        gamepad1.digital_mut().press(button1);
572        world.spawn(gamepad1);
573
574        let button2 = GamepadButton::East;
575        let mut gamepad2 = Gamepad::default();
576        gamepad2.analog_mut().set(button2, value);
577        gamepad2.digital_mut().press(button2);
578        world.spawn(gamepad2);
579
580        let mut reader = state.get_mut(&mut world);
581        assert_eq!(reader.value(button1), value.into());
582        assert_eq!(reader.value(button2), value.into());
583        assert_eq!(reader.value(Binding::AnyKey), true.into());
584        assert_eq!(reader.value(GamepadButton::North), 0.0.into());
585
586        reader.consume::<PreUpdate>(button1);
587        assert_eq!(reader.value(button1), 0.0.into());
588        assert_eq!(reader.value(Binding::AnyKey), true.into());
589
590        reader.consume::<PreUpdate>(button2);
591        assert_eq!(reader.value(button2), 0.0.into());
592        assert_eq!(reader.value(Binding::AnyKey), false.into());
593    }
594
595    #[test]
596    fn gamepad_axis() {
597        let (mut world, mut state) = init_world();
598
599        let value = 1.0;
600        let axis1 = GamepadAxis::LeftStickX;
601        let mut gamepad1 = Gamepad::default();
602        gamepad1.analog_mut().set(axis1, value);
603        let gamepad_entity = world.spawn(gamepad1).id();
604
605        let axis2 = GamepadAxis::LeftStickY;
606        let mut gamepad2 = Gamepad::default();
607        gamepad2.analog_mut().set(axis2, value);
608        world.spawn(gamepad2);
609
610        let mut reader = state.get_mut(&mut world);
611        reader.set_gamepad(gamepad_entity);
612        assert_eq!(reader.value(axis1), value.into());
613        assert_eq!(
614            reader.value(axis2),
615            0.0.into(),
616            "should read only from `{gamepad_entity:?}`"
617        );
618        assert_eq!(reader.value(GamepadAxis::RightStickX), 0.0.into());
619
620        reader.consume::<PreUpdate>(axis1);
621        assert_eq!(reader.value(axis1), 0.0.into());
622    }
623
624    #[test]
625    fn any_gamepad_axis() {
626        let (mut world, mut state) = init_world();
627
628        let value = 1.0;
629        let axis1 = GamepadAxis::LeftStickX;
630        let mut gamepad1 = Gamepad::default();
631        gamepad1.analog_mut().set(axis1, value);
632        world.spawn(gamepad1);
633
634        let axis2 = GamepadAxis::LeftStickY;
635        let mut gamepad2 = Gamepad::default();
636        gamepad2.analog_mut().set(axis2, value);
637        world.spawn(gamepad2);
638
639        let mut reader = state.get_mut(&mut world);
640        assert_eq!(reader.value(axis1), value.into());
641        assert_eq!(reader.value(axis2), value.into());
642        assert_eq!(reader.value(GamepadAxis::RightStickX), 0.0.into());
643
644        reader.consume::<PreUpdate>(axis1);
645        assert_eq!(reader.value(axis1), 0.0.into());
646
647        reader.consume::<PreUpdate>(axis2);
648        assert_eq!(reader.value(axis2), 0.0.into());
649    }
650
651    #[test]
652    fn no_gamepad() {
653        let (mut world, mut state) = init_world();
654
655        let value = 1.0;
656        let axis = GamepadAxis::LeftStickX;
657        let button = GamepadButton::South;
658        let mut gamepad = Gamepad::default();
659        gamepad.analog_mut().set(axis, value);
660        gamepad.analog_mut().set(button, value);
661        gamepad.digital_mut().press(button);
662        world.spawn(gamepad);
663
664        let mut reader = state.get_mut(&mut world);
665        reader.set_gamepad(None);
666        assert_eq!(reader.value(button), 0.0.into());
667        assert_eq!(reader.value(axis), 0.0.into());
668        assert_eq!(reader.value(Binding::AnyKey), false.into());
669    }
670
671    #[test]
672    fn any_gamepad_axis_sum() {
673        let (mut world, mut state) = init_world();
674
675        let axis = GamepadAxis::LeftStickX;
676        let mut gamepad1 = Gamepad::default();
677        gamepad1.analog_mut().set(axis, 0.001);
678        world.spawn(gamepad1);
679
680        let mut gamepad2 = Gamepad::default();
681        gamepad2.analog_mut().set(axis, 0.002);
682        world.spawn(gamepad2);
683
684        let mut reader = state.get_mut(&mut world);
685        assert_eq!(reader.value(axis), 0.003.into());
686        assert_eq!(reader.value(GamepadAxis::RightStickX), 0.0.into());
687
688        reader.consume::<PreUpdate>(axis);
689        assert_eq!(reader.value(axis), 0.0.into());
690    }
691
692    #[test]
693    fn keyboard_with_modifier() {
694        let (mut world, mut state) = init_world();
695
696        let key = KeyCode::Space;
697        let modifier = KeyCode::ControlLeft;
698        let mut keys = world.resource_mut::<ButtonInput<KeyCode>>();
699        keys.press(modifier);
700        keys.press(key);
701
702        let binding = key.with_mod_keys(modifier.into());
703        let mut reader = state.get_mut(&mut world);
704        assert_eq!(reader.value(binding), true.into());
705        assert_eq!(reader.value(key), true.into());
706        assert_eq!(reader.value(Binding::AnyKey), true.into());
707        assert_eq!(
708            reader.value(binding.with_mod_keys(ModKeys::ALT)),
709            false.into()
710        );
711        assert_eq!(
712            reader.value(binding.with_mod_keys(ModKeys::CONTROL | ModKeys::ALT)),
713            false.into()
714        );
715
716        reader.consume::<PreUpdate>(binding);
717        assert_eq!(reader.value(binding), false.into());
718        assert_eq!(
719            reader.value(Binding::AnyKey),
720            true.into(),
721            "should still be pressed due to modifier"
722        );
723
724        // Try another key, but with the same modifier that was consumed.
725        let other_key = KeyCode::Enter;
726        world
727            .resource_mut::<ButtonInput<KeyCode>>()
728            .press(other_key);
729        let other_input = other_key.with_mod_keys(modifier.into());
730        let reader = state.get_mut(&mut world);
731        assert_eq!(reader.value(other_input), false.into());
732        assert_eq!(reader.value(other_key), true.into());
733    }
734
735    #[test]
736    fn mouse_button_with_modifier() {
737        let (mut world, mut state) = init_world();
738
739        let button = MouseButton::Left;
740        let modifier = KeyCode::AltLeft;
741        world.resource_mut::<ButtonInput<KeyCode>>().press(modifier);
742        world
743            .resource_mut::<ButtonInput<MouseButton>>()
744            .press(button);
745
746        let binding = button.with_mod_keys(modifier.into());
747        let mut reader = state.get_mut(&mut world);
748        assert_eq!(reader.value(binding), true.into());
749        assert_eq!(reader.value(button), true.into());
750        assert_eq!(reader.value(Binding::AnyKey), true.into());
751        assert_eq!(
752            reader.value(binding.with_mod_keys(ModKeys::CONTROL)),
753            false.into()
754        );
755        assert_eq!(
756            reader.value(binding.with_mod_keys(ModKeys::CONTROL | ModKeys::ALT)),
757            false.into()
758        );
759
760        reader.consume::<PreUpdate>(binding);
761        assert_eq!(reader.value(binding), false.into());
762        assert_eq!(
763            reader.value(Binding::AnyKey),
764            true.into(),
765            "should still be pressed due to modifier"
766        );
767    }
768
769    #[test]
770    fn mouse_motion_with_modifier() {
771        let (mut world, mut state) = init_world();
772
773        let value = Vec2::ONE;
774        let modifier = KeyCode::ShiftLeft;
775        world.resource_mut::<ButtonInput<KeyCode>>().press(modifier);
776        world.insert_resource(AccumulatedMouseMotion { delta: value });
777
778        let binding = Binding::mouse_motion().with_mod_keys(modifier.into());
779        let mut reader = state.get_mut(&mut world);
780        reader.clear_consumed::<PreUpdate>();
781        assert_eq!(reader.value(binding), value.into());
782        assert_eq!(reader.value(binding.without_mod_keys()), value.into());
783        assert_eq!(
784            reader.value(binding.with_mod_keys(ModKeys::SUPER)),
785            Vec2::ZERO.into()
786        );
787        assert_eq!(
788            reader.value(binding.with_mod_keys(ModKeys::SHIFT | ModKeys::SUPER)),
789            Vec2::ZERO.into()
790        );
791
792        reader.consume::<PreUpdate>(binding);
793        assert_eq!(reader.value(binding), Vec2::ZERO.into());
794    }
795
796    #[test]
797    fn mouse_wheel_with_modifier() {
798        let (mut world, mut state) = init_world();
799
800        let value = Vec2::ONE;
801        let modifier = KeyCode::SuperLeft;
802        world.resource_mut::<ButtonInput<KeyCode>>().press(modifier);
803        world.insert_resource(AccumulatedMouseScroll {
804            unit: MouseScrollUnit::Line,
805            delta: value,
806        });
807
808        let binding = Binding::mouse_wheel().with_mod_keys(modifier.into());
809        let mut reader = state.get_mut(&mut world);
810        reader.clear_consumed::<PreUpdate>();
811        assert_eq!(reader.value(binding), value.into());
812        assert_eq!(reader.value(binding.without_mod_keys()), value.into());
813        assert_eq!(
814            reader.value(binding.with_mod_keys(ModKeys::SHIFT)),
815            Vec2::ZERO.into()
816        );
817        assert_eq!(
818            reader.value(binding.with_mod_keys(ModKeys::SHIFT | ModKeys::SUPER)),
819            Vec2::ZERO.into()
820        );
821
822        reader.consume::<PreUpdate>(binding);
823        assert_eq!(reader.value(binding), Vec2::ZERO.into());
824    }
825
826    #[test]
827    fn sources() {
828        let (mut world, mut state) = init_world();
829
830        let key = KeyCode::Space;
831        let mouse_button = MouseButton::Left;
832        let gamepad_button = GamepadButton::South;
833        let axis = GamepadAxis::LeftStickX;
834
835        world.resource_mut::<ButtonInput<KeyCode>>().press(key);
836        world
837            .resource_mut::<ButtonInput<MouseButton>>()
838            .press(mouse_button);
839
840        world.insert_resource(AccumulatedMouseMotion { delta: Vec2::ONE });
841        world.insert_resource(AccumulatedMouseScroll {
842            unit: MouseScrollUnit::Line,
843            delta: Vec2::ONE,
844        });
845
846        let mut gamepad = Gamepad::default();
847        gamepad.analog_mut().set(axis, 1.0);
848        gamepad.analog_mut().set(gamepad_button, 1.0);
849        world.spawn(gamepad);
850
851        let mut action_sources = world.resource_mut::<ActionSources>();
852        action_sources.keyboard = false;
853        action_sources.mouse_buttons = false;
854        action_sources.mouse_motion = false;
855        action_sources.mouse_wheel = false;
856        action_sources.gamepad_button = false;
857        action_sources.gamepad_axis = false;
858
859        let mut reader = state.get_mut(&mut world);
860        reader.clear_consumed::<PreUpdate>();
861
862        assert_eq!(reader.value(key), false.into());
863        assert_eq!(reader.value(mouse_button), false.into());
864        assert_eq!(reader.value(Binding::mouse_motion()), Vec2::ZERO.into());
865        assert_eq!(reader.value(Binding::mouse_wheel()), Vec2::ZERO.into());
866        assert_eq!(reader.value(gamepad_button), 0.0.into());
867        assert_eq!(reader.value(axis), 0.0.into());
868    }
869
870    fn init_world<'w, 's>() -> (World, SystemState<InputReader<'w, 's>>) {
871        let mut world = World::new();
872        world.init_resource::<ButtonInput<KeyCode>>();
873        world.init_resource::<ButtonInput<MouseButton>>();
874        world.init_resource::<Messages<MouseMotion>>();
875        world.init_resource::<Messages<MouseWheel>>();
876        world.init_resource::<ButtonInput<GamepadButton>>();
877        world.init_resource::<Axis<GamepadAxis>>();
878        world.init_resource::<AccumulatedMouseMotion>();
879        world.init_resource::<AccumulatedMouseScroll>();
880        world.init_resource::<ConsumedInputs>();
881        world.init_resource::<PendingBindings>();
882        world.init_resource::<ActionSources>();
883
884        let state = SystemState::<InputReader>::new(&mut world);
885
886        (world, state)
887    }
888}