Skip to main content

qwac/
input.rs

1use euclid::Vector2D;
2use qwac_sys::input::{gamepad_axis, gamepad_buttons};
3pub use qwac_sys::input::{GamepadAxis, GamepadButton};
4
5/// Get and check the state of all buttons on the gamepad.
6#[derive(Clone, Copy, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
7pub struct GamepadButtons(u64);
8
9/// Get the bit for the given button and player.
10const fn button_bit(player: u8, button: GamepadButton) -> u64 {
11    1u64 << player * 16 + button as u8
12}
13
14impl GamepadButtons {
15    pub fn poll() -> Self {
16        Self(unsafe { gamepad_buttons() } as u64)
17    }
18
19    pub fn pressed(&self, player: u8, button: GamepadButton) -> bool {
20        (self.0 & button_bit(player, button)) != 0
21    }
22}
23
24/// Tracks button state from frame to frame.
25#[derive(Clone, Copy, Debug)]
26pub struct ButtonTracker {
27    previous_frame: GamepadButtons,
28    current_frame: GamepadButtons,
29}
30
31impl Default for ButtonTracker {
32    fn default() -> Self {
33        Self {
34            previous_frame: GamepadButtons(0),
35            current_frame: GamepadButtons(0),
36        }
37    }
38}
39
40impl ButtonTracker {
41    pub fn new() -> Self {
42        Default::default()
43    }
44
45    /// Set the current frame explicitly.
46    pub fn frame(&mut self, frame: GamepadButtons) {
47        self.previous_frame = std::mem::replace(&mut self.current_frame, frame);
48    }
49
50    /// Set the current frame from the current button state
51    pub fn poll(&mut self) {
52        self.frame(GamepadButtons::poll());
53    }
54
55    /// Get the state of the previous and current frame, in that order.
56    pub fn state(&self, player: u8, button: GamepadButton) -> (bool, bool) {
57        (
58            self.previous_frame.pressed(player, button),
59            self.current_frame.pressed(player, button),
60        )
61    }
62
63    pub fn pressed(&self, player: u8, button: GamepadButton) -> bool {
64        self.current_frame.pressed(player, button)
65    }
66
67    pub fn just_pressed(&self, player: u8, button: GamepadButton) -> bool {
68        let (previous, current) = self.state(player, button);
69        !previous && current
70    }
71
72    pub fn just_released(&self, player: u8, button: GamepadButton) -> bool {
73        let (previous, current) = self.state(player, button);
74        previous && !current
75    }
76
77    pub fn held(&self, player: u8, button: GamepadButton) -> bool {
78        let (previous, current) = self.state(player, button);
79        previous && current
80    }
81}
82
83/// Tilt amount unit.
84#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
85pub struct Tilt;
86
87#[derive(Clone, Copy, Debug)]
88pub struct GamepadSticks {
89    left: Vector2D<f32, Tilt>,
90    right: Vector2D<f32, Tilt>,
91}
92
93fn with_dead_zone(
94    stick: Vector2D<f32, Tilt>,
95    dead_zone: f32,
96    square_dead_zone: f32,
97) -> Vector2D<f32, Tilt> {
98    let square_length = stick.square_length();
99    if square_length == 0.0 || square_length < square_dead_zone {
100        Vector2D::zero()
101    } else {
102        let length = square_length.sqrt();
103        // inverse lerp of the magnitude.
104        let t = (length - dead_zone) / (1.0 - dead_zone);
105        let new_length = t.clamp(0.0, 1.0);
106        let normalized = stick / length;
107        normalized * new_length
108    }
109}
110
111impl GamepadSticks {
112    pub fn poll(player: u8) -> Self {
113        let player = player as i32;
114        let left_up = unsafe { gamepad_axis(player, GamepadAxis::LeftUp as i32) };
115        let left_down = unsafe { gamepad_axis(player, GamepadAxis::LeftDown as i32) };
116        let left_left = unsafe { gamepad_axis(player, GamepadAxis::LeftLeft as i32) };
117        let left_right = unsafe { gamepad_axis(player, GamepadAxis::LeftRight as i32) };
118        let right_up = unsafe { gamepad_axis(player, GamepadAxis::RightUp as i32) };
119        let right_down = unsafe { gamepad_axis(player, GamepadAxis::RightDown as i32) };
120        let right_left = unsafe { gamepad_axis(player, GamepadAxis::RightLeft as i32) };
121        let right_right = unsafe { gamepad_axis(player, GamepadAxis::RightRight as i32) };
122        Self {
123            left: Vector2D::new(left_right - left_left, left_up - left_down),
124            right: Vector2D::new(right_right - right_left, right_up - right_down),
125        }
126    }
127
128    pub fn apply_dead_zone(&self, dead_zone: f32) -> Self {
129        if dead_zone == 0.0 {
130            self.clone()
131        } else {
132            let square_dead_zone = dead_zone.powi(2);
133            Self {
134                left: with_dead_zone(self.left, dead_zone, square_dead_zone),
135                right: with_dead_zone(self.right, dead_zone, square_dead_zone),
136            }
137        }
138    }
139
140    pub fn left(&self) -> Vector2D<f32, Tilt> {
141        self.left
142    }
143
144    pub fn right(&self) -> Vector2D<f32, Tilt> {
145        self.right
146    }
147}