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 vendor_id: u16,
12 product_id: u16,
13 lx: f32,
14 ly: f32,
15 rx: f32,
16 ry: f32,
17 lt_held: bool,
18 rt_held: bool,
19 south: bool,
20 east: bool,
21 west: bool,
22 dpad_l: bool,
23 dpad_u: bool,
24 dpad_r: bool,
25 dpad_d: bool,
26 north: bool,
27 lb: bool,
28 rb: bool,
29 start: bool,
30 select: bool,
31 l3: bool,
32 r3: bool,
33 lt_edge: bool,
34 rt_edge: bool,
35 south_edge: bool,
36 east_edge: bool,
37 west_edge: bool,
38 dpad_l_edge: bool,
39 dpad_u_edge: bool,
40 dpad_r_edge: bool,
41 dpad_d_edge: bool,
42 north_edge: bool,
43 lb_edge: bool,
44 rb_edge: bool,
45 start_edge: bool,
46 select_edge: bool,
47 l3_edge: bool,
48 r3_edge: bool,
49}
50
51impl PadBridge {
52 pub fn button(&mut self, button: GamepadButton, pressed: bool) {
53 let (held, edge) = match button {
54 GamepadButton::South => (&mut self.south, &mut self.south_edge),
55 GamepadButton::East => (&mut self.east, &mut self.east_edge),
56 GamepadButton::West => (&mut self.west, &mut self.west_edge),
57 GamepadButton::North => (&mut self.north, &mut self.north_edge),
58 GamepadButton::DPadLeft => (&mut self.dpad_l, &mut self.dpad_l_edge),
59 GamepadButton::DPadUp => (&mut self.dpad_u, &mut self.dpad_u_edge),
60 GamepadButton::DPadRight => (&mut self.dpad_r, &mut self.dpad_r_edge),
61 GamepadButton::DPadDown => (&mut self.dpad_d, &mut self.dpad_d_edge),
62 GamepadButton::LeftShoulder => (&mut self.lb, &mut self.lb_edge),
63 GamepadButton::RightShoulder => (&mut self.rb, &mut self.rb_edge),
64 GamepadButton::Start => (&mut self.start, &mut self.start_edge),
65 GamepadButton::Select => (&mut self.select, &mut self.select_edge),
66 GamepadButton::LeftStick => (&mut self.l3, &mut self.l3_edge),
67 GamepadButton::RightStick => (&mut self.r3, &mut self.r3_edge),
68 };
69 if pressed && !*held {
70 *edge = true;
71 }
72 *held = pressed;
73 }
74
75 pub fn axis(&mut self, axis: GamepadAxis, value: f32) {
76 match axis {
77 GamepadAxis::LeftStickX => self.lx = value,
78 GamepadAxis::LeftStickY => self.ly = value,
79 GamepadAxis::RightStickX => self.rx = value,
80 GamepadAxis::RightStickY => self.ry = value,
81 GamepadAxis::LeftTrigger => {
82 let held = value > TRIGGER_HELD;
83 if held && !self.lt_held {
84 self.lt_edge = true;
85 }
86 self.lt_held = held;
87 }
88 GamepadAxis::RightTrigger => {
89 let held = value > TRIGGER_HELD;
90 if held && !self.rt_held {
91 self.rt_edge = true;
92 }
93 self.rt_held = held;
94 }
95 }
96 }
97
98 pub fn snapshot(&self) -> GamepadState {
99 GamepadState {
100 vendor_id: self.vendor_id,
101 product_id: self.product_id,
102 left_stick: Vec2::new(self.lx, self.ly),
103 right_stick: Vec2::new(self.rx, self.ry),
104 left_trigger_held: self.lt_held,
105 left_trigger_pressed: self.lt_edge,
106 right_trigger_held: self.rt_held,
107 right_trigger_pressed: self.rt_edge,
108 south_held: self.south,
109 south_pressed: self.south_edge,
110 east_held: self.east,
111 east_pressed: self.east_edge,
112 west_held: self.west,
113 west_pressed: self.west_edge,
114 north_held: self.north,
115 north_pressed: self.north_edge,
116 dpad_left_held: self.dpad_l,
117 dpad_left_pressed: self.dpad_l_edge,
118 dpad_up_held: self.dpad_u,
119 dpad_up_pressed: self.dpad_u_edge,
120 dpad_right_held: self.dpad_r,
121 dpad_right_pressed: self.dpad_r_edge,
122 dpad_down_held: self.dpad_d,
123 dpad_down_pressed: self.dpad_d_edge,
124 left_shoulder_held: self.lb,
125 left_shoulder_pressed: self.lb_edge,
126 right_shoulder_held: self.rb,
127 right_shoulder_pressed: self.rb_edge,
128 start_held: self.start,
129 start_pressed: self.start_edge,
130 select_held: self.select,
131 select_pressed: self.select_edge,
132 left_stick_click_held: self.l3,
133 left_stick_click_pressed: self.l3_edge,
134 right_stick_click_held: self.r3,
135 right_stick_click_pressed: self.r3_edge,
136 }
137 }
138
139 pub fn clear_edges(&mut self) {
140 self.lt_edge = false;
141 self.rt_edge = false;
142 self.south_edge = false;
143 self.east_edge = false;
144 self.west_edge = false;
145 self.dpad_l_edge = false;
146 self.dpad_u_edge = false;
147 self.dpad_r_edge = false;
148 self.dpad_d_edge = false;
149 self.north_edge = false;
150 self.lb_edge = false;
151 self.rb_edge = false;
152 self.start_edge = false;
153 self.select_edge = false;
154 self.l3_edge = false;
155 self.r3_edge = false;
156 }
157}
158
159#[derive(Default)]
160pub struct PadBank {
161 pads: HashMap<GamepadId, PadBridge>,
162}
163
164impl PadBank {
165 pub fn feed(&mut self, events: Vec<GamepadEvent>) {
166 for ev in events {
167 match ev {
168 GamepadEvent::Connected {
169 id,
170 vendor_id,
171 product_id,
172 ..
173 } => {
174 let pad = self.pads.entry(id).or_default();
175 pad.vendor_id = vendor_id;
176 pad.product_id = product_id;
177 }
178 GamepadEvent::Disconnected { id } => {
179 self.pads.remove(&id);
180 }
181 GamepadEvent::Button {
182 id,
183 button,
184 pressed,
185 } => {
186 self.pads.entry(id).or_default().button(button, pressed);
187 }
188 GamepadEvent::Axis { id, axis, value } => {
189 self.pads.entry(id).or_default().axis(axis, value);
190 }
191 }
192 }
193 }
194
195 pub fn drain_with_id(&mut self) -> Vec<(GamepadId, GamepadState)> {
199 let mut ids: Vec<GamepadId> = self.pads.keys().copied().collect();
200 ids.sort_by_key(|id| id.0);
201 let mut out = Vec::with_capacity(ids.len());
202 for id in ids {
203 if let Some(bridge) = self.pads.get_mut(&id) {
204 out.push((id, bridge.snapshot()));
205 bridge.clear_edges();
206 }
207 }
208 out
209 }
210
211 pub fn drain(&mut self) -> Vec<GamepadState> {
212 self.drain_with_id().into_iter().map(|(_, s)| s).collect()
213 }
214
215 pub fn live(&self) -> bool {
216 !self.pads.is_empty()
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn rising_edge_fires_once() {
226 let mut b = PadBridge::default();
227 b.button(GamepadButton::South, true);
228 assert!(b.snapshot().south_pressed);
229 b.clear_edges();
230 b.button(GamepadButton::South, true);
231 assert!(!b.snapshot().south_pressed);
232 }
233
234 #[test]
235 fn trigger_threshold_edges() {
236 let mut b = PadBridge::default();
237 b.axis(GamepadAxis::RightTrigger, 0.8);
238 let s = b.snapshot();
239 assert!(s.right_trigger_held && s.right_trigger_pressed);
240 }
241
242 #[test]
243 fn shoulder_and_dpad_down_buttons_track() {
244 let mut b = PadBridge::default();
245 b.button(GamepadButton::RightShoulder, true);
246 b.button(GamepadButton::LeftShoulder, true);
247 b.button(GamepadButton::DPadDown, true);
248 b.button(GamepadButton::West, true);
249 let s = b.snapshot();
250 assert!(s.right_shoulder_held && s.right_shoulder_pressed);
251 assert!(s.left_shoulder_held && s.left_shoulder_pressed);
252 assert!(s.dpad_down_pressed);
253 assert!(s.west_pressed);
254 }
255
256 #[test]
257 fn drain_with_id_routes_devices() {
258 let mut bank = PadBank::default();
259 let id0 = GamepadId(3);
260 let id1 = GamepadId(1);
261 bank.feed(vec![
262 GamepadEvent::Connected {
263 id: id0,
264 name: "GameCube Adapter".to_string(),
265 vendor_id: 0x057e,
266 product_id: 0x0337,
267 },
268 GamepadEvent::Button {
269 id: id0,
270 button: GamepadButton::South,
271 pressed: true,
272 },
273 GamepadEvent::Button {
274 id: id1,
275 button: GamepadButton::East,
276 pressed: true,
277 },
278 ]);
279 let drained = bank.drain_with_id();
280 assert_eq!(drained.len(), 2);
281 assert_eq!(drained[0].0, id1, "id-sorted");
282 assert!(drained[1].1.south_pressed);
283 assert!(drained[0].1.east_pressed);
284 assert_eq!(
285 (drained[1].1.vendor_id, drained[1].1.product_id),
286 (0x057e, 0x0337),
287 "pad identity must reach the game"
288 );
289 assert_eq!(
290 (drained[0].1.vendor_id, drained[0].1.product_id),
291 (0, 0),
292 "a pad that never reported ids reads as zero"
293 );
294 }
295}