1#[cfg(not(target_arch = "wasm32"))]
5use gilrs::{Axis, Button, Event, EventType, Gilrs};
6use std::collections::HashMap;
7use std::fmt::Debug;
8use winit::event::{ElementState, KeyEvent, MouseButton};
9use winit::keyboard::{KeyCode, PhysicalKey};
10
11pub trait GameAction: Copy + Eq + Debug + 'static {
13 fn count() -> usize;
15
16 fn index(&self) -> usize;
18
19 fn from_index(index: usize) -> Option<Self>;
21
22 fn move_negative_x() -> Option<Self> {
23 None
24 }
25
26 fn move_positive_x() -> Option<Self> {
27 None
28 }
29
30 fn move_negative_y() -> Option<Self> {
31 None
32 }
33
34 fn move_positive_y() -> Option<Self> {
35 None
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41pub enum MouseBinding {
42 Left,
43 Right,
44 Middle,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub enum Key {
50 W,
51 A,
52 S,
53 D,
54 Space,
55 Shift,
56 Alt,
57 Up,
58 Down,
59 Left,
60 Right,
61 F12,
62 Escape,
63}
64
65impl Key {
66 fn from_keycode(code: KeyCode) -> Option<Self> {
67 match code {
68 KeyCode::KeyW => Some(Key::W),
69 KeyCode::KeyA => Some(Key::A),
70 KeyCode::KeyS => Some(Key::S),
71 KeyCode::KeyD => Some(Key::D),
72 KeyCode::Space => Some(Key::Space),
73 KeyCode::ShiftLeft => Some(Key::Shift),
74 KeyCode::AltLeft => Some(Key::Alt),
75 KeyCode::ArrowUp => Some(Key::Up),
76 KeyCode::ArrowDown => Some(Key::Down),
77 KeyCode::ArrowLeft => Some(Key::Left),
78 KeyCode::ArrowRight => Some(Key::Right),
79 KeyCode::F12 => Some(Key::F12),
80 KeyCode::Escape => Some(Key::Escape),
81 _ => None,
82 }
83 }
84}
85
86pub struct InputMap<A: GameAction> {
88 keyboard_map: HashMap<Key, A>,
89 mouse_map: HashMap<MouseBinding, A>,
90 #[cfg(not(target_arch = "wasm32"))]
91 gamepad_map: HashMap<Button, A>,
92}
93
94impl<A: GameAction> InputMap<A> {
95 pub fn new() -> Self {
96 Self {
97 keyboard_map: HashMap::new(),
98 mouse_map: HashMap::new(),
99 #[cfg(not(target_arch = "wasm32"))]
100 gamepad_map: HashMap::new(),
101 }
102 }
103
104 pub fn bind_key(&mut self, key: Key, action: A) {
105 self.keyboard_map.insert(key, action);
106 }
107
108 pub fn bind_mouse(&mut self, button: MouseBinding, action: A) {
109 self.mouse_map.insert(button, action);
110 }
111
112 #[cfg(not(target_arch = "wasm32"))]
113 pub fn bind_button(&mut self, button: Button, action: A) {
114 self.gamepad_map.insert(button, action);
115 }
116
117 #[cfg(not(target_arch = "wasm32"))]
118 fn get_action_for_button(&self, button: Button) -> Option<A> {
119 self.gamepad_map.get(&button).copied()
120 }
121}
122
123impl<A: GameAction> Default for InputMap<A> {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129#[derive(Debug, Clone, Copy)]
131struct BufferedInput<A: GameAction> {
132 action: A,
133 time_pressed: f32,
134}
135
136pub struct InputState<A: GameAction> {
138 keys: [bool; 13],
139 keys_prev: [bool; 13],
140 actions: Vec<bool>,
141 actions_prev: Vec<bool>,
142 mouse_buttons: [bool; 3],
143 gamepad_axes: [f32; 2],
144
145 #[cfg(not(target_arch = "wasm32"))]
146 gamepad_buttons: Vec<bool>,
147 #[cfg(not(target_arch = "wasm32"))]
148 gamepad_start: bool,
149 #[cfg(not(target_arch = "wasm32"))]
150 gamepad_start_prev: bool,
151 input_map: InputMap<A>,
152
153 #[cfg(not(target_arch = "wasm32"))]
154 gilrs: Option<Gilrs>,
155 input_buffer: Vec<BufferedInput<A>>,
156 current_time: f32,
157}
158
159impl<A: GameAction> InputState<A> {
160 pub fn new() -> Self {
161 let action_count = A::count();
162 Self {
163 keys: [false; 13],
164 keys_prev: [false; 13],
165 actions: vec![false; action_count],
166 actions_prev: vec![false; action_count],
167 mouse_buttons: [false; 3],
168 gamepad_axes: [0.0; 2],
169 #[cfg(not(target_arch = "wasm32"))]
170 gamepad_buttons: vec![false; action_count],
171 #[cfg(not(target_arch = "wasm32"))]
172 gamepad_start: false,
173 #[cfg(not(target_arch = "wasm32"))]
174 gamepad_start_prev: false,
175 input_map: InputMap::new(),
176
177 #[cfg(not(target_arch = "wasm32"))]
178 gilrs: match Gilrs::new() {
179 Ok(g) => Some(g),
180 Err(e) => {
181 log::warn!("Failed to initialize gamepad support: {e}");
182 None
183 }
184 },
185 input_buffer: Vec::with_capacity(8),
186 current_time: 0.0,
187 }
188 }
189
190 #[cfg(not(target_arch = "wasm32"))]
191 pub fn is_gamepad_start_just_pressed(&self) -> bool {
192 self.gamepad_start && !self.gamepad_start_prev
193 }
194
195 #[cfg(target_arch = "wasm32")]
196 pub fn is_gamepad_start_just_pressed(&self) -> bool {
197 false
198 }
199
200 pub fn is_action_pressed(&self, action: A) -> bool {
201 self.actions[action.index()]
202 }
203
204 pub fn is_action_just_pressed(&self, action: A) -> bool {
205 let idx = action.index();
206 self.actions[idx] && !self.actions_prev[idx]
207 }
208
209 pub fn was_action_pressed_buffered(&self, action: A, buffer_window: f32) -> bool {
210 if self.is_action_just_pressed(action) {
211 return true;
212 }
213
214 for buffered in &self.input_buffer {
215 if buffered.action == action {
216 let elapsed = self.current_time - buffered.time_pressed;
217 if elapsed <= buffer_window {
218 return true;
219 }
220 }
221 }
222
223 false
224 }
225
226 #[allow(dead_code)]
228 pub fn is_key_pressed(&self, key: Key) -> bool {
229 self.keys[key as usize]
230 }
231
232 #[allow(dead_code)]
234 pub fn is_key_just_pressed(&self, key: Key) -> bool {
235 let idx = key as usize;
236 self.keys[idx] && !self.keys_prev[idx]
237 }
238
239 pub fn is_mouse_pressed(&self, button: MouseButton) -> bool {
240 match button {
241 MouseButton::Left => self.mouse_buttons[0],
242 MouseButton::Right => self.mouse_buttons[1],
243 MouseButton::Middle => self.mouse_buttons[2],
244 _ => false,
245 }
246 }
247
248 pub fn get_move_x(&self) -> f32 {
250 let mut value = 0.0;
251
252 if let Some(left) = A::move_negative_x()
253 && self.is_action_pressed(left)
254 {
255 value -= 1.0;
256 }
257 if let Some(right) = A::move_positive_x()
258 && self.is_action_pressed(right)
259 {
260 value += 1.0;
261 }
262
263 if self.gamepad_axes[0].abs() > 0.1 {
264 value = self.gamepad_axes[0];
265 }
266
267 value
268 }
269
270 pub fn get_move_y(&self) -> f32 {
272 let mut value = 0.0;
273
274 if let Some(up) = A::move_negative_y()
275 && self.is_action_pressed(up)
276 {
277 value -= 1.0;
278 }
279 if let Some(down) = A::move_positive_y()
280 && self.is_action_pressed(down)
281 {
282 value += 1.0;
283 }
284
285 if self.gamepad_axes[1].abs() > 0.1 {
286 value = self.gamepad_axes[1];
287 }
288
289 value
290 }
291
292 pub fn any_keyboard_or_mouse(&self) -> bool {
293 self.keys.iter().any(|&k| k) || self.mouse_buttons.iter().any(|&b| b)
294 }
295
296 pub fn any_gamepad(&self) -> bool {
297 if self.gamepad_axes.iter().any(|&a| a.abs() > 0.1) {
298 return true;
299 }
300 #[cfg(not(target_arch = "wasm32"))]
301 {
302 if self.gamepad_buttons.iter().any(|&b| b) {
303 return true;
304 }
305 }
306 false
307 }
308
309 pub fn begin_frame(&mut self, delta_time: f32) {
313 self.actions_prev.clone_from(&self.actions);
314 self.current_time += delta_time;
315
316 self.actions.fill(false);
318 for (&key, &action) in &self.input_map.keyboard_map {
319 if self.keys[key as usize] {
320 self.actions[action.index()] = true;
321 }
322 }
323
324 for (&binding, &action) in &self.input_map.mouse_map {
326 let idx = match binding {
327 MouseBinding::Left => 0,
328 MouseBinding::Right => 1,
329 MouseBinding::Middle => 2,
330 };
331 if self.mouse_buttons[idx] {
332 self.actions[action.index()] = true;
333 }
334 }
335
336 #[cfg(not(target_arch = "wasm32"))]
338 {
339 if let Some(ref mut gilrs) = self.gilrs {
340 while let Some(Event { event, .. }) = gilrs.next_event() {
341 match event {
342 EventType::ButtonPressed(button, _) => {
343 if button == Button::Start {
344 self.gamepad_start = true;
345 } else if let Some(action) =
346 self.input_map.get_action_for_button(button)
347 {
348 self.gamepad_buttons[action.index()] = true;
349 }
350 }
351 EventType::ButtonReleased(button, _) => {
352 if button == Button::Start {
353 self.gamepad_start = false;
354 } else if let Some(action) =
355 self.input_map.get_action_for_button(button)
356 {
357 self.gamepad_buttons[action.index()] = false;
358 }
359 }
360 EventType::AxisChanged(Axis::LeftStickX, value, _) => {
361 self.gamepad_axes[0] = value;
362 }
363 EventType::AxisChanged(Axis::LeftStickY, value, _) => {
364 self.gamepad_axes[1] = -value;
365 }
366 _ => {}
367 }
368 }
369 }
370
371 let action_count = A::count();
373 for i in 0..action_count {
374 if self.gamepad_buttons[i] {
375 self.actions[i] = true;
376 }
377 }
378 }
379
380 let action_count = A::count();
382 for i in 0..action_count {
383 if self.actions[i]
384 && !self.actions_prev[i]
385 && let Some(action) = A::from_index(i)
386 {
387 self.input_buffer.push(BufferedInput {
388 action,
389 time_pressed: self.current_time,
390 });
391 }
392 }
393
394 self.input_buffer
396 .retain(|buffered| self.current_time - buffered.time_pressed < 1.0);
397 }
398
399 pub fn end_frame(&mut self) {
400 self.keys_prev = self.keys;
401 #[cfg(not(target_arch = "wasm32"))]
402 {
403 self.gamepad_start_prev = self.gamepad_start;
404 }
405 }
406
407 pub(crate) fn handle_key_event(&mut self, event: &KeyEvent) {
409 let pressed = event.state == ElementState::Pressed;
410
411 if let PhysicalKey::Code(keycode) = event.physical_key
412 && let Some(key) = Key::from_keycode(keycode)
413 {
414 self.keys[key as usize] = pressed;
415 }
416 }
417
418 pub(crate) fn handle_mouse_button(&mut self, button: MouseButton, pressed: bool) {
420 match button {
421 MouseButton::Left => self.mouse_buttons[0] = pressed,
422 MouseButton::Right => self.mouse_buttons[1] = pressed,
423 MouseButton::Middle => self.mouse_buttons[2] = pressed,
424 _ => {}
425 }
426 }
427
428 pub fn input_map_mut(&mut self) -> &mut InputMap<A> {
429 &mut self.input_map
430 }
431}
432
433impl<A: GameAction> Default for InputState<A> {
434 fn default() -> Self {
435 Self::new()
436 }
437}