1
//! POD types for the gamepad / game-controller surface
2
//! (SUPER_PLAN_2 §1 feature 6 + research/03 §"Feature 6").
3
//!
4
//! Cross-platform controller input: `gilrs` on the desktop
5
//! (Windows / Linux / macOS), iOS `GCController` + Android `InputDevice`
6
//! on mobile (research/03). Defined here in `azul-core` so the manager +
7
//! accessors cross the FFI without `azul-layout` as a dependency; the
8
//! stateful side lives in `azul_layout::managers::gamepad::GamepadManager`.
9
//!
10
//! Poll model, like the sensors: the backend keeps a [`GamepadState`]
11
//! snapshot per connected pad current, and a callback reads the latest each
12
//! frame (`CallbackInfo::get_gamepad_state`) to drive movement / menus.
13
//! Button + axis naming follows the SDL / gilrs "standard gamepad" mapping,
14
//! so the face buttons are Xbox-style: South = A, East = B, West = X,
15
//! North = Y.
16

            
17
/// A connected gamepad's id — stable for the lifetime of the connection,
18
/// assigned by the backend on connect. (gilrs `GamepadId` / the platform
19
/// device id, normalised to a `u32`.)
20
#[repr(C)]
21
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22
pub struct GamepadId {
23
    pub id: u32,
24
}
25

            
26
/// A standard-layout gamepad button. Face buttons are Xbox-style by
27
/// position (South = A / Cross, East = B / Circle, West = X / Square,
28
/// North = Y / Triangle), so layouts stay consistent across vendors.
29
///
30
/// The discriminant order is also the bit position in
31
/// [`GamepadState::buttons`] — don't reorder without bumping the ABI.
32
#[repr(C)]
33
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34
pub enum GamepadButton {
35
    /// Bottom face button (A / Cross).
36
    South,
37
    /// Right face button (B / Circle).
38
    East,
39
    /// Top face button (Y / Triangle).
40
    North,
41
    /// Left face button (X / Square).
42
    West,
43
    /// Left shoulder button (L1 / LB).
44
    LeftBumper,
45
    /// Right shoulder button (R1 / RB).
46
    RightBumper,
47
    /// Left trigger as a digital press (L2 / LT). Analog value: `LeftZ`.
48
    LeftTrigger,
49
    /// Right trigger as a digital press (R2 / RT). Analog value: `RightZ`.
50
    RightTrigger,
51
    /// Select / Back / Share.
52
    Select,
53
    /// Start / Options / Menu.
54
    Start,
55
    /// Vendor / guide button (Xbox / PS / Home).
56
    Mode,
57
    /// Left stick click (L3).
58
    LeftThumb,
59
    /// Right stick click (R3).
60
    RightThumb,
61
    /// D-pad up.
62
    DPadUp,
63
    /// D-pad down.
64
    DPadDown,
65
    /// D-pad left.
66
    DPadLeft,
67
    /// D-pad right.
68
    DPadRight,
69
}
70

            
71
/// A gamepad analog axis. Stick axes are in `[-1, 1]` (right / up positive);
72
/// trigger axes ([`GamepadAxis::LeftZ`] / [`GamepadAxis::RightZ`]) in
73
/// `[0, 1]`.
74
#[repr(C)]
75
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
76
pub enum GamepadAxis {
77
    /// Left stick horizontal (left −1 … right +1).
78
    LeftStickX,
79
    /// Left stick vertical (down −1 … up +1).
80
    LeftStickY,
81
    /// Right stick horizontal.
82
    RightStickX,
83
    /// Right stick vertical.
84
    RightStickY,
85
    /// Left trigger pressure (0 … 1).
86
    LeftZ,
87
    /// Right trigger pressure (0 … 1).
88
    RightZ,
89
}
90

            
91
/// Snapshot of one gamepad's state. Buttons are a bitset (bit `n` = the
92
/// [`GamepadButton`] with discriminant `n`); axes are explicit fields. All
93
/// POD / `Copy`, so it crosses the FFI by value.
94
#[repr(C)]
95
#[derive(Debug, Clone, Copy, PartialEq)]
96
pub struct GamepadState {
97
    /// Which pad this snapshot is for.
98
    pub id: GamepadId,
99
    /// `false` once the pad disconnects (the manager keeps the last slot so
100
    /// a callback can observe the disconnect).
101
    pub connected: bool,
102
    /// Pressed-button bitset — bit `n` set ⇔ the `GamepadButton` with
103
    /// discriminant `n` is held. Read via [`GamepadState::is_pressed`].
104
    pub buttons: u32,
105
    /// Left stick X in `[-1, 1]`.
106
    pub left_stick_x: f32,
107
    /// Left stick Y in `[-1, 1]`.
108
    pub left_stick_y: f32,
109
    /// Right stick X in `[-1, 1]`.
110
    pub right_stick_x: f32,
111
    /// Right stick Y in `[-1, 1]`.
112
    pub right_stick_y: f32,
113
    /// Left trigger pressure in `[0, 1]`.
114
    pub left_z: f32,
115
    /// Right trigger pressure in `[0, 1]`.
116
    pub right_z: f32,
117
}
118

            
119
impl GamepadButton {
120
    /// This button's bit in [`GamepadState::buttons`].
121
5
    pub fn bit(self) -> u32 {
122
5
        1u32 << (self as u32)
123
5
    }
124
}
125

            
126
impl GamepadState {
127
    /// An empty (disconnected) state for `id` — all buttons up, axes zero.
128
10
    pub fn empty(id: GamepadId) -> Self {
129
10
        Self {
130
10
            id,
131
10
            connected: false,
132
10
            buttons: 0,
133
10
            left_stick_x: 0.0,
134
10
            left_stick_y: 0.0,
135
10
            right_stick_x: 0.0,
136
10
            right_stick_y: 0.0,
137
10
            left_z: 0.0,
138
10
            right_z: 0.0,
139
10
        }
140
10
    }
141

            
142
    /// Whether `button` is currently held.
143
3
    pub fn is_pressed(&self, button: GamepadButton) -> bool {
144
3
        self.buttons & button.bit() != 0
145
3
    }
146

            
147
    /// The current value of `axis` (sticks `[-1, 1]`, triggers `[0, 1]`).
148
    pub fn axis(&self, axis: GamepadAxis) -> f32 {
149
        match axis {
150
            GamepadAxis::LeftStickX => self.left_stick_x,
151
            GamepadAxis::LeftStickY => self.left_stick_y,
152
            GamepadAxis::RightStickX => self.right_stick_x,
153
            GamepadAxis::RightStickY => self.right_stick_y,
154
            GamepadAxis::LeftZ => self.left_z,
155
            GamepadAxis::RightZ => self.right_z,
156
        }
157
    }
158
}
159

            
160
// FFI Option wrapper for `CallbackInfo::get_gamepad_state(id) ->
161
// Option<GamepadState>` (mirrors `OptionSensorReading`).
162
impl_option!(
163
    GamepadState,
164
    OptionGamepadState,
165
    [Debug, Clone, Copy, PartialEq]
166
);