bevy_input/
gamepad.rs

1//! The gamepad input functionality.
2
3use crate::{Axis, ButtonInput, ButtonState};
4use bevy_core::Name;
5use bevy_ecs::{
6    change_detection::DetectChangesMut,
7    component::Component,
8    entity::Entity,
9    event::{Event, EventReader, EventWriter},
10    system::{Commands, Query},
11};
12use bevy_math::Vec2;
13#[cfg(feature = "bevy_reflect")]
14use bevy_reflect::{std_traits::ReflectDefault, Reflect};
15#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
16use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
17use bevy_utils::{
18    tracing::{info, warn},
19    Duration, HashMap,
20};
21use derive_more::derive::{Display, Error, From};
22
23/// A gamepad event.
24///
25/// This event type is used over the [`GamepadConnectionEvent`],
26/// [`GamepadButtonChangedEvent`] and [`GamepadAxisChangedEvent`] when
27/// the in-frame relative ordering of events is important.
28///
29/// This event is produced by `bevy_input`
30#[derive(Event, Debug, Clone, PartialEq, From)]
31#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
32#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
33#[cfg_attr(
34    all(feature = "serialize", feature = "bevy_reflect"),
35    reflect(Serialize, Deserialize)
36)]
37pub enum GamepadEvent {
38    /// A gamepad has been connected or disconnected.
39    Connection(GamepadConnectionEvent),
40    /// A button of the gamepad has been triggered.
41    Button(GamepadButtonChangedEvent),
42    /// An axis of the gamepad has been triggered.
43    Axis(GamepadAxisChangedEvent),
44}
45
46/// A raw gamepad event.
47///
48/// This event type is used over the [`GamepadConnectionEvent`],
49/// [`RawGamepadButtonChangedEvent`] and [`RawGamepadAxisChangedEvent`] when
50/// the in-frame relative ordering of events is important.
51///
52/// This event type is used by `bevy_input` to feed its components.
53#[derive(Event, Debug, Clone, PartialEq, Reflect, From)]
54#[reflect(Debug, PartialEq)]
55#[cfg_attr(
56    feature = "serialize",
57    derive(serde::Serialize, serde::Deserialize),
58    reflect(Serialize, Deserialize)
59)]
60pub enum RawGamepadEvent {
61    /// A gamepad has been connected or disconnected.
62    Connection(GamepadConnectionEvent),
63    /// A button of the gamepad has been triggered.
64    Button(RawGamepadButtonChangedEvent),
65    /// An axis of the gamepad has been triggered.
66    Axis(RawGamepadAxisChangedEvent),
67}
68
69/// [`GamepadButton`] changed event unfiltered by [`GamepadSettings`]
70#[derive(Event, Debug, Copy, Clone, PartialEq, Reflect)]
71#[reflect(Debug, PartialEq)]
72#[cfg_attr(
73    feature = "serialize",
74    derive(serde::Serialize, serde::Deserialize),
75    reflect(Serialize, Deserialize)
76)]
77pub struct RawGamepadButtonChangedEvent {
78    /// The gamepad on which the button is triggered.
79    pub gamepad: Entity,
80    /// The type of the triggered button.
81    pub button: GamepadButton,
82    /// The value of the button.
83    pub value: f32,
84}
85
86impl RawGamepadButtonChangedEvent {
87    /// Creates a [`RawGamepadButtonChangedEvent`].
88    pub fn new(gamepad: Entity, button_type: GamepadButton, value: f32) -> Self {
89        Self {
90            gamepad,
91            button: button_type,
92            value,
93        }
94    }
95}
96
97/// [`GamepadAxis`] changed event unfiltered by [`GamepadSettings`]
98#[derive(Event, Debug, Copy, Clone, PartialEq, Reflect)]
99#[reflect(Debug, PartialEq)]
100#[cfg_attr(
101    feature = "serialize",
102    derive(serde::Serialize, serde::Deserialize),
103    reflect(Serialize, Deserialize)
104)]
105pub struct RawGamepadAxisChangedEvent {
106    /// The gamepad on which the axis is triggered.
107    pub gamepad: Entity,
108    /// The type of the triggered axis.
109    pub axis: GamepadAxis,
110    /// The value of the axis.
111    pub value: f32,
112}
113
114impl RawGamepadAxisChangedEvent {
115    /// Creates a [`RawGamepadAxisChangedEvent`].
116    pub fn new(gamepad: Entity, axis_type: GamepadAxis, value: f32) -> Self {
117        Self {
118            gamepad,
119            axis: axis_type,
120            value,
121        }
122    }
123}
124
125/// A Gamepad connection event. Created when a connection to a gamepad
126/// is established and when a gamepad is disconnected.
127#[derive(Event, Debug, Clone, PartialEq, Reflect)]
128#[reflect(Debug, PartialEq)]
129#[cfg_attr(
130    feature = "serialize",
131    derive(serde::Serialize, serde::Deserialize),
132    reflect(Serialize, Deserialize)
133)]
134pub struct GamepadConnectionEvent {
135    /// The gamepad whose connection status changed.
136    pub gamepad: Entity,
137    /// The change in the gamepads connection.
138    pub connection: GamepadConnection,
139}
140
141impl GamepadConnectionEvent {
142    /// Creates a [`GamepadConnectionEvent`].
143    pub fn new(gamepad: Entity, connection: GamepadConnection) -> Self {
144        Self {
145            gamepad,
146            connection,
147        }
148    }
149
150    /// Is the gamepad connected?
151    pub fn connected(&self) -> bool {
152        matches!(self.connection, GamepadConnection::Connected { .. })
153    }
154
155    /// Is the gamepad disconnected?
156    pub fn disconnected(&self) -> bool {
157        !self.connected()
158    }
159}
160
161/// [`GamepadButton`] event triggered by a digital state change
162#[derive(Event, Debug, Clone, Copy, PartialEq, Eq, Reflect)]
163#[reflect(Debug, PartialEq)]
164#[cfg_attr(
165    feature = "serialize",
166    derive(serde::Serialize, serde::Deserialize),
167    reflect(Serialize, Deserialize)
168)]
169pub struct GamepadButtonStateChangedEvent {
170    /// The entity that represents this gamepad.
171    pub entity: Entity,
172    /// The gamepad button assigned to the event.
173    pub button: GamepadButton,
174    /// The pressed state of the button.
175    pub state: ButtonState,
176}
177
178impl GamepadButtonStateChangedEvent {
179    /// Creates a new [`GamepadButtonStateChangedEvent`]
180    pub fn new(entity: Entity, button: GamepadButton, state: ButtonState) -> Self {
181        Self {
182            entity,
183            button,
184            state,
185        }
186    }
187}
188
189/// [`GamepadButton`] event triggered by an analog state change
190#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
191#[reflect(Debug, PartialEq)]
192#[cfg_attr(
193    feature = "serialize",
194    derive(serde::Serialize, serde::Deserialize),
195    reflect(Serialize, Deserialize)
196)]
197pub struct GamepadButtonChangedEvent {
198    /// The entity that represents this gamepad.
199    pub entity: Entity,
200    /// The gamepad button assigned to the event.
201    pub button: GamepadButton,
202    /// The pressed state of the button.
203    pub state: ButtonState,
204    /// The analog value of the button.
205    pub value: f32,
206}
207
208impl GamepadButtonChangedEvent {
209    /// Creates a new [`GamepadButtonChangedEvent`]
210    pub fn new(entity: Entity, button: GamepadButton, state: ButtonState, value: f32) -> Self {
211        Self {
212            entity,
213            button,
214            state,
215            value,
216        }
217    }
218}
219
220/// [`GamepadAxis`] event triggered by an analog state change
221#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
222#[reflect(Debug, PartialEq)]
223#[cfg_attr(
224    feature = "serialize",
225    derive(serde::Serialize, serde::Deserialize),
226    reflect(Serialize, Deserialize)
227)]
228pub struct GamepadAxisChangedEvent {
229    /// The entity that represents this gamepad.
230    pub entity: Entity,
231    /// The gamepad axis assigned to the event.
232    pub axis: GamepadAxis,
233    /// The value of this axis.
234    pub value: f32,
235}
236
237impl GamepadAxisChangedEvent {
238    /// Creates a new [`GamepadAxisChangedEvent`]
239    pub fn new(entity: Entity, axis: GamepadAxis, value: f32) -> Self {
240        Self {
241            entity,
242            axis,
243            value,
244        }
245    }
246}
247
248/// Errors that occur when setting axis settings for gamepad input.
249#[derive(Error, Display, Debug, PartialEq)]
250pub enum AxisSettingsError {
251    /// The given parameter `livezone_lowerbound` was not in range -1.0..=0.0.
252    #[display("invalid livezone_lowerbound {_0}, expected value [-1.0..=0.0]")]
253    #[error(ignore)]
254    LiveZoneLowerBoundOutOfRange(f32),
255    /// The given parameter `deadzone_lowerbound` was not in range -1.0..=0.0.
256    #[display("invalid deadzone_lowerbound {_0}, expected value [-1.0..=0.0]")]
257    #[error(ignore)]
258    DeadZoneLowerBoundOutOfRange(f32),
259    /// The given parameter `deadzone_lowerbound` was not in range -1.0..=0.0.
260    #[display("invalid deadzone_upperbound {_0}, expected value [0.0..=1.0]")]
261    #[error(ignore)]
262    DeadZoneUpperBoundOutOfRange(f32),
263    /// The given parameter `deadzone_lowerbound` was not in range -1.0..=0.0.
264    #[display("invalid livezone_upperbound {_0}, expected value [0.0..=1.0]")]
265    #[error(ignore)]
266    LiveZoneUpperBoundOutOfRange(f32),
267    /// Parameter `livezone_lowerbound` was not less than or equal to parameter `deadzone_lowerbound`.
268    #[display("invalid parameter values livezone_lowerbound {} deadzone_lowerbound {}, expected livezone_lowerbound <= deadzone_lowerbound", livezone_lowerbound, deadzone_lowerbound)]
269    LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
270        /// The value of the `livezone_lowerbound` parameter.
271        livezone_lowerbound: f32,
272        /// The value of the `deadzone_lowerbound` parameter.
273        deadzone_lowerbound: f32,
274    },
275    ///  Parameter `deadzone_upperbound` was not less than or equal to parameter `livezone_upperbound`.
276    #[display("invalid parameter values livezone_upperbound {} deadzone_upperbound {}, expected deadzone_upperbound <= livezone_upperbound", livezone_upperbound, deadzone_upperbound)]
277    DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
278        /// The value of the `livezone_upperbound` parameter.
279        livezone_upperbound: f32,
280        /// The value of the `deadzone_upperbound` parameter.
281        deadzone_upperbound: f32,
282    },
283    /// The given parameter was not in range 0.0..=2.0.
284    #[display("invalid threshold {_0}, expected 0.0 <= threshold <= 2.0")]
285    #[error(ignore)]
286    Threshold(f32),
287}
288
289/// Errors that occur when setting button settings for gamepad input.
290#[derive(Error, Display, Debug, PartialEq)]
291pub enum ButtonSettingsError {
292    /// The given parameter was not in range 0.0..=1.0.
293    #[display("invalid release_threshold {_0}, expected value [0.0..=1.0]")]
294    #[error(ignore)]
295    ReleaseThresholdOutOfRange(f32),
296    /// The given parameter was not in range 0.0..=1.0.
297    #[display("invalid press_threshold {_0}, expected [0.0..=1.0]")]
298    #[error(ignore)]
299    PressThresholdOutOfRange(f32),
300    /// Parameter `release_threshold` was not less than or equal to `press_threshold`.
301    #[display("invalid parameter values release_threshold {} press_threshold {}, expected release_threshold <= press_threshold", release_threshold, press_threshold)]
302    ReleaseThresholdGreaterThanPressThreshold {
303        /// The value of the `press_threshold` parameter.
304        press_threshold: f32,
305        /// The value of the `release_threshold` parameter.
306        release_threshold: f32,
307    },
308}
309
310/// Stores a connected gamepad's metadata such as the name and its [`GamepadButton`] and [`GamepadAxis`].
311///
312/// An entity with this component is spawned automatically after [`GamepadConnectionEvent`]
313/// and updated by [`gamepad_event_processing_system`].
314///
315/// See also [`GamepadSettings`] for configuration.
316///
317/// # Examples
318///
319/// ```
320/// # use bevy_input::gamepad::{Gamepad, GamepadAxis, GamepadButton};
321/// # use bevy_ecs::system::Query;
322/// # use bevy_core::Name;
323/// #
324/// fn gamepad_usage_system(gamepads: Query<(&Name, &Gamepad)>) {
325///     for (name, gamepad) in &gamepads {
326///         println!("{name}");
327///
328///         if gamepad.just_pressed(GamepadButton::North) {
329///             println!("{} just pressed North", name)
330///         }
331///
332///         if let Some(left_stick_x) = gamepad.get(GamepadAxis::LeftStickX)  {
333///             println!("left stick X: {}", left_stick_x)
334///         }
335///     }
336/// }
337/// ```
338#[derive(Component, Debug)]
339#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug))]
340#[require(GamepadSettings)]
341pub struct Gamepad {
342    /// The USB vendor ID as assigned by the USB-IF, if available.
343    pub(crate) vendor_id: Option<u16>,
344
345    /// The USB product ID as assigned by the [vendor], if available.
346    ///
347    /// [vendor]: Self::vendor_id
348    pub(crate) product_id: Option<u16>,
349
350    /// [`ButtonInput`] of [`GamepadButton`] representing their digital state
351    pub(crate) digital: ButtonInput<GamepadButton>,
352
353    /// [`Axis`] of [`GamepadButton`] representing their analog state.
354    pub(crate) analog: Axis<GamepadInput>,
355}
356
357impl Gamepad {
358    /// Returns the USB vendor ID as assigned by the USB-IF, if available.
359    pub fn vendor_id(&self) -> Option<u16> {
360        self.vendor_id
361    }
362
363    /// Returns the USB product ID as assigned by the [vendor], if available.
364    ///
365    /// [vendor]: Self::vendor_id
366    pub fn product_id(&self) -> Option<u16> {
367        self.product_id
368    }
369
370    /// Returns the analog data of the provided [`GamepadAxis`] or [`GamepadButton`].
371    ///
372    /// This will be clamped between [[`Axis::MIN`],[`Axis::MAX`]].
373    pub fn get(&self, input: impl Into<GamepadInput>) -> Option<f32> {
374        self.analog.get(input.into())
375    }
376
377    /// Returns the unclamped analog data of the provided [`GamepadAxis`] or [`GamepadButton`].
378    ///
379    /// This value may be outside the [`Axis::MIN`] and [`Axis::MAX`] range.
380    pub fn get_unclamped(&self, input: impl Into<GamepadInput>) -> Option<f32> {
381        self.analog.get_unclamped(input.into())
382    }
383
384    /// Returns the left stick as a [`Vec2`]
385    pub fn left_stick(&self) -> Vec2 {
386        Vec2 {
387            x: self.get(GamepadAxis::LeftStickX).unwrap_or(0.0),
388            y: self.get(GamepadAxis::LeftStickY).unwrap_or(0.0),
389        }
390    }
391
392    /// Returns the right stick as a [`Vec2`]
393    pub fn right_stick(&self) -> Vec2 {
394        Vec2 {
395            x: self.get(GamepadAxis::RightStickX).unwrap_or(0.0),
396            y: self.get(GamepadAxis::RightStickY).unwrap_or(0.0),
397        }
398    }
399
400    /// Returns the directional pad as a [`Vec2`]
401    pub fn dpad(&self) -> Vec2 {
402        Vec2 {
403            x: self.get(GamepadButton::DPadRight).unwrap_or(0.0)
404                - self.get(GamepadButton::DPadLeft).unwrap_or(0.0),
405            y: self.get(GamepadButton::DPadUp).unwrap_or(0.0)
406                - self.get(GamepadButton::DPadDown).unwrap_or(0.0),
407        }
408    }
409
410    /// Returns `true` if the [`GamepadButton`] has been pressed.
411    pub fn pressed(&self, button_type: GamepadButton) -> bool {
412        self.digital.pressed(button_type)
413    }
414
415    /// Returns `true` if any item in the [`GamepadButton`] iterator has been pressed.
416    pub fn any_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
417        self.digital.any_pressed(button_inputs)
418    }
419
420    /// Returns `true` if all items in the [`GamepadButton`] iterator have been pressed.
421    pub fn all_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
422        self.digital.all_pressed(button_inputs)
423    }
424
425    /// Returns `true` if the [`GamepadButton`] has been pressed during the current frame.
426    ///
427    /// Note: This function does not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_released`].
428    pub fn just_pressed(&self, button_type: GamepadButton) -> bool {
429        self.digital.just_pressed(button_type)
430    }
431
432    /// Returns `true` if any item in the [`GamepadButton`] iterator has been pressed during the current frame.
433    pub fn any_just_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
434        self.digital.any_just_pressed(button_inputs)
435    }
436
437    /// Returns `true` if all items in the [`GamepadButton`] iterator have been just pressed.
438    pub fn all_just_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
439        self.digital.all_just_pressed(button_inputs)
440    }
441
442    /// Returns `true` if the [`GamepadButton`] has been released during the current frame.
443    ///
444    /// Note: This function does not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_pressed`].
445    pub fn just_released(&self, button_type: GamepadButton) -> bool {
446        self.digital.just_released(button_type)
447    }
448
449    /// Returns `true` if any item in the [`GamepadButton`] iterator has just been released.
450    pub fn any_just_released(
451        &self,
452        button_inputs: impl IntoIterator<Item = GamepadButton>,
453    ) -> bool {
454        self.digital.any_just_released(button_inputs)
455    }
456
457    /// Returns `true` if all items in the [`GamepadButton`] iterator have just been released.
458    pub fn all_just_released(
459        &self,
460        button_inputs: impl IntoIterator<Item = GamepadButton>,
461    ) -> bool {
462        self.digital.all_just_released(button_inputs)
463    }
464
465    /// Returns an iterator over all digital [button]s that are pressed.
466    ///
467    /// [button]: GamepadButton
468    pub fn get_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
469        self.digital.get_pressed()
470    }
471
472    /// Returns an iterator over all digital [button]s that were just pressed.
473    ///
474    /// [button]: GamepadButton
475    pub fn get_just_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
476        self.digital.get_just_pressed()
477    }
478
479    /// Returns an iterator over all digital [button]s that were just released.
480    ///
481    /// [button]: GamepadButton
482    pub fn get_just_released(&self) -> impl Iterator<Item = &GamepadButton> {
483        self.digital.get_just_released()
484    }
485
486    /// Returns an iterator over all analog [axes].
487    ///
488    /// [axes]: GamepadInput
489    pub fn get_analog_axes(&self) -> impl Iterator<Item = &GamepadInput> {
490        self.analog.all_axes()
491    }
492
493    /// [`ButtonInput`] of [`GamepadButton`] representing their digital state
494    pub fn digital(&self) -> &ButtonInput<GamepadButton> {
495        &self.digital
496    }
497
498    /// Mutable [`ButtonInput`] of [`GamepadButton`] representing their digital state. Useful for mocking inputs.
499    pub fn digital_mut(&mut self) -> &mut ButtonInput<GamepadButton> {
500        &mut self.digital
501    }
502
503    /// [`Axis`] of [`GamepadButton`] representing their analog state.
504    pub fn analog(&self) -> &Axis<GamepadInput> {
505        &self.analog
506    }
507
508    /// Mutable [`Axis`] of [`GamepadButton`] representing their analog state. Useful for mocking inputs.
509    pub fn analog_mut(&mut self) -> &mut Axis<GamepadInput> {
510        &mut self.analog
511    }
512}
513
514impl Default for Gamepad {
515    fn default() -> Self {
516        let mut analog = Axis::default();
517        for button in GamepadButton::all().iter().copied() {
518            analog.set(button, 0.0);
519        }
520        for axis_type in GamepadAxis::all().iter().copied() {
521            analog.set(axis_type, 0.0);
522        }
523
524        Self {
525            vendor_id: None,
526            product_id: None,
527            digital: Default::default(),
528            analog,
529        }
530    }
531}
532
533/// Represents gamepad input types that are mapped in the range [0.0, 1.0].
534///
535/// ## Usage
536///
537/// This is used to determine which button has changed its value when receiving gamepad button events
538/// It is also used in the [`Gamepad`] component.
539#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
540#[cfg_attr(
541    feature = "bevy_reflect",
542    derive(Reflect),
543    reflect(Debug, Hash, PartialEq)
544)]
545#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
546#[cfg_attr(
547    all(feature = "serialize", feature = "bevy_reflect"),
548    reflect(Serialize, Deserialize)
549)]
550pub enum GamepadButton {
551    /// The bottom action button of the action pad (i.e. PS: Cross, Xbox: A).
552    South,
553    /// The right action button of the action pad (i.e. PS: Circle, Xbox: B).
554    East,
555    /// The upper action button of the action pad (i.e. PS: Triangle, Xbox: Y).
556    North,
557    /// The left action button of the action pad (i.e. PS: Square, Xbox: X).
558    West,
559
560    /// The C button.
561    C,
562    /// The Z button.
563    Z,
564
565    /// The first left trigger.
566    LeftTrigger,
567    /// The second left trigger.
568    LeftTrigger2,
569    /// The first right trigger.
570    RightTrigger,
571    /// The second right trigger.
572    RightTrigger2,
573    /// The select button.
574    Select,
575    /// The start button.
576    Start,
577    /// The mode button.
578    Mode,
579
580    /// The left thumb stick button.
581    LeftThumb,
582    /// The right thumb stick button.
583    RightThumb,
584
585    /// The up button of the D-Pad.
586    DPadUp,
587    /// The down button of the D-Pad.
588    DPadDown,
589    /// The left button of the D-Pad.
590    DPadLeft,
591    /// The right button of the D-Pad.
592    DPadRight,
593
594    /// Miscellaneous buttons, considered non-standard (i.e. Extra buttons on a flight stick that do not have a gamepad equivalent).
595    Other(u8),
596}
597
598impl GamepadButton {
599    /// Returns an array of all the standard [`GamepadButton`]
600    pub const fn all() -> [GamepadButton; 19] {
601        [
602            GamepadButton::South,
603            GamepadButton::East,
604            GamepadButton::North,
605            GamepadButton::West,
606            GamepadButton::C,
607            GamepadButton::Z,
608            GamepadButton::LeftTrigger,
609            GamepadButton::LeftTrigger2,
610            GamepadButton::RightTrigger,
611            GamepadButton::RightTrigger2,
612            GamepadButton::Select,
613            GamepadButton::Start,
614            GamepadButton::Mode,
615            GamepadButton::LeftThumb,
616            GamepadButton::RightThumb,
617            GamepadButton::DPadUp,
618            GamepadButton::DPadDown,
619            GamepadButton::DPadLeft,
620            GamepadButton::DPadRight,
621        ]
622    }
623}
624
625/// Represents gamepad input types that are mapped in the range [-1.0, 1.0]
626///
627/// ## Usage
628///
629/// This is used to determine which axis has changed its value when receiving a
630/// gamepad axis event. It is also used in the [`Gamepad`] component.
631#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
632#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
633#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
634#[cfg_attr(
635    all(feature = "serialize", feature = "bevy_reflect"),
636    reflect(Serialize, Deserialize)
637)]
638pub enum GamepadAxis {
639    /// The horizontal value of the left stick.
640    LeftStickX,
641    /// The vertical value of the left stick.
642    LeftStickY,
643    /// The value of the left `Z` button.
644    LeftZ,
645
646    /// The horizontal value of the right stick.
647    RightStickX,
648    /// The vertical value of the right stick.
649    RightStickY,
650    /// The value of the right `Z` button.
651    RightZ,
652
653    /// Non-standard support for other axis types (i.e. HOTAS sliders, potentiometers, etc).
654    Other(u8),
655}
656
657impl GamepadAxis {
658    /// Returns an array of all the standard [`GamepadAxis`].
659    pub const fn all() -> [GamepadAxis; 6] {
660        [
661            GamepadAxis::LeftStickX,
662            GamepadAxis::LeftStickY,
663            GamepadAxis::LeftZ,
664            GamepadAxis::RightStickX,
665            GamepadAxis::RightStickY,
666            GamepadAxis::RightZ,
667        ]
668    }
669}
670
671/// Encapsulation over [`GamepadAxis`] and [`GamepadButton`]
672// This is done so Gamepad can share a single Axis<T> and simplifies the API by having only one get/get_unclamped method
673#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, From)]
674#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
675pub enum GamepadInput {
676    /// A [`GamepadAxis`]
677    Axis(GamepadAxis),
678    /// A [`GamepadButton`]
679    Button(GamepadButton),
680}
681
682/// Gamepad settings component.
683///
684/// ## Usage
685///
686/// It is used to create a `bevy` component that stores the settings of [`GamepadButton`] and [`GamepadAxis`] in [`Gamepad`].
687/// If no user defined [`ButtonSettings`], [`AxisSettings`], or [`ButtonAxisSettings`]
688/// are defined, the default settings of each are used as a fallback accordingly.
689///
690/// ## Note
691///
692/// The [`GamepadSettings`] are used to determine when raw gamepad events
693/// should register. Events that don't meet the change thresholds defined in [`GamepadSettings`]
694/// will not register. To modify these settings, mutate the corresponding component.
695#[derive(Component, Clone, Default, Debug)]
696#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Default))]
697pub struct GamepadSettings {
698    /// The default button settings.
699    pub default_button_settings: ButtonSettings,
700    /// The default axis settings.
701    pub default_axis_settings: AxisSettings,
702    /// The default button axis settings.
703    pub default_button_axis_settings: ButtonAxisSettings,
704    /// The user defined button settings.
705    pub button_settings: HashMap<GamepadButton, ButtonSettings>,
706    /// The user defined axis settings.
707    pub axis_settings: HashMap<GamepadAxis, AxisSettings>,
708    /// The user defined button axis settings.
709    pub button_axis_settings: HashMap<GamepadButton, ButtonAxisSettings>,
710}
711
712impl GamepadSettings {
713    /// Returns the [`ButtonSettings`] of the [`GamepadButton`].
714    ///
715    /// If no user defined [`ButtonSettings`] are specified the default [`ButtonSettings`] get returned.
716    ///
717    /// # Examples
718    ///
719    /// ```
720    /// # use bevy_input::gamepad::{GamepadSettings, GamepadButton};
721    /// #
722    /// # let settings = GamepadSettings::default();
723    /// let button_settings = settings.get_button_settings(GamepadButton::South);
724    /// ```
725    pub fn get_button_settings(&self, button: GamepadButton) -> &ButtonSettings {
726        self.button_settings
727            .get(&button)
728            .unwrap_or(&self.default_button_settings)
729    }
730
731    /// Returns the [`AxisSettings`] of the [`GamepadAxis`].
732    ///
733    /// If no user defined [`AxisSettings`] are specified the default [`AxisSettings`] get returned.
734    ///
735    /// # Examples
736    ///
737    /// ```
738    /// # use bevy_input::gamepad::{GamepadSettings, GamepadAxis};
739    /// #
740    /// # let settings = GamepadSettings::default();
741    /// let axis_settings = settings.get_axis_settings(GamepadAxis::LeftStickX);
742    /// ```
743    pub fn get_axis_settings(&self, axis: GamepadAxis) -> &AxisSettings {
744        self.axis_settings
745            .get(&axis)
746            .unwrap_or(&self.default_axis_settings)
747    }
748
749    /// Returns the [`ButtonAxisSettings`] of the [`GamepadButton`].
750    ///
751    /// If no user defined [`ButtonAxisSettings`] are specified the default [`ButtonAxisSettings`] get returned.
752    ///
753    /// # Examples
754    ///
755    /// ```
756    /// # use bevy_input::gamepad::{GamepadSettings, GamepadButton};
757    /// #
758    /// # let settings = GamepadSettings::default();
759    /// let button_axis_settings = settings.get_button_axis_settings(GamepadButton::South);
760    /// ```
761    pub fn get_button_axis_settings(&self, button: GamepadButton) -> &ButtonAxisSettings {
762        self.button_axis_settings
763            .get(&button)
764            .unwrap_or(&self.default_button_axis_settings)
765    }
766}
767
768/// Manages settings for gamepad buttons.
769///
770/// It is used inside [`GamepadSettings`] to define the threshold for a [`GamepadButton`]
771/// to be considered pressed or released. A button is considered pressed if the `press_threshold`
772/// value is surpassed and released if the `release_threshold` value is undercut.
773///
774/// Allowed values: `0.0 <= ``release_threshold`` <= ``press_threshold`` <= 1.0`
775#[derive(Debug, PartialEq, Clone)]
776#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Default))]
777pub struct ButtonSettings {
778    press_threshold: f32,
779    release_threshold: f32,
780}
781
782impl Default for ButtonSettings {
783    fn default() -> Self {
784        ButtonSettings {
785            press_threshold: 0.75,
786            release_threshold: 0.65,
787        }
788    }
789}
790
791impl ButtonSettings {
792    /// Creates a new [`ButtonSettings`] instance.
793    ///
794    /// # Parameters
795    ///
796    /// + `press_threshold` is the button input value above which the button is considered pressed.
797    /// + `release_threshold` is the button input value below which the button is considered released.
798    ///
799    /// Restrictions:
800    /// + `0.0 <= ``release_threshold`` <= ``press_threshold`` <= 1.0`
801    ///
802    /// # Errors
803    ///
804    /// If the restrictions are not met, returns one of
805    /// `GamepadSettingsError::ButtonReleaseThresholdOutOfRange`,
806    /// `GamepadSettingsError::ButtonPressThresholdOutOfRange`, or
807    /// `GamepadSettingsError::ButtonReleaseThresholdGreaterThanPressThreshold`.
808    pub fn new(
809        press_threshold: f32,
810        release_threshold: f32,
811    ) -> Result<ButtonSettings, ButtonSettingsError> {
812        if !(0.0..=1.0).contains(&release_threshold) {
813            Err(ButtonSettingsError::ReleaseThresholdOutOfRange(
814                release_threshold,
815            ))
816        } else if !(0.0..=1.0).contains(&press_threshold) {
817            Err(ButtonSettingsError::PressThresholdOutOfRange(
818                press_threshold,
819            ))
820        } else if release_threshold > press_threshold {
821            Err(
822                ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
823                    press_threshold,
824                    release_threshold,
825                },
826            )
827        } else {
828            Ok(ButtonSettings {
829                press_threshold,
830                release_threshold,
831            })
832        }
833    }
834
835    /// Returns `true` if the button is pressed.
836    ///
837    /// A button is considered pressed if the `value` passed is greater than or equal to the press threshold.
838    pub fn is_pressed(&self, value: f32) -> bool {
839        value >= self.press_threshold
840    }
841
842    /// Returns `true` if the button is released.
843    ///
844    /// A button is considered released if the `value` passed is lower than or equal to the release threshold.
845    pub fn is_released(&self, value: f32) -> bool {
846        value <= self.release_threshold
847    }
848
849    /// Get the button input threshold above which the button is considered pressed.
850    pub fn press_threshold(&self) -> f32 {
851        self.press_threshold
852    }
853
854    /// Try to set the button input threshold above which the button is considered pressed.
855    ///
856    /// # Errors
857    ///
858    /// If the value passed is outside the range [release threshold..=1.0], returns either
859    /// `GamepadSettingsError::ButtonPressThresholdOutOfRange` or
860    /// `GamepadSettingsError::ButtonReleaseThresholdGreaterThanPressThreshold`.
861    pub fn try_set_press_threshold(&mut self, value: f32) -> Result<(), ButtonSettingsError> {
862        if (self.release_threshold..=1.0).contains(&value) {
863            self.press_threshold = value;
864            Ok(())
865        } else if !(0.0..1.0).contains(&value) {
866            Err(ButtonSettingsError::PressThresholdOutOfRange(value))
867        } else {
868            Err(
869                ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
870                    press_threshold: value,
871                    release_threshold: self.release_threshold,
872                },
873            )
874        }
875    }
876
877    /// Try to set the button input threshold above which the button is considered pressed.
878    /// If the value passed is outside the range [release threshold..=1.0], the value will not be changed.
879    ///
880    /// Returns the new value of the press threshold.
881    pub fn set_press_threshold(&mut self, value: f32) -> f32 {
882        self.try_set_press_threshold(value).ok();
883        self.press_threshold
884    }
885
886    /// Get the button input threshold below which the button is considered released.
887    pub fn release_threshold(&self) -> f32 {
888        self.release_threshold
889    }
890
891    /// Try to set the button input threshold below which the button is considered released.
892    ///
893    /// # Errors
894    ///
895    /// If the value passed is outside the range [0.0..=press threshold], returns
896    /// `ButtonSettingsError::ReleaseThresholdOutOfRange` or
897    /// `ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold`.
898    pub fn try_set_release_threshold(&mut self, value: f32) -> Result<(), ButtonSettingsError> {
899        if (0.0..=self.press_threshold).contains(&value) {
900            self.release_threshold = value;
901            Ok(())
902        } else if !(0.0..1.0).contains(&value) {
903            Err(ButtonSettingsError::ReleaseThresholdOutOfRange(value))
904        } else {
905            Err(
906                ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
907                    press_threshold: self.press_threshold,
908                    release_threshold: value,
909                },
910            )
911        }
912    }
913
914    /// Try to set the button input threshold below which the button is considered released. If the
915    /// value passed is outside the range [0.0..=press threshold], the value will not be changed.
916    ///
917    /// Returns the new value of the release threshold.
918    pub fn set_release_threshold(&mut self, value: f32) -> f32 {
919        self.try_set_release_threshold(value).ok();
920        self.release_threshold
921    }
922}
923
924/// Settings for a [`GamepadAxis`].
925///
926/// It is used inside the [`GamepadSettings`] to define the sensitivity range and
927/// threshold for an axis.
928/// Values that are higher than `livezone_upperbound` will be rounded up to 1.0.
929/// Values that are lower than `livezone_lowerbound` will be rounded down to -1.0.
930/// Values that are in-between `deadzone_lowerbound` and `deadzone_upperbound` will be rounded
931/// to 0.0.
932/// Otherwise, values will not be rounded.
933///
934/// The valid range is `[-1.0, 1.0]`.
935#[derive(Debug, Clone, PartialEq)]
936#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Default))]
937pub struct AxisSettings {
938    /// Values that are higher than `livezone_upperbound` will be rounded up to 1.0.
939    livezone_upperbound: f32,
940    /// Positive values that are less than `deadzone_upperbound` will be rounded down to 0.0.
941    deadzone_upperbound: f32,
942    /// Negative values that are greater than `deadzone_lowerbound` will be rounded up to 0.0.
943    deadzone_lowerbound: f32,
944    /// Values that are lower than `livezone_lowerbound` will be rounded down to -1.0.
945    livezone_lowerbound: f32,
946    /// `threshold` defines the minimum difference between old and new values to apply the changes.
947    threshold: f32,
948}
949
950impl Default for AxisSettings {
951    fn default() -> Self {
952        AxisSettings {
953            livezone_upperbound: 1.0,
954            deadzone_upperbound: 0.05,
955            deadzone_lowerbound: -0.05,
956            livezone_lowerbound: -1.0,
957            threshold: 0.01,
958        }
959    }
960}
961
962impl AxisSettings {
963    /// Creates a new [`AxisSettings`] instance.
964    ///
965    /// # Arguments
966    ///
967    /// + `livezone_lowerbound` - the value below which inputs will be rounded down to -1.0.
968    /// + `deadzone_lowerbound` - the value above which negative inputs will be rounded up to 0.0.
969    /// + `deadzone_upperbound` - the value below which positive inputs will be rounded down to 0.0.
970    /// + `livezone_upperbound` - the value above which inputs will be rounded up to 1.0.
971    /// + `threshold` - the minimum value by which input must change before the change is registered.
972    ///
973    /// Restrictions:
974    ///
975    /// + `-1.0 <= livezone_lowerbound <= deadzone_lowerbound <= 0.0`
976    /// + `0.0 <= deadzone_upperbound <= livezone_upperbound <= 1.0`
977    /// + `0.0 <= threshold <= 2.0`
978    ///
979    /// # Errors
980    ///
981    /// Returns an [`AxisSettingsError`] if any restrictions on the zone values are not met.
982    /// If the zone restrictions are met, but the `threshold` value restrictions are not met,
983    /// returns [`AxisSettingsError::Threshold`].
984    pub fn new(
985        livezone_lowerbound: f32,
986        deadzone_lowerbound: f32,
987        deadzone_upperbound: f32,
988        livezone_upperbound: f32,
989        threshold: f32,
990    ) -> Result<AxisSettings, AxisSettingsError> {
991        if !(-1.0..=0.0).contains(&livezone_lowerbound) {
992            Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(
993                livezone_lowerbound,
994            ))
995        } else if !(-1.0..=0.0).contains(&deadzone_lowerbound) {
996            Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(
997                deadzone_lowerbound,
998            ))
999        } else if !(0.0..=1.0).contains(&deadzone_upperbound) {
1000            Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(
1001                deadzone_upperbound,
1002            ))
1003        } else if !(0.0..=1.0).contains(&livezone_upperbound) {
1004            Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(
1005                livezone_upperbound,
1006            ))
1007        } else if livezone_lowerbound > deadzone_lowerbound {
1008            Err(
1009                AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1010                    livezone_lowerbound,
1011                    deadzone_lowerbound,
1012                },
1013            )
1014        } else if deadzone_upperbound > livezone_upperbound {
1015            Err(
1016                AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1017                    livezone_upperbound,
1018                    deadzone_upperbound,
1019                },
1020            )
1021        } else if !(0.0..=2.0).contains(&threshold) {
1022            Err(AxisSettingsError::Threshold(threshold))
1023        } else {
1024            Ok(Self {
1025                livezone_lowerbound,
1026                deadzone_lowerbound,
1027                deadzone_upperbound,
1028                livezone_upperbound,
1029                threshold,
1030            })
1031        }
1032    }
1033
1034    /// Get the value above which inputs will be rounded up to 1.0.
1035    pub fn livezone_upperbound(&self) -> f32 {
1036        self.livezone_upperbound
1037    }
1038
1039    /// Try to set the value above which inputs will be rounded up to 1.0.
1040    ///
1041    /// # Errors
1042    ///
1043    /// If the value passed is less than the dead zone upper bound,
1044    /// returns `AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound`.
1045    /// If the value passed is not in range [0.0..=1.0], returns `AxisSettingsError::LiveZoneUpperBoundOutOfRange`.
1046    pub fn try_set_livezone_upperbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1047        if !(0.0..=1.0).contains(&value) {
1048            Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(value))
1049        } else if value < self.deadzone_upperbound {
1050            Err(
1051                AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1052                    livezone_upperbound: value,
1053                    deadzone_upperbound: self.deadzone_upperbound,
1054                },
1055            )
1056        } else {
1057            self.livezone_upperbound = value;
1058            Ok(())
1059        }
1060    }
1061
1062    /// Try to set the value above which inputs will be rounded up to 1.0.
1063    /// If the value passed is negative or less than `deadzone_upperbound`,
1064    /// the value will not be changed.
1065    ///
1066    /// Returns the new value of `livezone_upperbound`.
1067    pub fn set_livezone_upperbound(&mut self, value: f32) -> f32 {
1068        self.try_set_livezone_upperbound(value).ok();
1069        self.livezone_upperbound
1070    }
1071
1072    /// Get the value below which positive inputs will be rounded down to 0.0.
1073    pub fn deadzone_upperbound(&self) -> f32 {
1074        self.deadzone_upperbound
1075    }
1076
1077    /// Try to set the value below which positive inputs will be rounded down to 0.0.
1078    ///
1079    /// # Errors
1080    ///
1081    /// If the value passed is greater than the live zone upper bound,
1082    /// returns `AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound`.
1083    /// If the value passed is not in range [0.0..=1.0], returns `AxisSettingsError::DeadZoneUpperBoundOutOfRange`.
1084    pub fn try_set_deadzone_upperbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1085        if !(0.0..=1.0).contains(&value) {
1086            Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(value))
1087        } else if self.livezone_upperbound < value {
1088            Err(
1089                AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1090                    livezone_upperbound: self.livezone_upperbound,
1091                    deadzone_upperbound: value,
1092                },
1093            )
1094        } else {
1095            self.deadzone_upperbound = value;
1096            Ok(())
1097        }
1098    }
1099
1100    /// Try to set the value below which positive inputs will be rounded down to 0.0.
1101    /// If the value passed is negative or greater than `livezone_upperbound`,
1102    /// the value will not be changed.
1103    ///
1104    /// Returns the new value of `deadzone_upperbound`.
1105    pub fn set_deadzone_upperbound(&mut self, value: f32) -> f32 {
1106        self.try_set_deadzone_upperbound(value).ok();
1107        self.deadzone_upperbound
1108    }
1109
1110    /// Get the value below which negative inputs will be rounded down to -1.0.
1111    pub fn livezone_lowerbound(&self) -> f32 {
1112        self.livezone_lowerbound
1113    }
1114
1115    /// Try to set the value below which negative inputs will be rounded down to -1.0.
1116    ///
1117    /// # Errors
1118    ///
1119    /// If the value passed is less than the dead zone lower bound,
1120    /// returns `AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound`.
1121    /// If the value passed is not in range [-1.0..=0.0], returns `AxisSettingsError::LiveZoneLowerBoundOutOfRange`.
1122    pub fn try_set_livezone_lowerbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1123        if !(-1.0..=0.0).contains(&value) {
1124            Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(value))
1125        } else if value > self.deadzone_lowerbound {
1126            Err(
1127                AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1128                    livezone_lowerbound: value,
1129                    deadzone_lowerbound: self.deadzone_lowerbound,
1130                },
1131            )
1132        } else {
1133            self.livezone_lowerbound = value;
1134            Ok(())
1135        }
1136    }
1137
1138    /// Try to set the value below which negative inputs will be rounded down to -1.0.
1139    /// If the value passed is positive or greater than `deadzone_lowerbound`,
1140    /// the value will not be changed.
1141    ///
1142    /// Returns the new value of `livezone_lowerbound`.
1143    pub fn set_livezone_lowerbound(&mut self, value: f32) -> f32 {
1144        self.try_set_livezone_lowerbound(value).ok();
1145        self.livezone_lowerbound
1146    }
1147
1148    /// Get the value above which inputs will be rounded up to 0.0.
1149    pub fn deadzone_lowerbound(&self) -> f32 {
1150        self.deadzone_lowerbound
1151    }
1152
1153    /// Try to set the value above which inputs will be rounded up to 0.0.
1154    ///
1155    /// # Errors
1156    ///
1157    /// If the value passed is less than the live zone lower bound,
1158    /// returns `AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound`.
1159    /// If the value passed is not in range [-1.0..=0.0], returns `AxisSettingsError::DeadZoneLowerBoundOutOfRange`.
1160    pub fn try_set_deadzone_lowerbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1161        if !(-1.0..=0.0).contains(&value) {
1162            Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(value))
1163        } else if self.livezone_lowerbound > value {
1164            Err(
1165                AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1166                    livezone_lowerbound: self.livezone_lowerbound,
1167                    deadzone_lowerbound: value,
1168                },
1169            )
1170        } else {
1171            self.deadzone_lowerbound = value;
1172            Ok(())
1173        }
1174    }
1175
1176    /// Try to set the value above which inputs will be rounded up to 0.0.
1177    /// If the value passed is less than -1.0 or less than `livezone_lowerbound`,
1178    /// the value will not be changed.
1179    ///
1180    /// Returns the new value of `deadzone_lowerbound`.
1181    pub fn set_deadzone_lowerbound(&mut self, value: f32) -> f32 {
1182        self.try_set_deadzone_lowerbound(value).ok();
1183        self.deadzone_lowerbound
1184    }
1185
1186    /// Get the minimum value by which input must change before the change is registered.
1187    pub fn threshold(&self) -> f32 {
1188        self.threshold
1189    }
1190
1191    /// Try to set the minimum value by which input must change before the change is registered.
1192    ///
1193    /// # Errors
1194    ///
1195    /// If the value passed is not within [0.0..=2.0], returns `GamepadSettingsError::AxisThreshold`.
1196    pub fn try_set_threshold(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1197        if !(0.0..=2.0).contains(&value) {
1198            Err(AxisSettingsError::Threshold(value))
1199        } else {
1200            self.threshold = value;
1201            Ok(())
1202        }
1203    }
1204
1205    /// Try to set the minimum value by which input must change before the changes will be applied.
1206    /// If the value passed is not within [0.0..=2.0], the value will not be changed.
1207    ///
1208    /// Returns the new value of threshold.
1209    pub fn set_threshold(&mut self, value: f32) -> f32 {
1210        self.try_set_threshold(value).ok();
1211        self.threshold
1212    }
1213
1214    /// Clamps the `raw_value` according to the `AxisSettings`.
1215    pub fn clamp(&self, new_value: f32) -> f32 {
1216        if self.deadzone_lowerbound <= new_value && new_value <= self.deadzone_upperbound {
1217            0.0
1218        } else if new_value >= self.livezone_upperbound {
1219            1.0
1220        } else if new_value <= self.livezone_lowerbound {
1221            -1.0
1222        } else {
1223            new_value
1224        }
1225    }
1226
1227    /// Determines whether the change from `old_value` to `new_value` should
1228    /// be registered as a change, according to the [`AxisSettings`].
1229    fn should_register_change(&self, new_value: f32, old_value: Option<f32>) -> bool {
1230        if old_value.is_none() {
1231            return true;
1232        }
1233
1234        f32::abs(new_value - old_value.unwrap()) > self.threshold
1235    }
1236
1237    /// Filters the `new_value` based on the `old_value`, according to the [`AxisSettings`].
1238    ///
1239    /// Returns the clamped `new_value` if the change exceeds the settings threshold,
1240    /// and `None` otherwise.
1241    pub fn filter(&self, new_value: f32, old_value: Option<f32>) -> Option<f32> {
1242        let new_value = self.clamp(new_value);
1243
1244        if self.should_register_change(new_value, old_value) {
1245            return Some(new_value);
1246        }
1247        None
1248    }
1249}
1250
1251/// Settings for a [`GamepadButton`].
1252///
1253/// It is used inside the [`GamepadSettings`] to define the sensitivity range and
1254/// threshold for a button axis.
1255///
1256/// ## Logic
1257///
1258/// - Values that are higher than or equal to `high` will be rounded to 1.0.
1259/// - Values that are lower than or equal to `low` will be rounded to 0.0.
1260/// - Otherwise, values will not be rounded.
1261///
1262/// The valid range is from 0.0 to 1.0, inclusive.
1263#[derive(Debug, Clone)]
1264#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Default))]
1265pub struct ButtonAxisSettings {
1266    /// The high value at which to apply rounding.
1267    pub high: f32,
1268    /// The low value at which to apply rounding.
1269    pub low: f32,
1270    /// The threshold to apply rounding.
1271    pub threshold: f32,
1272}
1273
1274impl Default for ButtonAxisSettings {
1275    fn default() -> Self {
1276        ButtonAxisSettings {
1277            high: 0.95,
1278            low: 0.05,
1279            threshold: 0.01,
1280        }
1281    }
1282}
1283
1284impl ButtonAxisSettings {
1285    /// Clamps the `raw_value` according to the specified settings.
1286    ///
1287    /// If the `raw_value` is:
1288    /// - lower than or equal to `low` it will be rounded to 0.0.
1289    /// - higher than or equal to `high` it will be rounded to 1.0.
1290    /// - Otherwise it will not be rounded.
1291    fn clamp(&self, raw_value: f32) -> f32 {
1292        if raw_value <= self.low {
1293            return 0.0;
1294        }
1295        if raw_value >= self.high {
1296            return 1.0;
1297        }
1298
1299        raw_value
1300    }
1301
1302    /// Determines whether the change from an `old_value` to a `new_value` should
1303    /// be registered as a change event, according to the specified settings.
1304    fn should_register_change(&self, new_value: f32, old_value: Option<f32>) -> bool {
1305        if old_value.is_none() {
1306            return true;
1307        }
1308
1309        f32::abs(new_value - old_value.unwrap()) > self.threshold
1310    }
1311
1312    /// Filters the `new_value` based on the `old_value`, according to the [`ButtonAxisSettings`].
1313    ///
1314    /// Returns the clamped `new_value`, according to the [`ButtonAxisSettings`], if the change
1315    /// exceeds the settings threshold, and `None` otherwise.
1316    pub fn filter(&self, new_value: f32, old_value: Option<f32>) -> Option<f32> {
1317        let new_value = self.clamp(new_value);
1318
1319        if self.should_register_change(new_value, old_value) {
1320            return Some(new_value);
1321        }
1322        None
1323    }
1324}
1325
1326/// Handles [`GamepadConnectionEvent`]s events.
1327///
1328/// On connection, adds the components representing a [`Gamepad`] to the entity.
1329/// On disconnection, removes the [`Gamepad`] and other related components.
1330/// Entities are left alive and might leave components like [`GamepadSettings`] to preserve state in the case of a reconnection
1331///
1332/// ## Note
1333///
1334/// Whenever a [`Gamepad`] connects or disconnects, an information gets printed to the console using the [`info!`] macro.
1335pub fn gamepad_connection_system(
1336    mut commands: Commands,
1337    mut connection_events: EventReader<GamepadConnectionEvent>,
1338) {
1339    for connection_event in connection_events.read() {
1340        let id = connection_event.gamepad;
1341        match &connection_event.connection {
1342            GamepadConnection::Connected {
1343                name,
1344                vendor_id,
1345                product_id,
1346            } => {
1347                let Some(mut gamepad) = commands.get_entity(id) else {
1348                    warn!("Gamepad {:} removed before handling connection event.", id);
1349                    continue;
1350                };
1351                gamepad.insert((
1352                    Name::new(name.clone()),
1353                    Gamepad {
1354                        vendor_id: *vendor_id,
1355                        product_id: *product_id,
1356                        ..Default::default()
1357                    },
1358                ));
1359                info!("Gamepad {:?} connected.", id);
1360            }
1361            GamepadConnection::Disconnected => {
1362                let Some(mut gamepad) = commands.get_entity(id) else {
1363                    warn!("Gamepad {:} removed before handling disconnection event. You can ignore this if you manually removed it.", id);
1364                    continue;
1365                };
1366                // Gamepad entities are left alive to preserve their state (e.g. [`GamepadSettings`]).
1367                // Instead of despawning, we remove Gamepad components that don't need to preserve state
1368                // and re-add them if they ever reconnect.
1369                gamepad.remove::<Gamepad>();
1370                info!("Gamepad {:} disconnected.", id);
1371            }
1372        }
1373    }
1374}
1375
1376// Note that we don't expose `gilrs::Gamepad::uuid` due to
1377// https://gitlab.com/gilrs-project/gilrs/-/issues/153.
1378//
1379/// The connection status of a gamepad.
1380#[derive(Debug, Clone, PartialEq)]
1381#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1382#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1383#[cfg_attr(
1384    all(feature = "serialize", feature = "bevy_reflect"),
1385    reflect(Serialize, Deserialize)
1386)]
1387pub enum GamepadConnection {
1388    /// The gamepad is connected.
1389    Connected {
1390        /// The name of the gamepad.
1391        ///
1392        /// This name is generally defined by the OS.
1393        ///
1394        /// For example on Windows the name may be "HID-compliant game controller".
1395        name: String,
1396
1397        /// The USB vendor ID as assigned by the USB-IF, if available.
1398        vendor_id: Option<u16>,
1399
1400        /// The USB product ID as assigned by the vendor, if available.
1401        product_id: Option<u16>,
1402    },
1403    /// The gamepad is disconnected.
1404    Disconnected,
1405}
1406
1407/// Consumes [`RawGamepadEvent`] events, filters them using their [`GamepadSettings`] and if successful,
1408/// updates the [`Gamepad`] and sends [`GamepadAxisChangedEvent`], [`GamepadButtonStateChangedEvent`], [`GamepadButtonChangedEvent`] events.
1409pub fn gamepad_event_processing_system(
1410    mut gamepads: Query<(&mut Gamepad, &GamepadSettings)>,
1411    mut raw_events: EventReader<RawGamepadEvent>,
1412    mut processed_events: EventWriter<GamepadEvent>,
1413    mut processed_axis_events: EventWriter<GamepadAxisChangedEvent>,
1414    mut processed_digital_events: EventWriter<GamepadButtonStateChangedEvent>,
1415    mut processed_analog_events: EventWriter<GamepadButtonChangedEvent>,
1416) {
1417    // Clear digital buttons state
1418    for (mut gamepad, _) in gamepads.iter_mut() {
1419        gamepad.bypass_change_detection().digital.clear();
1420    }
1421
1422    for event in raw_events.read() {
1423        match event {
1424            // Connections require inserting/removing components so they are done in a separate system
1425            RawGamepadEvent::Connection(send_event) => {
1426                processed_events.send(GamepadEvent::from(send_event.clone()));
1427            }
1428            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
1429                gamepad,
1430                axis,
1431                value,
1432            }) => {
1433                let (gamepad, axis, value) = (*gamepad, *axis, *value);
1434                let Ok((mut gamepad_axis, gamepad_settings)) = gamepads.get_mut(gamepad) else {
1435                    continue;
1436                };
1437                let Some(filtered_value) = gamepad_settings
1438                    .get_axis_settings(axis)
1439                    .filter(value, gamepad_axis.get(axis))
1440                else {
1441                    continue;
1442                };
1443
1444                gamepad_axis.analog.set(axis, filtered_value);
1445                let send_event = GamepadAxisChangedEvent::new(gamepad, axis, filtered_value);
1446                processed_axis_events.send(send_event);
1447                processed_events.send(GamepadEvent::from(send_event));
1448            }
1449            RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
1450                gamepad,
1451                button,
1452                value,
1453            }) => {
1454                let (gamepad, button, value) = (*gamepad, *button, *value);
1455                let Ok((mut gamepad_buttons, settings)) = gamepads.get_mut(gamepad) else {
1456                    continue;
1457                };
1458                let Some(filtered_value) = settings
1459                    .get_button_axis_settings(button)
1460                    .filter(value, gamepad_buttons.get(button))
1461                else {
1462                    continue;
1463                };
1464                let button_settings = settings.get_button_settings(button);
1465                gamepad_buttons.analog.set(button, filtered_value);
1466
1467                if button_settings.is_released(filtered_value) {
1468                    // Check if button was previously pressed
1469                    if gamepad_buttons.pressed(button) {
1470                        processed_digital_events.send(GamepadButtonStateChangedEvent::new(
1471                            gamepad,
1472                            button,
1473                            ButtonState::Released,
1474                        ));
1475                    }
1476                    // We don't have to check if the button was previously pressed here
1477                    // because that check is performed within Input<T>::release()
1478                    gamepad_buttons.digital.release(button);
1479                } else if button_settings.is_pressed(filtered_value) {
1480                    // Check if button was previously not pressed
1481                    if !gamepad_buttons.pressed(button) {
1482                        processed_digital_events.send(GamepadButtonStateChangedEvent::new(
1483                            gamepad,
1484                            button,
1485                            ButtonState::Pressed,
1486                        ));
1487                    }
1488                    gamepad_buttons.digital.press(button);
1489                };
1490
1491                let button_state = if gamepad_buttons.digital.pressed(button) {
1492                    ButtonState::Pressed
1493                } else {
1494                    ButtonState::Released
1495                };
1496                let send_event =
1497                    GamepadButtonChangedEvent::new(gamepad, button, button_state, filtered_value);
1498                processed_analog_events.send(send_event);
1499                processed_events.send(GamepadEvent::from(send_event));
1500            }
1501        }
1502    }
1503}
1504
1505/// The intensity at which a gamepad's force-feedback motors may rumble.
1506#[derive(Clone, Copy, Debug, PartialEq)]
1507#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1508pub struct GamepadRumbleIntensity {
1509    /// The rumble intensity of the strong gamepad motor.
1510    ///
1511    /// Ranges from `0.0` to `1.0`.
1512    ///
1513    /// By convention, this is usually a low-frequency motor on the left-hand
1514    /// side of the gamepad, though it may vary across platforms and hardware.
1515    pub strong_motor: f32,
1516    /// The rumble intensity of the weak gamepad motor.
1517    ///
1518    /// Ranges from `0.0` to `1.0`.
1519    ///
1520    /// By convention, this is usually a high-frequency motor on the right-hand
1521    /// side of the gamepad, though it may vary across platforms and hardware.
1522    pub weak_motor: f32,
1523}
1524
1525impl GamepadRumbleIntensity {
1526    /// Rumble both gamepad motors at maximum intensity.
1527    pub const MAX: Self = GamepadRumbleIntensity {
1528        strong_motor: 1.0,
1529        weak_motor: 1.0,
1530    };
1531
1532    /// Rumble the weak motor at maximum intensity.
1533    pub const WEAK_MAX: Self = GamepadRumbleIntensity {
1534        strong_motor: 0.0,
1535        weak_motor: 1.0,
1536    };
1537
1538    /// Rumble the strong motor at maximum intensity.
1539    pub const STRONG_MAX: Self = GamepadRumbleIntensity {
1540        strong_motor: 1.0,
1541        weak_motor: 0.0,
1542    };
1543
1544    /// Creates a new rumble intensity with weak motor intensity set to the given value.
1545    ///
1546    /// Clamped within the `0.0` to `1.0` range.
1547    pub const fn weak_motor(intensity: f32) -> Self {
1548        Self {
1549            weak_motor: intensity,
1550            strong_motor: 0.0,
1551        }
1552    }
1553
1554    /// Creates a new rumble intensity with strong motor intensity set to the given value.
1555    ///
1556    /// Clamped within the `0.0` to `1.0` range.
1557    pub const fn strong_motor(intensity: f32) -> Self {
1558        Self {
1559            strong_motor: intensity,
1560            weak_motor: 0.0,
1561        }
1562    }
1563}
1564
1565/// An event that controls force-feedback rumbling of a [`Gamepad`] [`entity`](Entity).
1566///
1567/// # Notes
1568///
1569/// Does nothing if the gamepad or platform does not support rumble.
1570///
1571/// # Example
1572///
1573/// ```
1574/// # use bevy_input::gamepad::{Gamepad, GamepadRumbleRequest, GamepadRumbleIntensity};
1575/// # use bevy_ecs::prelude::{EventWriter, Res, Query, Entity, With};
1576/// # use bevy_utils::Duration;
1577/// fn rumble_gamepad_system(
1578///     mut rumble_requests: EventWriter<GamepadRumbleRequest>,
1579///     gamepads: Query<Entity, With<Gamepad>>,
1580/// ) {
1581///     for entity in gamepads.iter() {
1582///         rumble_requests.send(GamepadRumbleRequest::Add {
1583///             gamepad: entity,
1584///             intensity: GamepadRumbleIntensity::MAX,
1585///             duration: Duration::from_secs_f32(0.5),
1586///         });
1587///     }
1588/// }
1589/// ```
1590#[doc(alias = "haptic feedback")]
1591#[doc(alias = "force feedback")]
1592#[doc(alias = "vibration")]
1593#[doc(alias = "vibrate")]
1594#[derive(Event, Clone)]
1595#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
1596pub enum GamepadRumbleRequest {
1597    /// Add a rumble to the given gamepad.
1598    ///
1599    /// Simultaneous rumble effects add up to the sum of their strengths.
1600    ///
1601    /// Consequently, if two rumbles at half intensity are added at the same
1602    /// time, their intensities will be added up, and the controller will rumble
1603    /// at full intensity until one of the rumbles finishes, then the rumble
1604    /// will continue at the intensity of the remaining event.
1605    ///
1606    /// To replace an existing rumble, send a [`GamepadRumbleRequest::Stop`] event first.
1607    Add {
1608        /// How long the gamepad should rumble.
1609        duration: Duration,
1610        /// How intense the rumble should be.
1611        intensity: GamepadRumbleIntensity,
1612        /// The gamepad to rumble.
1613        gamepad: Entity,
1614    },
1615    /// Stop all running rumbles on the given [`Entity`].
1616    Stop {
1617        /// The gamepad to stop rumble.
1618        gamepad: Entity,
1619    },
1620}
1621
1622impl GamepadRumbleRequest {
1623    /// Get the [`Entity`] associated with this request.
1624    pub fn gamepad(&self) -> Entity {
1625        match self {
1626            Self::Add { gamepad, .. } | Self::Stop { gamepad } => *gamepad,
1627        }
1628    }
1629}
1630
1631#[cfg(test)]
1632mod tests {
1633    use super::{
1634        gamepad_connection_system, gamepad_event_processing_system, AxisSettings,
1635        AxisSettingsError, ButtonAxisSettings, ButtonSettings, ButtonSettingsError, Gamepad,
1636        GamepadAxis, GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
1637        GamepadButtonStateChangedEvent,
1638        GamepadConnection::{Connected, Disconnected},
1639        GamepadConnectionEvent, GamepadEvent, GamepadSettings, RawGamepadAxisChangedEvent,
1640        RawGamepadButtonChangedEvent, RawGamepadEvent,
1641    };
1642    use crate::ButtonState;
1643    use bevy_app::{App, PreUpdate};
1644    use bevy_ecs::entity::Entity;
1645    use bevy_ecs::event::Events;
1646    use bevy_ecs::schedule::IntoSystemConfigs;
1647
1648    fn test_button_axis_settings_filter(
1649        settings: ButtonAxisSettings,
1650        new_value: f32,
1651        old_value: Option<f32>,
1652        expected: Option<f32>,
1653    ) {
1654        let actual = settings.filter(new_value, old_value);
1655        assert_eq!(
1656            expected, actual,
1657            "Testing filtering for {settings:?} with new_value = {new_value:?}, old_value = {old_value:?}",
1658        );
1659    }
1660
1661    #[test]
1662    fn test_button_axis_settings_default_filter() {
1663        let cases = [
1664            (1.0, None, Some(1.0)),
1665            (0.99, None, Some(1.0)),
1666            (0.96, None, Some(1.0)),
1667            (0.95, None, Some(1.0)),
1668            (0.9499, None, Some(0.9499)),
1669            (0.84, None, Some(0.84)),
1670            (0.43, None, Some(0.43)),
1671            (0.05001, None, Some(0.05001)),
1672            (0.05, None, Some(0.0)),
1673            (0.04, None, Some(0.0)),
1674            (0.01, None, Some(0.0)),
1675            (0.0, None, Some(0.0)),
1676        ];
1677
1678        for (new_value, old_value, expected) in cases {
1679            let settings = ButtonAxisSettings::default();
1680            test_button_axis_settings_filter(settings, new_value, old_value, expected);
1681        }
1682    }
1683
1684    #[test]
1685    fn test_button_axis_settings_default_filter_with_old_value() {
1686        let cases = [
1687            (0.43, Some(0.44001), Some(0.43)),
1688            (0.43, Some(0.44), None),
1689            (0.43, Some(0.43), None),
1690            (0.43, Some(0.41999), Some(0.43)),
1691            (0.43, Some(0.17), Some(0.43)),
1692            (0.43, Some(0.84), Some(0.43)),
1693            (0.05, Some(0.055), Some(0.0)),
1694            (0.95, Some(0.945), Some(1.0)),
1695        ];
1696
1697        for (new_value, old_value, expected) in cases {
1698            let settings = ButtonAxisSettings::default();
1699            test_button_axis_settings_filter(settings, new_value, old_value, expected);
1700        }
1701    }
1702
1703    fn test_axis_settings_filter(
1704        settings: AxisSettings,
1705        new_value: f32,
1706        old_value: Option<f32>,
1707        expected: Option<f32>,
1708    ) {
1709        let actual = settings.filter(new_value, old_value);
1710        assert_eq!(
1711            expected, actual,
1712            "Testing filtering for {settings:?} with new_value = {new_value:?}, old_value = {old_value:?}",
1713        );
1714    }
1715
1716    #[test]
1717    fn test_axis_settings_default_filter() {
1718        let cases = [
1719            (1.0, Some(1.0)),
1720            (0.99, Some(1.0)),
1721            (0.96, Some(1.0)),
1722            (0.95, Some(1.0)),
1723            (0.9499, Some(0.9499)),
1724            (0.84, Some(0.84)),
1725            (0.43, Some(0.43)),
1726            (0.05001, Some(0.05001)),
1727            (0.05, Some(0.0)),
1728            (0.04, Some(0.0)),
1729            (0.01, Some(0.0)),
1730            (0.0, Some(0.0)),
1731            (-1.0, Some(-1.0)),
1732            (-0.99, Some(-1.0)),
1733            (-0.96, Some(-1.0)),
1734            (-0.95, Some(-1.0)),
1735            (-0.9499, Some(-0.9499)),
1736            (-0.84, Some(-0.84)),
1737            (-0.43, Some(-0.43)),
1738            (-0.05001, Some(-0.05001)),
1739            (-0.05, Some(0.0)),
1740            (-0.04, Some(0.0)),
1741            (-0.01, Some(0.0)),
1742        ];
1743
1744        for (new_value, expected) in cases {
1745            let settings = AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.01).unwrap();
1746            test_axis_settings_filter(settings, new_value, None, expected);
1747        }
1748    }
1749
1750    #[test]
1751    fn test_axis_settings_default_filter_with_old_values() {
1752        let cases = [
1753            (0.43, Some(0.44001), Some(0.43)),
1754            (0.43, Some(0.44), None),
1755            (0.43, Some(0.43), None),
1756            (0.43, Some(0.41999), Some(0.43)),
1757            (0.43, Some(0.17), Some(0.43)),
1758            (0.43, Some(0.84), Some(0.43)),
1759            (0.05, Some(0.055), Some(0.0)),
1760            (0.95, Some(0.945), Some(1.0)),
1761            (-0.43, Some(-0.44001), Some(-0.43)),
1762            (-0.43, Some(-0.44), None),
1763            (-0.43, Some(-0.43), None),
1764            (-0.43, Some(-0.41999), Some(-0.43)),
1765            (-0.43, Some(-0.17), Some(-0.43)),
1766            (-0.43, Some(-0.84), Some(-0.43)),
1767            (-0.05, Some(-0.055), Some(0.0)),
1768            (-0.95, Some(-0.945), Some(-1.0)),
1769        ];
1770
1771        for (new_value, old_value, expected) in cases {
1772            let settings = AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.01).unwrap();
1773            test_axis_settings_filter(settings, new_value, old_value, expected);
1774        }
1775    }
1776
1777    #[test]
1778    fn test_button_settings_default_is_pressed() {
1779        let cases = [
1780            (1.0, true),
1781            (0.95, true),
1782            (0.9, true),
1783            (0.8, true),
1784            (0.75, true),
1785            (0.7, false),
1786            (0.65, false),
1787            (0.5, false),
1788            (0.0, false),
1789        ];
1790
1791        for (value, expected) in cases {
1792            let settings = ButtonSettings::default();
1793            let actual = settings.is_pressed(value);
1794
1795            assert_eq!(
1796                expected, actual,
1797                "testing ButtonSettings::is_pressed() for value: {value}",
1798            );
1799        }
1800    }
1801
1802    #[test]
1803    fn test_button_settings_default_is_released() {
1804        let cases = [
1805            (1.0, false),
1806            (0.95, false),
1807            (0.9, false),
1808            (0.8, false),
1809            (0.75, false),
1810            (0.7, false),
1811            (0.65, true),
1812            (0.5, true),
1813            (0.0, true),
1814        ];
1815
1816        for (value, expected) in cases {
1817            let settings = ButtonSettings::default();
1818            let actual = settings.is_released(value);
1819
1820            assert_eq!(
1821                expected, actual,
1822                "testing ButtonSettings::is_released() for value: {value}",
1823            );
1824        }
1825    }
1826
1827    #[test]
1828    fn test_new_button_settings_given_valid_parameters() {
1829        let cases = [
1830            (1.0, 0.0),
1831            (1.0, 1.0),
1832            (1.0, 0.9),
1833            (0.9, 0.9),
1834            (0.9, 0.0),
1835            (0.0, 0.0),
1836        ];
1837
1838        for (press_threshold, release_threshold) in cases {
1839            let bs = ButtonSettings::new(press_threshold, release_threshold);
1840            match bs {
1841                Ok(button_settings) => {
1842                    assert_eq!(button_settings.press_threshold, press_threshold);
1843                    assert_eq!(button_settings.release_threshold, release_threshold);
1844                }
1845                Err(_) => {
1846                    panic!(
1847                        "ButtonSettings::new({press_threshold}, {release_threshold}) should be valid"
1848                    );
1849                }
1850            }
1851        }
1852    }
1853
1854    #[test]
1855    fn test_new_button_settings_given_invalid_parameters() {
1856        let cases = [
1857            (1.1, 0.0),
1858            (1.1, 1.0),
1859            (1.0, 1.1),
1860            (-1.0, 0.9),
1861            (-1.0, 0.0),
1862            (-1.0, -0.4),
1863            (0.9, 1.0),
1864            (0.0, 0.1),
1865        ];
1866
1867        for (press_threshold, release_threshold) in cases {
1868            let bs = ButtonSettings::new(press_threshold, release_threshold);
1869            match bs {
1870                Ok(_) => {
1871                    panic!(
1872                        "ButtonSettings::new({press_threshold}, {release_threshold}) should be invalid"
1873                    );
1874                }
1875                Err(err_code) => match err_code {
1876                    ButtonSettingsError::PressThresholdOutOfRange(_press_threshold) => {}
1877                    ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
1878                        press_threshold: _press_threshold,
1879                        release_threshold: _release_threshold,
1880                    } => {}
1881                    ButtonSettingsError::ReleaseThresholdOutOfRange(_release_threshold) => {}
1882                },
1883            }
1884        }
1885    }
1886
1887    #[test]
1888    fn test_try_out_of_range_axis_settings() {
1889        let mut axis_settings = AxisSettings::default();
1890        assert_eq!(
1891            AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.001),
1892            Ok(AxisSettings {
1893                livezone_lowerbound: -0.95,
1894                deadzone_lowerbound: -0.05,
1895                deadzone_upperbound: 0.05,
1896                livezone_upperbound: 0.95,
1897                threshold: 0.001,
1898            })
1899        );
1900        assert_eq!(
1901            Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(-2.0)),
1902            axis_settings.try_set_livezone_lowerbound(-2.0)
1903        );
1904        assert_eq!(
1905            Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(0.1)),
1906            axis_settings.try_set_livezone_lowerbound(0.1)
1907        );
1908        assert_eq!(
1909            Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(-2.0)),
1910            axis_settings.try_set_deadzone_lowerbound(-2.0)
1911        );
1912        assert_eq!(
1913            Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(0.1)),
1914            axis_settings.try_set_deadzone_lowerbound(0.1)
1915        );
1916
1917        assert_eq!(
1918            Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(-0.1)),
1919            axis_settings.try_set_deadzone_upperbound(-0.1)
1920        );
1921        assert_eq!(
1922            Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(1.1)),
1923            axis_settings.try_set_deadzone_upperbound(1.1)
1924        );
1925        assert_eq!(
1926            Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(-0.1)),
1927            axis_settings.try_set_livezone_upperbound(-0.1)
1928        );
1929        assert_eq!(
1930            Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(1.1)),
1931            axis_settings.try_set_livezone_upperbound(1.1)
1932        );
1933
1934        axis_settings.set_livezone_lowerbound(-0.7);
1935        axis_settings.set_deadzone_lowerbound(-0.3);
1936        assert_eq!(
1937            Err(
1938                AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1939                    livezone_lowerbound: -0.1,
1940                    deadzone_lowerbound: -0.3,
1941                }
1942            ),
1943            axis_settings.try_set_livezone_lowerbound(-0.1)
1944        );
1945        assert_eq!(
1946            Err(
1947                AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1948                    livezone_lowerbound: -0.7,
1949                    deadzone_lowerbound: -0.9
1950                }
1951            ),
1952            axis_settings.try_set_deadzone_lowerbound(-0.9)
1953        );
1954
1955        axis_settings.set_deadzone_upperbound(0.3);
1956        axis_settings.set_livezone_upperbound(0.7);
1957        assert_eq!(
1958            Err(
1959                AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1960                    deadzone_upperbound: 0.8,
1961                    livezone_upperbound: 0.7
1962                }
1963            ),
1964            axis_settings.try_set_deadzone_upperbound(0.8)
1965        );
1966        assert_eq!(
1967            Err(
1968                AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1969                    deadzone_upperbound: 0.3,
1970                    livezone_upperbound: 0.1
1971                }
1972            ),
1973            axis_settings.try_set_livezone_upperbound(0.1)
1974        );
1975    }
1976
1977    struct TestContext {
1978        pub app: App,
1979    }
1980
1981    impl TestContext {
1982        pub fn new() -> Self {
1983            let mut app = App::new();
1984            app.add_systems(
1985                PreUpdate,
1986                (
1987                    gamepad_connection_system,
1988                    gamepad_event_processing_system.after(gamepad_connection_system),
1989                ),
1990            )
1991            .add_event::<GamepadEvent>()
1992            .add_event::<GamepadConnectionEvent>()
1993            .add_event::<RawGamepadButtonChangedEvent>()
1994            .add_event::<GamepadButtonChangedEvent>()
1995            .add_event::<GamepadButtonStateChangedEvent>()
1996            .add_event::<GamepadAxisChangedEvent>()
1997            .add_event::<RawGamepadAxisChangedEvent>()
1998            .add_event::<RawGamepadEvent>();
1999            Self { app }
2000        }
2001
2002        pub fn update(&mut self) {
2003            self.app.update();
2004        }
2005
2006        pub fn send_gamepad_connection_event(&mut self, gamepad: Option<Entity>) -> Entity {
2007            let gamepad = gamepad.unwrap_or_else(|| self.app.world_mut().spawn_empty().id());
2008            self.app
2009                .world_mut()
2010                .resource_mut::<Events<GamepadConnectionEvent>>()
2011                .send(GamepadConnectionEvent::new(
2012                    gamepad,
2013                    Connected {
2014                        name: "Test gamepad".to_string(),
2015                        vendor_id: None,
2016                        product_id: None,
2017                    },
2018                ));
2019            gamepad
2020        }
2021
2022        pub fn send_gamepad_disconnection_event(&mut self, gamepad: Entity) {
2023            self.app
2024                .world_mut()
2025                .resource_mut::<Events<GamepadConnectionEvent>>()
2026                .send(GamepadConnectionEvent::new(gamepad, Disconnected));
2027        }
2028
2029        pub fn send_raw_gamepad_event(&mut self, event: RawGamepadEvent) {
2030            self.app
2031                .world_mut()
2032                .resource_mut::<Events<RawGamepadEvent>>()
2033                .send(event);
2034        }
2035
2036        pub fn send_raw_gamepad_event_batch(
2037            &mut self,
2038            events: impl IntoIterator<Item = RawGamepadEvent>,
2039        ) {
2040            self.app
2041                .world_mut()
2042                .resource_mut::<Events<RawGamepadEvent>>()
2043                .send_batch(events);
2044        }
2045    }
2046
2047    #[test]
2048    fn connection_event() {
2049        let mut ctx = TestContext::new();
2050        assert_eq!(
2051            ctx.app
2052                .world_mut()
2053                .query::<&Gamepad>()
2054                .iter(ctx.app.world())
2055                .len(),
2056            0
2057        );
2058        ctx.send_gamepad_connection_event(None);
2059        ctx.update();
2060        assert_eq!(
2061            ctx.app
2062                .world_mut()
2063                .query::<(&Gamepad, &GamepadSettings)>()
2064                .iter(ctx.app.world())
2065                .len(),
2066            1
2067        );
2068    }
2069
2070    #[test]
2071    fn disconnection_event() {
2072        let mut ctx = TestContext::new();
2073        assert_eq!(
2074            ctx.app
2075                .world_mut()
2076                .query::<&Gamepad>()
2077                .iter(ctx.app.world())
2078                .len(),
2079            0
2080        );
2081        let entity = ctx.send_gamepad_connection_event(None);
2082        ctx.update();
2083        assert_eq!(
2084            ctx.app
2085                .world_mut()
2086                .query::<(&Gamepad, &GamepadSettings)>()
2087                .iter(ctx.app.world())
2088                .len(),
2089            1
2090        );
2091        ctx.send_gamepad_disconnection_event(entity);
2092        ctx.update();
2093        // Gamepad component should be removed
2094        assert!(ctx
2095            .app
2096            .world_mut()
2097            .query::<&Gamepad>()
2098            .get(ctx.app.world(), entity)
2099            .is_err());
2100        // Settings should be kept
2101        assert!(ctx
2102            .app
2103            .world_mut()
2104            .query::<&GamepadSettings>()
2105            .get(ctx.app.world(), entity)
2106            .is_ok());
2107
2108        // Mistakenly sending a second disconnection event shouldn't break anything
2109        ctx.send_gamepad_disconnection_event(entity);
2110        ctx.update();
2111        assert!(ctx
2112            .app
2113            .world_mut()
2114            .query::<&Gamepad>()
2115            .get(ctx.app.world(), entity)
2116            .is_err());
2117        assert!(ctx
2118            .app
2119            .world_mut()
2120            .query::<&GamepadSettings>()
2121            .get(ctx.app.world(), entity)
2122            .is_ok());
2123    }
2124
2125    #[test]
2126    fn connection_disconnection_frame_event() {
2127        let mut ctx = TestContext::new();
2128        assert_eq!(
2129            ctx.app
2130                .world_mut()
2131                .query::<&Gamepad>()
2132                .iter(ctx.app.world())
2133                .len(),
2134            0
2135        );
2136        let entity = ctx.send_gamepad_connection_event(None);
2137        ctx.send_gamepad_disconnection_event(entity);
2138        ctx.update();
2139        // Gamepad component should be removed
2140        assert!(ctx
2141            .app
2142            .world_mut()
2143            .query::<&Gamepad>()
2144            .get(ctx.app.world(), entity)
2145            .is_err());
2146        // Settings should be kept
2147        assert!(ctx
2148            .app
2149            .world_mut()
2150            .query::<&GamepadSettings>()
2151            .get(ctx.app.world(), entity)
2152            .is_ok());
2153    }
2154
2155    #[test]
2156    fn reconnection_event() {
2157        let button_settings = ButtonSettings::new(0.7, 0.2).expect("correct parameters");
2158        let mut ctx = TestContext::new();
2159        assert_eq!(
2160            ctx.app
2161                .world_mut()
2162                .query::<&Gamepad>()
2163                .iter(ctx.app.world())
2164                .len(),
2165            0
2166        );
2167        let entity = ctx.send_gamepad_connection_event(None);
2168        ctx.update();
2169        let mut settings = ctx
2170            .app
2171            .world_mut()
2172            .query::<&mut GamepadSettings>()
2173            .get_mut(ctx.app.world_mut(), entity)
2174            .expect("be alive");
2175        assert_ne!(settings.default_button_settings, button_settings);
2176        settings.default_button_settings = button_settings.clone();
2177        ctx.send_gamepad_disconnection_event(entity);
2178        ctx.update();
2179        assert_eq!(
2180            ctx.app
2181                .world_mut()
2182                .query::<&Gamepad>()
2183                .iter(ctx.app.world())
2184                .len(),
2185            0
2186        );
2187        ctx.send_gamepad_connection_event(Some(entity));
2188        ctx.update();
2189        let settings = ctx
2190            .app
2191            .world_mut()
2192            .query::<&GamepadSettings>()
2193            .get(ctx.app.world(), entity)
2194            .expect("be alive");
2195        assert_eq!(settings.default_button_settings, button_settings);
2196    }
2197
2198    #[test]
2199    fn reconnection_same_frame_event() {
2200        let mut ctx = TestContext::new();
2201        assert_eq!(
2202            ctx.app
2203                .world_mut()
2204                .query::<&Gamepad>()
2205                .iter(ctx.app.world())
2206                .len(),
2207            0
2208        );
2209        let entity = ctx.send_gamepad_connection_event(None);
2210        ctx.send_gamepad_disconnection_event(entity);
2211        ctx.update();
2212        assert_eq!(
2213            ctx.app
2214                .world_mut()
2215                .query::<&Gamepad>()
2216                .iter(ctx.app.world())
2217                .len(),
2218            0
2219        );
2220        assert!(ctx
2221            .app
2222            .world_mut()
2223            .query::<(Entity, &GamepadSettings)>()
2224            .get(ctx.app.world(), entity)
2225            .is_ok());
2226    }
2227
2228    #[test]
2229    fn gamepad_axis_valid() {
2230        let mut ctx = TestContext::new();
2231
2232        // Create test gamepad
2233        let entity = ctx.send_gamepad_connection_event(None);
2234        ctx.app
2235            .world_mut()
2236            .resource_mut::<Events<RawGamepadEvent>>()
2237            .send_batch([
2238                RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2239                    entity,
2240                    GamepadAxis::LeftStickY,
2241                    0.5,
2242                )),
2243                RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2244                    entity,
2245                    GamepadAxis::RightStickX,
2246                    0.6,
2247                )),
2248                RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2249                    entity,
2250                    GamepadAxis::RightZ,
2251                    -0.4,
2252                )),
2253                RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2254                    entity,
2255                    GamepadAxis::RightStickY,
2256                    -0.8,
2257                )),
2258            ]);
2259        ctx.update();
2260        assert_eq!(
2261            ctx.app
2262                .world()
2263                .resource::<Events<GamepadAxisChangedEvent>>()
2264                .len(),
2265            4
2266        );
2267    }
2268
2269    #[test]
2270    fn gamepad_axis_threshold_filter() {
2271        let mut ctx = TestContext::new();
2272
2273        // Create test gamepad
2274        let entity = ctx.send_gamepad_connection_event(None);
2275        let settings = GamepadSettings::default().default_axis_settings;
2276        // Set of events to ensure they are being properly filtered
2277        let base_value = 0.5;
2278        let events = [
2279            // Event above threshold
2280            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2281                entity,
2282                GamepadAxis::LeftStickX,
2283                base_value,
2284            )),
2285            // Event below threshold, should be filtered
2286            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2287                entity,
2288                GamepadAxis::LeftStickX,
2289                base_value + settings.threshold - 0.01,
2290            )),
2291            // Event above threshold
2292            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2293                entity,
2294                GamepadAxis::LeftStickX,
2295                base_value + settings.threshold + 0.01,
2296            )),
2297        ];
2298        ctx.app
2299            .world_mut()
2300            .resource_mut::<Events<RawGamepadEvent>>()
2301            .send_batch(events);
2302        ctx.update();
2303        assert_eq!(
2304            ctx.app
2305                .world()
2306                .resource::<Events<GamepadAxisChangedEvent>>()
2307                .len(),
2308            2
2309        );
2310    }
2311
2312    #[test]
2313    fn gamepad_axis_deadzone_filter() {
2314        let mut ctx = TestContext::new();
2315
2316        // Create test gamepad
2317        let entity = ctx.send_gamepad_connection_event(None);
2318        let settings = GamepadSettings::default().default_axis_settings;
2319
2320        // Set of events to ensure they are being properly filtered
2321        let events = [
2322            // Event below deadzone upperbound should be filtered
2323            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2324                entity,
2325                GamepadAxis::LeftStickX,
2326                settings.deadzone_upperbound - 0.01,
2327            )),
2328            // Event above deadzone lowerbound should be filtered
2329            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2330                entity,
2331                GamepadAxis::LeftStickX,
2332                settings.deadzone_lowerbound + 0.01,
2333            )),
2334        ];
2335        ctx.app
2336            .world_mut()
2337            .resource_mut::<Events<RawGamepadEvent>>()
2338            .send_batch(events);
2339        ctx.update();
2340        assert_eq!(
2341            ctx.app
2342                .world()
2343                .resource::<Events<GamepadAxisChangedEvent>>()
2344                .len(),
2345            0
2346        );
2347    }
2348
2349    #[test]
2350    fn gamepad_axis_deadzone_rounded() {
2351        let mut ctx = TestContext::new();
2352
2353        // Create test gamepad
2354        let entity = ctx.send_gamepad_connection_event(None);
2355        let settings = GamepadSettings::default().default_axis_settings;
2356
2357        // Set of events to ensure they are being properly filtered
2358        let events = [
2359            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2360                entity,
2361                GamepadAxis::LeftStickX,
2362                1.0,
2363            )),
2364            // Event below deadzone upperbound should be rounded to 0
2365            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2366                entity,
2367                GamepadAxis::LeftStickX,
2368                settings.deadzone_upperbound - 0.01,
2369            )),
2370            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2371                entity,
2372                GamepadAxis::LeftStickX,
2373                1.0,
2374            )),
2375            // Event above deadzone lowerbound should be rounded to 0
2376            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2377                entity,
2378                GamepadAxis::LeftStickX,
2379                settings.deadzone_lowerbound + 0.01,
2380            )),
2381        ];
2382        let results = [1.0, 0.0, 1.0, 0.0];
2383        ctx.app
2384            .world_mut()
2385            .resource_mut::<Events<RawGamepadEvent>>()
2386            .send_batch(events);
2387        ctx.update();
2388
2389        let events = ctx
2390            .app
2391            .world()
2392            .resource::<Events<GamepadAxisChangedEvent>>();
2393        let mut event_reader = events.get_cursor();
2394        for (event, result) in event_reader.read(events).zip(results) {
2395            assert_eq!(event.value, result);
2396        }
2397        assert_eq!(
2398            ctx.app
2399                .world()
2400                .resource::<Events<GamepadAxisChangedEvent>>()
2401                .len(),
2402            4
2403        );
2404    }
2405
2406    #[test]
2407    fn gamepad_axis_livezone_filter() {
2408        let mut ctx = TestContext::new();
2409
2410        // Create test gamepad
2411        let entity = ctx.send_gamepad_connection_event(None);
2412        let settings = GamepadSettings::default().default_axis_settings;
2413
2414        // Set of events to ensure they are being properly filtered
2415        let events = [
2416            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2417                entity,
2418                GamepadAxis::LeftStickX,
2419                1.0,
2420            )),
2421            // Event above livezone upperbound should be filtered
2422            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2423                entity,
2424                GamepadAxis::LeftStickX,
2425                settings.livezone_upperbound + 0.01,
2426            )),
2427            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2428                entity,
2429                GamepadAxis::LeftStickX,
2430                -1.0,
2431            )),
2432            // Event below livezone lowerbound should be filtered
2433            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2434                entity,
2435                GamepadAxis::LeftStickX,
2436                settings.livezone_lowerbound - 0.01,
2437            )),
2438        ];
2439        ctx.app
2440            .world_mut()
2441            .resource_mut::<Events<RawGamepadEvent>>()
2442            .send_batch(events);
2443        ctx.update();
2444        assert_eq!(
2445            ctx.app
2446                .world()
2447                .resource::<Events<GamepadAxisChangedEvent>>()
2448                .len(),
2449            2
2450        );
2451    }
2452
2453    #[test]
2454    fn gamepad_axis_livezone_rounded() {
2455        let mut ctx = TestContext::new();
2456
2457        // Create test gamepad
2458        let entity = ctx.send_gamepad_connection_event(None);
2459        let settings = GamepadSettings::default().default_axis_settings;
2460
2461        // Set of events to ensure they are being properly filtered
2462        let events = [
2463            // Event above livezone upperbound should be rounded to 1
2464            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2465                entity,
2466                GamepadAxis::LeftStickX,
2467                settings.livezone_upperbound + 0.01,
2468            )),
2469            // Event below livezone lowerbound should be rounded to -1
2470            RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2471                entity,
2472                GamepadAxis::LeftStickX,
2473                settings.livezone_lowerbound - 0.01,
2474            )),
2475        ];
2476        let results = [1.0, -1.0];
2477        ctx.app
2478            .world_mut()
2479            .resource_mut::<Events<RawGamepadEvent>>()
2480            .send_batch(events);
2481        ctx.update();
2482
2483        let events = ctx
2484            .app
2485            .world()
2486            .resource::<Events<GamepadAxisChangedEvent>>();
2487        let mut event_reader = events.get_cursor();
2488        for (event, result) in event_reader.read(events).zip(results) {
2489            assert_eq!(event.value, result);
2490        }
2491        assert_eq!(
2492            ctx.app
2493                .world()
2494                .resource::<Events<GamepadAxisChangedEvent>>()
2495                .len(),
2496            2
2497        );
2498    }
2499
2500    #[test]
2501    fn gamepad_buttons_pressed() {
2502        let mut ctx = TestContext::new();
2503
2504        // Create test gamepad
2505        let entity = ctx.send_gamepad_connection_event(None);
2506        let digital_settings = GamepadSettings::default().default_button_settings;
2507
2508        let events = [RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2509            entity,
2510            GamepadButton::DPadDown,
2511            digital_settings.press_threshold,
2512        ))];
2513        ctx.app
2514            .world_mut()
2515            .resource_mut::<Events<RawGamepadEvent>>()
2516            .send_batch(events);
2517        ctx.update();
2518
2519        assert_eq!(
2520            ctx.app
2521                .world()
2522                .resource::<Events<GamepadButtonStateChangedEvent>>()
2523                .len(),
2524            1
2525        );
2526        let events = ctx
2527            .app
2528            .world()
2529            .resource::<Events<GamepadButtonStateChangedEvent>>();
2530        let mut event_reader = events.get_cursor();
2531        for event in event_reader.read(events) {
2532            assert_eq!(event.button, GamepadButton::DPadDown);
2533            assert_eq!(event.state, ButtonState::Pressed);
2534        }
2535        assert!(ctx
2536            .app
2537            .world_mut()
2538            .query::<&Gamepad>()
2539            .get(ctx.app.world(), entity)
2540            .unwrap()
2541            .pressed(GamepadButton::DPadDown));
2542
2543        ctx.app
2544            .world_mut()
2545            .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2546            .clear();
2547        ctx.update();
2548
2549        assert_eq!(
2550            ctx.app
2551                .world()
2552                .resource::<Events<GamepadButtonStateChangedEvent>>()
2553                .len(),
2554            0
2555        );
2556        assert!(ctx
2557            .app
2558            .world_mut()
2559            .query::<&Gamepad>()
2560            .get(ctx.app.world(), entity)
2561            .unwrap()
2562            .pressed(GamepadButton::DPadDown));
2563    }
2564
2565    #[test]
2566    fn gamepad_buttons_just_pressed() {
2567        let mut ctx = TestContext::new();
2568
2569        // Create test gamepad
2570        let entity = ctx.send_gamepad_connection_event(None);
2571        let digital_settings = GamepadSettings::default().default_button_settings;
2572
2573        ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2574            entity,
2575            GamepadButton::DPadDown,
2576            digital_settings.press_threshold,
2577        )));
2578        ctx.update();
2579
2580        // Check it is flagged for this frame
2581        assert!(ctx
2582            .app
2583            .world_mut()
2584            .query::<&Gamepad>()
2585            .get(ctx.app.world(), entity)
2586            .unwrap()
2587            .just_pressed(GamepadButton::DPadDown));
2588        ctx.update();
2589
2590        //Check it clears next frame
2591        assert!(!ctx
2592            .app
2593            .world_mut()
2594            .query::<&Gamepad>()
2595            .get(ctx.app.world(), entity)
2596            .unwrap()
2597            .just_pressed(GamepadButton::DPadDown));
2598    }
2599    #[test]
2600    fn gamepad_buttons_released() {
2601        let mut ctx = TestContext::new();
2602
2603        // Create test gamepad
2604        let entity = ctx.send_gamepad_connection_event(None);
2605        let digital_settings = GamepadSettings::default().default_button_settings;
2606
2607        ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2608            entity,
2609            GamepadButton::DPadDown,
2610            digital_settings.press_threshold,
2611        )));
2612        ctx.update();
2613
2614        ctx.app
2615            .world_mut()
2616            .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2617            .clear();
2618        ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2619            entity,
2620            GamepadButton::DPadDown,
2621            digital_settings.release_threshold - 0.01,
2622        )));
2623        ctx.update();
2624        assert_eq!(
2625            ctx.app
2626                .world()
2627                .resource::<Events<GamepadButtonStateChangedEvent>>()
2628                .len(),
2629            1
2630        );
2631        let events = ctx
2632            .app
2633            .world()
2634            .resource::<Events<GamepadButtonStateChangedEvent>>();
2635        let mut event_reader = events.get_cursor();
2636        for event in event_reader.read(events) {
2637            assert_eq!(event.button, GamepadButton::DPadDown);
2638            assert_eq!(event.state, ButtonState::Released);
2639        }
2640        assert!(!ctx
2641            .app
2642            .world_mut()
2643            .query::<&Gamepad>()
2644            .get(ctx.app.world(), entity)
2645            .unwrap()
2646            .pressed(GamepadButton::DPadDown));
2647        ctx.app
2648            .world_mut()
2649            .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2650            .clear();
2651        ctx.update();
2652
2653        assert_eq!(
2654            ctx.app
2655                .world()
2656                .resource::<Events<GamepadButtonStateChangedEvent>>()
2657                .len(),
2658            0
2659        );
2660    }
2661
2662    #[test]
2663    fn gamepad_buttons_just_released() {
2664        let mut ctx = TestContext::new();
2665
2666        // Create test gamepad
2667        let entity = ctx.send_gamepad_connection_event(None);
2668        let digital_settings = GamepadSettings::default().default_button_settings;
2669
2670        ctx.send_raw_gamepad_event_batch([
2671            RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2672                entity,
2673                GamepadButton::DPadDown,
2674                digital_settings.press_threshold,
2675            )),
2676            RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2677                entity,
2678                GamepadButton::DPadDown,
2679                digital_settings.release_threshold - 0.01,
2680            )),
2681        ]);
2682        ctx.update();
2683
2684        // Check it is flagged for this frame
2685        assert!(ctx
2686            .app
2687            .world_mut()
2688            .query::<&Gamepad>()
2689            .get(ctx.app.world(), entity)
2690            .unwrap()
2691            .just_released(GamepadButton::DPadDown));
2692        ctx.update();
2693
2694        //Check it clears next frame
2695        assert!(!ctx
2696            .app
2697            .world_mut()
2698            .query::<&Gamepad>()
2699            .get(ctx.app.world(), entity)
2700            .unwrap()
2701            .just_released(GamepadButton::DPadDown));
2702    }
2703
2704    #[test]
2705    fn gamepad_buttons_axis() {
2706        let mut ctx = TestContext::new();
2707
2708        // Create test gamepad
2709        let entity = ctx.send_gamepad_connection_event(None);
2710        let digital_settings = GamepadSettings::default().default_button_settings;
2711        let analog_settings = GamepadSettings::default().default_button_axis_settings;
2712
2713        // Test events
2714        let events = [
2715            // Should trigger event
2716            RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2717                entity,
2718                GamepadButton::DPadDown,
2719                digital_settings.press_threshold,
2720            )),
2721            // Should trigger event
2722            RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2723                entity,
2724                GamepadButton::DPadDown,
2725                digital_settings.release_threshold,
2726            )),
2727            // Shouldn't trigger a state changed event
2728            RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2729                entity,
2730                GamepadButton::DPadDown,
2731                digital_settings.release_threshold - analog_settings.threshold * 1.01,
2732            )),
2733            // Shouldn't trigger any event
2734            RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2735                entity,
2736                GamepadButton::DPadDown,
2737                digital_settings.release_threshold - (analog_settings.threshold * 1.5),
2738            )),
2739            // Shouldn't trigger a state changed event
2740            RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2741                entity,
2742                GamepadButton::DPadDown,
2743                digital_settings.release_threshold - (analog_settings.threshold * 2.02),
2744            )),
2745        ];
2746        ctx.send_raw_gamepad_event_batch(events);
2747        ctx.update();
2748        assert_eq!(
2749            ctx.app
2750                .world()
2751                .resource::<Events<GamepadButtonStateChangedEvent>>()
2752                .len(),
2753            2
2754        );
2755        assert_eq!(
2756            ctx.app
2757                .world()
2758                .resource::<Events<GamepadButtonChangedEvent>>()
2759                .len(),
2760            4
2761        );
2762    }
2763}