1#[cfg(not(target_arch = "wasm32"))]
9mod native;
10#[cfg(target_arch = "wasm32")]
11mod web;
12
13use shuyi_ui::RawInputEvent;
14use shuyi_ui::input::{PadId, PadKind, PadState, Pads, StickDef};
15
16pub struct PadInput {
18 #[cfg(not(target_arch = "wasm32"))]
20 source: native::Source,
21 pads: Pads,
22 focused: bool,
24}
25
26impl PadInput {
27 pub fn new(stick: StickDef) -> Self {
31 Self {
32 #[cfg(not(target_arch = "wasm32"))]
33 source: native::Source::new(),
34 pads: Pads::new(stick),
35 focused: true,
36 }
37 }
38
39 pub fn poll(&mut self, focused: bool) -> Vec<RawInputEvent> {
43 #[cfg(not(target_arch = "wasm32"))]
45 let connected = self.source.read();
46 #[cfg(target_arch = "wasm32")]
47 let connected = web::read();
48 let was = core::mem::replace(&mut self.focused, focused);
49 pad_events(&mut self.pads, &connected, (was, focused))
50 }
51}
52
53pub(crate) fn pad_events(
60 pads: &mut Pads,
61 connected: &[(PadId, PadKind, PadState)],
62 (was, focused): (bool, bool),
63) -> Vec<RawInputEvent> {
64 let heard = if focused { connected } else { &[] };
66 let mut events = RawInputEvent::from_pads(pads, heard);
67 if focused && !was {
68 events.retain(|event| !matches!(event, RawInputEvent::PadDown(..)));
69 }
70 events
71}
72
73#[cfg(test)]
74mod tests {
75 use shuyi_ui::input::Button;
76
77 use super::*;
78
79 fn pads() -> Pads {
80 Pads::new(StickDef {
81 press_percent: 50,
82 release_percent: 35,
83 })
84 }
85
86 fn pad(buttons: &[Button], stick: (f32, f32)) -> [(PadId, PadKind, PadState); 1] {
88 let state = PadState {
89 buttons: buttons.iter().copied().collect(),
90 left_stick: stick,
91 ..PadState::default()
92 };
93 [(0, PadKind::Xbox, state)]
94 }
95
96 const FOCUSED: (bool, bool) = (true, true);
97 const LOST: (bool, bool) = (true, false);
98 const AWAY: (bool, bool) = (false, false);
99 const BACK: (bool, bool) = (false, true);
100
101 #[test]
102 fn losing_focus_lets_go_of_buttons_and_stick_directions_and_presses_none() {
103 let mut p = pads();
104 let held = pad(&[Button::South], (1.0, 0.0));
105 let down = pad_events(&mut p, &held, FOCUSED);
106 assert_eq!(down.len(), 2, "{down:?}");
107 let mut up = pad_events(&mut p, &held, LOST);
108 up.sort_by_key(|e| format!("{e:?}"));
109 assert_eq!(
110 up,
111 [
112 RawInputEvent::PadUp(Button::LeftStickRight),
113 RawInputEvent::PadUp(Button::South),
114 ]
115 );
116 }
117
118 #[test]
119 fn a_press_while_unfocused_gives_nothing() {
120 let mut p = pads();
121 assert_eq!(pad_events(&mut p, &pad(&[], (0.0, 0.0)), FOCUSED), []);
122 let pressed = pad(&[Button::East], (0.0, 0.0));
123 assert_eq!(pad_events(&mut p, &pressed, LOST), []);
124 assert_eq!(pad_events(&mut p, &pressed, AWAY), []);
125 }
126
127 #[test]
128 fn a_button_held_across_the_change_counts_once_let_go_and_pressed_again() {
129 let mut p = pads();
130 let held = pad(&[Button::South], (0.0, 0.0));
131 let _ = pad_events(&mut p, &held, FOCUSED);
132 let _ = pad_events(&mut p, &held, LOST);
133 assert_eq!(pad_events(&mut p, &held, BACK), []);
135 assert_eq!(pad_events(&mut p, &held, FOCUSED), []);
136 let free = pad(&[], (0.0, 0.0));
137 let up = pad_events(&mut p, &free, FOCUSED);
138 assert_eq!(up, [RawInputEvent::PadUp(Button::South)]);
139 let down = pad_events(&mut p, &held, FOCUSED);
140 assert_eq!(down, [RawInputEvent::PadDown(Button::South, PadKind::Xbox)]);
141 }
142}