arci_gamepad_gilrs/
lib.rs

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        // https://docs.rs/gilrs/0.8/gilrs/ev/enum.Button.html
122        #[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        // https://docs.rs/gilrs/0.8/gilrs/ev/enum.Axis.html
147        #[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            // TODO: On macOS `gamepads()` does not works.
235            #[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        // gil.next_event is no block. We have to polling it.
305        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}