bevy_input/
gamepad.rs

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