1#![doc = include_str!("../README.md")]
2
3use std::{
4 sync::{
5 atomic::{AtomicBool, Ordering},
6 Arc,
7 },
8 time::Duration,
9};
10
11use arci::{gamepad::*, *};
12use indexmap::IndexMap;
13use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
14use serde::{Deserialize, Serialize};
15use serde_with::serde_as;
16use tracing::{debug, error, info};
17
18#[serde_as]
19#[derive(Debug, Serialize, Deserialize, Clone)]
20#[serde(deny_unknown_fields)]
21pub struct Map {
22 #[serde_as(as = "Vec<(_, _)>")]
23 #[serde(default = "default_button_map")]
24 button_map: IndexMap<gilrs::Button, Button>,
25 #[serde_as(as = "Vec<(_, _)>")]
26 #[serde(default = "default_axis_map")]
27 axis_map: IndexMap<gilrs::Axis, Axis>,
28 #[serde_as(as = "Vec<(_, _)>")]
29 #[serde(default = "default_axis_value_map")]
30 axis_value_map: IndexMap<Axis, f64>,
31}
32
33impl Map {
34 pub fn new() -> Self {
35 Self {
36 button_map: default_button_map(),
37 axis_map: default_axis_map(),
38 axis_value_map: default_axis_value_map(),
39 }
40 }
41
42 pub fn new_playstation() -> Self {
43 let mut button_map = default_button_map();
44 button_map.insert(gilrs::Button::East, Button::South);
45 button_map.insert(gilrs::Button::C, Button::East);
46 button_map.insert(gilrs::Button::North, Button::North);
47 button_map.insert(gilrs::Button::South, Button::West);
48 button_map.insert(gilrs::Button::West, Button::LeftTrigger);
49 button_map.insert(gilrs::Button::LeftTrigger, Button::LeftTrigger2);
50 button_map.insert(gilrs::Button::Z, Button::RightTrigger);
51 button_map.insert(gilrs::Button::RightTrigger, Button::RightTrigger2);
52 button_map.insert(gilrs::Button::LeftTrigger2, Button::Select);
53 button_map.insert(gilrs::Button::RightTrigger2, Button::Start);
54 button_map.insert(gilrs::Button::Select, Button::LeftThumb);
55 button_map.insert(gilrs::Button::Start, Button::RightThumb);
56
57 let mut axis_map = default_axis_map();
58 axis_map.insert(gilrs::Axis::LeftZ, Axis::RightStickX);
59 axis_map.insert(gilrs::Axis::RightZ, Axis::RightStickY);
60
61 let mut axis_value_map = default_axis_value_map();
62 axis_value_map.insert(Axis::RightStickX, -1.0);
63 axis_value_map.insert(Axis::RightStickY, -1.0);
64
65 Self {
66 button_map,
67 axis_map,
68 axis_value_map,
69 }
70 }
71
72 fn convert_button(&self, b: gilrs::Button) -> Button {
73 if let Some(e) = self.button_map.get(&b) {
74 debug!("convert_button {b:?} -> {e:?}");
75 *e
76 } else {
77 debug!("unknown map {b:?}");
78 Button::Unknown
79 }
80 }
81
82 fn convert_axis(&self, a: gilrs::Axis, v: f32) -> (Axis, f64) {
83 if let Some(e) = self.axis_map.get(&a) {
84 debug!("convert_axis {a:?} -> {e:?}");
85 (*e, v as f64 * self.axis_value_map.get(e).unwrap_or(&1.0))
86 } else {
87 debug!("unknown map {a:?}");
88 (Axis::Unknown, 0.0)
89 }
90 }
91
92 fn convert_event(&self, e: gilrs::EventType) -> Option<GamepadEvent> {
93 match e {
94 gilrs::EventType::ButtonPressed(b, _c) => {
95 Some(GamepadEvent::ButtonPressed(self.convert_button(b)))
96 }
97 gilrs::EventType::ButtonReleased(b, _c) => {
98 Some(GamepadEvent::ButtonReleased(self.convert_button(b)))
99 }
100 gilrs::EventType::AxisChanged(a, v, _c) => {
101 let (axis, value) = self.convert_axis(a, v);
102 Some(GamepadEvent::AxisChanged(axis, value))
103 }
104 _ => None,
105 }
106 }
107}
108
109impl Default for Map {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl JsonSchema for Map {
116 fn schema_name() -> String {
117 "Map".to_string()
118 }
119
120 fn json_schema(gen: &mut SchemaGenerator) -> Schema {
121 #[allow(dead_code)]
123 #[derive(JsonSchema)]
124 enum GilrsButton {
125 South,
126 East,
127 North,
128 West,
129 C,
130 Z,
131 LeftTrigger,
132 LeftTrigger2,
133 RightTrigger,
134 RightTrigger2,
135 Select,
136 Start,
137 Mode,
138 LeftThumb,
139 RightThumb,
140 DPadUp,
141 DPadDown,
142 DPadLeft,
143 DPadRight,
144 Unknown,
145 }
146 #[allow(dead_code)]
148 #[derive(JsonSchema)]
149 enum GilrsAxis {
150 LeftStickX,
151 LeftStickY,
152 LeftZ,
153 RightStickX,
154 RightStickY,
155 RightZ,
156 DPadX,
157 DPadY,
158 Unknown,
159 }
160 #[allow(dead_code)]
161 #[derive(JsonSchema)]
162 struct MapRepr {
163 button_map: Vec<(GilrsButton, Button)>,
164 axis_map: Vec<(GilrsAxis, Axis)>,
165 axis_value_map: Vec<(Axis, f64)>,
166 }
167
168 MapRepr::json_schema(gen)
169 }
170}
171
172fn default_button_map() -> IndexMap<gilrs::Button, Button> {
173 let mut button_map = IndexMap::new();
174 button_map.insert(gilrs::Button::South, Button::South);
175 button_map.insert(gilrs::Button::East, Button::East);
176 button_map.insert(gilrs::Button::North, Button::North);
177 button_map.insert(gilrs::Button::West, Button::West);
178 button_map.insert(gilrs::Button::LeftTrigger, Button::LeftTrigger);
179 button_map.insert(gilrs::Button::LeftTrigger2, Button::LeftTrigger2);
180 button_map.insert(gilrs::Button::RightTrigger, Button::RightTrigger);
181 button_map.insert(gilrs::Button::RightTrigger2, Button::RightTrigger2);
182 button_map.insert(gilrs::Button::Select, Button::Select);
183 button_map.insert(gilrs::Button::Start, Button::Start);
184 button_map.insert(gilrs::Button::Mode, Button::Mode);
185 button_map.insert(gilrs::Button::LeftThumb, Button::LeftThumb);
186 button_map.insert(gilrs::Button::RightThumb, Button::RightThumb);
187 button_map.insert(gilrs::Button::DPadUp, Button::DPadUp);
188 button_map.insert(gilrs::Button::DPadDown, Button::DPadDown);
189 button_map.insert(gilrs::Button::DPadLeft, Button::DPadLeft);
190 button_map.insert(gilrs::Button::DPadRight, Button::DPadRight);
191 button_map
192}
193
194fn default_axis_map() -> IndexMap<gilrs::Axis, Axis> {
195 let mut axis_map = IndexMap::new();
196 axis_map.insert(gilrs::Axis::LeftStickX, Axis::LeftStickX);
197 axis_map.insert(gilrs::Axis::LeftStickY, Axis::LeftStickY);
198 axis_map.insert(gilrs::Axis::RightStickX, Axis::RightStickX);
199 axis_map.insert(gilrs::Axis::RightStickY, Axis::RightStickY);
200 axis_map.insert(gilrs::Axis::DPadX, Axis::DPadX);
201 axis_map.insert(gilrs::Axis::DPadY, Axis::DPadY);
202 axis_map
203}
204
205fn default_axis_value_map() -> IndexMap<Axis, f64> {
206 let mut axis_value_map = IndexMap::new();
207 axis_value_map.insert(Axis::RightStickX, -1.0);
208 axis_value_map.insert(Axis::LeftStickX, -1.0);
209 axis_value_map
210}
211
212#[derive(Debug, Default)]
213pub enum FlagType {
214 #[default]
215 Event,
216 TimeStep(f64),
217}
218
219#[derive(Debug)]
220pub struct GilGamepad {
221 rx: flume::Receiver<GamepadEvent>,
222 is_running: Arc<AtomicBool>,
223}
224
225impl GilGamepad {
226 pub fn new(id: usize, map: Map, flag_type: FlagType) -> Self {
227 let (tx, rx) = flume::unbounded();
228 let is_running = Arc::new(AtomicBool::new(true));
229 let is_running_cloned = is_running.clone();
230 std::thread::spawn(move || {
231 let mut gil = gilrs::Gilrs::new().unwrap();
232 #[cfg_attr(target_os = "macos", allow(unused_mut))]
233 let mut selected_gamepad_id = None;
234 #[cfg(not(target_os = "macos"))]
236 {
237 let mut is_found = false;
238 for (connected_id, gamepad) in gil.gamepads() {
239 info!("{} is {:?}", gamepad.name(), gamepad.power_info());
240 if id == Into::<usize>::into(connected_id) {
241 is_found = true;
242 selected_gamepad_id = Some(connected_id);
243 }
244 }
245 if !is_found {
246 panic!("No Gamepad id={id} is found");
247 }
248 }
249 let mut is_connected = true;
250 tx.send(GamepadEvent::Connected).unwrap();
251 let mut last_message = GamepadEvent::Connected;
252 while is_running_cloned.load(Ordering::Relaxed) {
253 if let Some(gamepad_id) = selected_gamepad_id {
254 let gamepad = gil.gamepad(gamepad_id);
255 if is_connected && !gamepad.is_connected() {
256 error!("gamepad [{}] is disconnected", gamepad.name());
257 is_connected = false;
258 tx.send(GamepadEvent::Disconnected).unwrap();
259 last_message = GamepadEvent::Disconnected;
260 } else if !is_connected && gamepad.is_connected() {
261 info!("gamepad [{}] is connected", gamepad.name());
262 is_connected = true;
263 tx.send(GamepadEvent::Connected).unwrap();
264 last_message = GamepadEvent::Connected;
265 }
266 }
267 match flag_type {
268 FlagType::Event => GilGamepad::send_by_event(&mut gil, id, map.clone(), &tx),
269 FlagType::TimeStep(t) => {
270 last_message = GilGamepad::send_on_time(
271 &mut gil,
272 id,
273 map.clone(),
274 &tx,
275 last_message,
276 t,
277 )
278 }
279 }
280 }
281 });
282
283 Self { rx, is_running }
284 }
285
286 pub fn new_from_config(config: GilGamepadConfig) -> Self {
287 if config.time_step.is_normal() && config.time_step.is_sign_positive() {
288 Self::new(
289 config.device_id,
290 config.map,
291 FlagType::TimeStep(config.time_step),
292 )
293 } else {
294 Self::new(config.device_id, config.map, FlagType::Event)
295 }
296 }
297
298 fn send_by_event(
299 gil: &mut gilrs::Gilrs,
300 id: usize,
301 map: Map,
302 tx: &flume::Sender<GamepadEvent>,
303 ) {
304 match gil.next_event() {
306 Some(gilrs::Event {
307 id: recv_id, event, ..
308 }) => {
309 if id == Into::<usize>::into(recv_id) {
310 if let Some(e) = map.convert_event(event) {
311 tx.send(e).unwrap();
312 }
313 }
314 }
315 None => {
316 std::thread::sleep(Duration::from_secs_f64(0.01));
317 }
318 }
319 }
320
321 fn send_on_time(
322 gil: &mut gilrs::Gilrs,
323 id: usize,
324 map: Map,
325 tx: &flume::Sender<GamepadEvent>,
326 last_message: GamepadEvent,
327 time_step: f64,
328 ) -> GamepadEvent {
329 let mut msg = last_message;
330 match gil.next_event() {
331 Some(gilrs::Event {
332 id: recv_id, event, ..
333 }) => {
334 if id == Into::<usize>::into(recv_id) {
335 if let Some(e) = map.convert_event(event) {
336 tx.send(e.clone()).unwrap();
337 msg = e;
338 }
339 }
340 }
341 None => tx.send(msg.clone()).unwrap(),
342 }
343
344 std::thread::sleep(Duration::from_secs_f64(time_step));
345 msg
346 }
347}
348
349#[derive(Debug, Serialize, Deserialize, Clone, Default, JsonSchema)]
350#[serde(deny_unknown_fields)]
351pub struct GilGamepadConfig {
352 #[serde(default)]
353 device_id: usize,
354 #[serde(default)]
355 time_step: f64,
356 #[serde(default)]
357 map: Map,
358}
359
360#[async_trait]
361impl Gamepad for GilGamepad {
362 async fn next_event(&self) -> GamepadEvent {
363 match self.rx.recv_async().await {
364 Ok(e) => e,
365 Err(e) => {
366 error!("recv error: {e}");
367 GamepadEvent::Unknown
368 }
369 }
370 }
371
372 fn stop(&self) {
373 self.is_running.store(false, Ordering::Relaxed);
374 }
375}
376
377impl Drop for GilGamepad {
378 fn drop(&mut self) {
379 self.stop();
380 }
381}
382
383#[cfg(test)]
384mod test {
385 use assert_approx_eq::assert_approx_eq;
386
387 use super::*;
388
389 #[test]
390 fn test_playstation_map() {
391 let m = Map::new_playstation();
392 assert_eq!(
393 m.convert_button(gilrs::Button::North),
394 arci::gamepad::Button::North
395 );
396 assert_eq!(
397 m.convert_button(gilrs::Button::South),
398 arci::gamepad::Button::West
399 );
400 let (axis, value) = m.convert_axis(gilrs::Axis::RightStickX, 0.2);
401 assert_eq!(axis, arci::gamepad::Axis::RightStickX);
402 assert_approx_eq!(value, -0.2);
403 let (a, v) = m.convert_axis(gilrs::Axis::RightZ, 0.1);
404 assert_eq!(a, arci::gamepad::Axis::RightStickY);
405 assert!((v - -0.1).abs() < 0.00001);
406 }
407
408 #[test]
409 fn test_default_map() {
410 let m = Map::default();
411 assert_eq!(
412 m.convert_button(gilrs::Button::North),
413 arci::gamepad::Button::North
414 );
415 assert_eq!(
416 m.convert_button(gilrs::Button::South),
417 arci::gamepad::Button::South
418 );
419 let (a, v) = m.convert_axis(gilrs::Axis::RightStickY, 0.1);
420 assert_eq!(a, arci::gamepad::Axis::RightStickY);
421 assert!((v - 0.1).abs() < 0.00001);
422 }
423
424 #[test]
425 fn test_make_map() {
426 let m = Map {
427 button_map: IndexMap::new(),
428 axis_map: IndexMap::new(),
429 axis_value_map: IndexMap::new(),
430 };
431 assert_eq!(
432 m.convert_button(gilrs::Button::North),
433 arci::gamepad::Button::Unknown,
434 );
435 let (a, v) = m.convert_axis(gilrs::Axis::RightStickY, 0.1);
436 assert_eq!(a, arci::gamepad::Axis::Unknown);
437 assert!((v - 0.0).abs() < 0.00001);
438 }
439}