micro_games_kit/
gamepad.rs

1use gilrs::{Axis, Button, GamepadId, Gilrs};
2use spitfire_input::{InputActionOrAxisRef, InputAxis};
3use std::{
4    cell::RefCell,
5    collections::{HashMap, HashSet},
6    rc::Rc,
7};
8
9#[derive(Debug, Clone)]
10pub enum GamepadInputAxis {
11    Single {
12        input: InputActionOrAxisRef,
13        deadzone: f32,
14    },
15    Double {
16        negative: InputActionOrAxisRef,
17        positive: InputActionOrAxisRef,
18        deadzone: f32,
19    },
20}
21
22impl GamepadInputAxis {
23    pub fn single(input: impl Into<InputActionOrAxisRef>, deadzone: f32) -> Self {
24        Self::Single {
25            input: input.into(),
26            deadzone,
27        }
28    }
29
30    pub fn double(
31        negative: impl Into<InputActionOrAxisRef>,
32        positive: impl Into<InputActionOrAxisRef>,
33        deadzone: f32,
34    ) -> Self {
35        Self::Double {
36            negative: negative.into(),
37            positive: positive.into(),
38            deadzone,
39        }
40    }
41}
42
43#[derive(Clone)]
44pub struct GamepadInput {
45    instance: Rc<RefCell<Gilrs>>,
46    used_gamepads: Rc<RefCell<HashSet<GamepadId>>>,
47    id: Option<GamepadId>,
48    pub buttons: HashMap<Button, InputActionOrAxisRef>,
49    pub axes: HashMap<Axis, GamepadInputAxis>,
50    pub auto_acquire: bool,
51}
52
53impl Drop for GamepadInput {
54    fn drop(&mut self) {
55        self.release();
56    }
57}
58
59impl GamepadInput {
60    pub fn acquire(&mut self) -> bool {
61        self.release();
62        let mut used_gamepads = self.used_gamepads.borrow_mut();
63        for (id, _) in self.instance.borrow().gamepads() {
64            if !used_gamepads.contains(&id) {
65                used_gamepads.insert(id);
66                self.id = Some(id);
67                return true;
68            }
69        }
70        false
71    }
72
73    pub fn release(&mut self) {
74        if let Some(id) = self.id.as_ref() {
75            self.used_gamepads.borrow_mut().remove(id);
76        }
77        self.id = None;
78    }
79
80    pub fn id(&self) -> Option<GamepadId> {
81        self.id
82    }
83
84    pub fn is_connected(&self) -> bool {
85        self.id.is_some()
86    }
87
88    pub fn button(mut self, id: Button, input: impl Into<InputActionOrAxisRef>) -> Self {
89        self.buttons.insert(id, input.into());
90        self
91    }
92
93    pub fn axis(mut self, id: Axis, input: impl Into<GamepadInputAxis>) -> Self {
94        self.axes.insert(id, input.into());
95        self
96    }
97
98    pub fn auto_acquire(mut self) -> Self {
99        self.auto_acquire = true;
100        self
101    }
102
103    pub fn apply(&mut self) {
104        if self.auto_acquire && !self.is_connected() {
105            self.acquire();
106        }
107        if let Some(id) = self.id {
108            let instance = self.instance.borrow();
109            let mut used_gamepads = self.used_gamepads.borrow_mut();
110            if let Some(gamepad) = instance.connected_gamepad(id) {
111                for (id, input) in &mut self.buttons {
112                    if let Some(data) = gamepad.button_data(*id) {
113                        match input {
114                            InputActionOrAxisRef::Action(input) => {
115                                input.set(input.get().change(data.is_pressed()));
116                            }
117                            InputActionOrAxisRef::Axis(input) => {
118                                input.set(InputAxis(data.value()));
119                            }
120                            _ => {}
121                        }
122                    }
123                }
124                for (id, input) in &mut self.axes {
125                    if let Some(data) = gamepad.axis_data(*id) {
126                        match input {
127                            GamepadInputAxis::Single { input, deadzone } => {
128                                let mut value = data.value().abs();
129                                if value < *deadzone {
130                                    value = 0.0;
131                                }
132                                match input {
133                                    InputActionOrAxisRef::Action(input) => {
134                                        input.set(input.get().change(value > 0.5));
135                                    }
136                                    InputActionOrAxisRef::Axis(input) => {
137                                        input.set(InputAxis(value));
138                                    }
139                                    _ => {}
140                                }
141                            }
142                            GamepadInputAxis::Double {
143                                negative,
144                                positive,
145                                deadzone,
146                            } => {
147                                let mut value = data.value();
148                                if value.abs() < *deadzone {
149                                    value = 0.0;
150                                }
151                                match positive {
152                                    InputActionOrAxisRef::Action(input) => {
153                                        input.set(input.get().change(value > 0.5));
154                                    }
155                                    InputActionOrAxisRef::Axis(input) => {
156                                        input.set(InputAxis(value.max(0.0)));
157                                    }
158                                    _ => {}
159                                }
160                                match negative {
161                                    InputActionOrAxisRef::Action(input) => {
162                                        input.set(input.get().change(value < -0.5));
163                                    }
164                                    InputActionOrAxisRef::Axis(input) => {
165                                        input.set(InputAxis(-value.min(0.0)));
166                                    }
167                                    _ => {}
168                                }
169                            }
170                        }
171                    }
172                }
173            } else {
174                used_gamepads.remove(&id);
175                self.id = None;
176            }
177        }
178    }
179}
180
181pub struct GamepadManager {
182    instance: Option<Rc<RefCell<Gilrs>>>,
183    used_gamepads: Rc<RefCell<HashSet<GamepadId>>>,
184}
185
186impl Default for GamepadManager {
187    fn default() -> Self {
188        Self {
189            instance: Gilrs::new()
190                .ok()
191                .map(|instance| Rc::new(RefCell::new(instance))),
192            used_gamepads: Default::default(),
193        }
194    }
195}
196
197impl GamepadManager {
198    pub fn is_supported(&self) -> bool {
199        self.instance.is_some()
200    }
201
202    pub fn request_gamepad(&self) -> Option<GamepadInput> {
203        Some(GamepadInput {
204            instance: self.instance.as_ref()?.clone(),
205            used_gamepads: self.used_gamepads.clone(),
206            id: Default::default(),
207            buttons: Default::default(),
208            axes: Default::default(),
209            auto_acquire: Default::default(),
210        })
211    }
212
213    pub fn maintain(&mut self) {
214        if let Some(instance) = self.instance.as_mut() {
215            while instance.borrow_mut().next_event().is_some() {}
216        }
217    }
218}