1use std::collections::HashMap;
2
3use glam::Vec2;
4use repame_input::GamepadState;
5use repose_core::input::{GamepadAxis, GamepadButton, GamepadEvent, GamepadId};
6
7pub const TRIGGER_HELD: f32 = 0.5;
8
9#[derive(Default)]
10pub struct PadBridge {
11 lx: f32,
12 ly: f32,
13 rx: f32,
14 ry: f32,
15 lt_held: bool,
16 rt_held: bool,
17 south: bool,
18 east: bool,
19 west: bool,
20 dpad_l: bool,
21 dpad_u: bool,
22 dpad_r: bool,
23 dpad_d: bool,
24 north: bool,
25 lb: bool,
26 rb: bool,
27 lt_edge: bool,
28 rt_edge: bool,
29 south_edge: bool,
30 east_edge: bool,
31 west_edge: bool,
32 dpad_l_edge: bool,
33 dpad_u_edge: bool,
34 dpad_r_edge: bool,
35 dpad_d_edge: bool,
36 north_edge: bool,
37 lb_edge: bool,
38 rb_edge: bool,
39}
40
41impl PadBridge {
42 pub fn button(&mut self, button: GamepadButton, pressed: bool) {
43 let (held, edge) = match button {
44 GamepadButton::South => (&mut self.south, &mut self.south_edge),
45 GamepadButton::East => (&mut self.east, &mut self.east_edge),
46 GamepadButton::West => (&mut self.west, &mut self.west_edge),
47 GamepadButton::North => (&mut self.north, &mut self.north_edge),
48 GamepadButton::DPadLeft => (&mut self.dpad_l, &mut self.dpad_l_edge),
49 GamepadButton::DPadUp => (&mut self.dpad_u, &mut self.dpad_u_edge),
50 GamepadButton::DPadRight => (&mut self.dpad_r, &mut self.dpad_r_edge),
51 GamepadButton::DPadDown => (&mut self.dpad_d, &mut self.dpad_d_edge),
52 GamepadButton::LeftShoulder => (&mut self.lb, &mut self.lb_edge),
53 GamepadButton::RightShoulder => (&mut self.rb, &mut self.rb_edge),
54 _ => return,
55 };
56 if pressed && !*held {
57 *edge = true;
58 }
59 *held = pressed;
60 }
61
62 pub fn axis(&mut self, axis: GamepadAxis, value: f32) {
63 match axis {
64 GamepadAxis::LeftStickX => self.lx = value,
65 GamepadAxis::LeftStickY => self.ly = value,
66 GamepadAxis::RightStickX => self.rx = value,
67 GamepadAxis::RightStickY => self.ry = value,
68 GamepadAxis::LeftTrigger => {
69 let held = value > TRIGGER_HELD;
70 if held && !self.lt_held {
71 self.lt_edge = true;
72 }
73 self.lt_held = held;
74 }
75 GamepadAxis::RightTrigger => {
76 let held = value > TRIGGER_HELD;
77 if held && !self.rt_held {
78 self.rt_edge = true;
79 }
80 self.rt_held = held;
81 }
82 }
83 }
84
85 pub fn snapshot(&self) -> GamepadState {
86 GamepadState {
87 left_stick: Vec2::new(self.lx, self.ly),
88 right_stick: Vec2::new(self.rx, self.ry),
89 right_trigger_held: self.rt_held,
90 right_trigger_pressed: self.rt_edge,
91 left_trigger_held: self.lt_held,
92 left_trigger_pressed: self.lt_edge,
93 south_pressed: self.south_edge,
94 east_pressed: self.east_edge,
95 west_pressed: self.west_edge,
96 dpad_left_pressed: self.dpad_l_edge,
97 dpad_up_pressed: self.dpad_u_edge,
98 dpad_right_pressed: self.dpad_r_edge,
99 dpad_down_pressed: self.dpad_d_edge,
100 north_pressed: self.north_edge,
101 left_shoulder_held: self.lb,
102 left_shoulder_pressed: self.lb_edge,
103 right_shoulder_held: self.rb,
104 right_shoulder_pressed: self.rb_edge,
105 }
106 }
107
108 pub fn clear_edges(&mut self) {
109 self.lt_edge = false;
110 self.rt_edge = false;
111 self.south_edge = false;
112 self.east_edge = false;
113 self.west_edge = false;
114 self.dpad_l_edge = false;
115 self.dpad_u_edge = false;
116 self.dpad_r_edge = false;
117 self.dpad_d_edge = false;
118 self.north_edge = false;
119 self.lb_edge = false;
120 self.rb_edge = false;
121 }
122}
123
124#[derive(Default)]
125pub struct PadBank {
126 pads: HashMap<GamepadId, PadBridge>,
127}
128
129impl PadBank {
130 pub fn feed(&mut self, events: Vec<GamepadEvent>) {
131 for ev in events {
132 match ev {
133 GamepadEvent::Connected { .. } => {}
134 GamepadEvent::Disconnected { id } => {
135 self.pads.remove(&id);
136 }
137 GamepadEvent::Button {
138 id,
139 button,
140 pressed,
141 } => {
142 self.pads.entry(id).or_default().button(button, pressed);
143 }
144 GamepadEvent::Axis { id, axis, value } => {
145 self.pads.entry(id).or_default().axis(axis, value);
146 }
147 }
148 }
149 }
150
151 pub fn drain_with_id(&mut self) -> Vec<(GamepadId, GamepadState)> {
155 let mut ids: Vec<GamepadId> = self.pads.keys().copied().collect();
156 ids.sort_by_key(|id| id.0);
157 let mut out = Vec::with_capacity(ids.len());
158 for id in ids {
159 if let Some(bridge) = self.pads.get_mut(&id) {
160 out.push((id, bridge.snapshot()));
161 bridge.clear_edges();
162 }
163 }
164 out
165 }
166
167 pub fn drain(&mut self) -> Vec<GamepadState> {
168 self.drain_with_id().into_iter().map(|(_, s)| s).collect()
169 }
170
171 pub fn live(&self) -> bool {
172 !self.pads.is_empty()
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn rising_edge_fires_once() {
182 let mut b = PadBridge::default();
183 b.button(GamepadButton::South, true);
184 assert!(b.snapshot().south_pressed);
185 b.clear_edges();
186 b.button(GamepadButton::South, true);
187 assert!(!b.snapshot().south_pressed);
188 }
189
190 #[test]
191 fn trigger_threshold_edges() {
192 let mut b = PadBridge::default();
193 b.axis(GamepadAxis::RightTrigger, 0.8);
194 let s = b.snapshot();
195 assert!(s.right_trigger_held && s.right_trigger_pressed);
196 }
197
198 #[test]
199 fn shoulder_and_dpad_down_buttons_track() {
200 let mut b = PadBridge::default();
201 b.button(GamepadButton::RightShoulder, true);
202 b.button(GamepadButton::LeftShoulder, true);
203 b.button(GamepadButton::DPadDown, true);
204 b.button(GamepadButton::West, true);
205 let s = b.snapshot();
206 assert!(s.right_shoulder_held && s.right_shoulder_pressed);
207 assert!(s.left_shoulder_held && s.left_shoulder_pressed);
208 assert!(s.dpad_down_pressed);
209 assert!(s.west_pressed);
210 }
211
212 #[test]
213 fn drain_with_id_routes_devices() {
214 let mut bank = PadBank::default();
215 let id0 = GamepadId(3);
216 let id1 = GamepadId(1);
217 bank.feed(vec![
218 GamepadEvent::Button {
219 id: id0,
220 button: GamepadButton::South,
221 pressed: true,
222 },
223 GamepadEvent::Button {
224 id: id1,
225 button: GamepadButton::East,
226 pressed: true,
227 },
228 ]);
229 let drained = bank.drain_with_id();
230 assert_eq!(drained.len(), 2);
231 assert_eq!(drained[0].0, id1, "id-sorted");
232 assert!(drained[1].1.south_pressed);
233 assert!(drained[0].1.east_pressed);
234 }
235}