Skip to main content

limnus_gamepad_gilrs/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/limnus
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use gilrs::{Error, EventType, Gilrs};
6use limnus_app::prelude::{App, Plugin};
7use limnus_default_stages::First;
8use limnus_gamepad::{Axis, Button, GamepadMessage, Gamepads};
9use limnus_local_resource::prelude::LocalResource;
10use limnus_message::Messages;
11use limnus_system_params::prelude::*;
12use tracing::trace;
13
14#[derive(Debug)]
15pub enum GamepadError {
16    Error(Error),
17}
18
19impl From<Error> for GamepadError {
20    fn from(error: Error) -> Self {
21        Self::Error(error)
22    }
23}
24
25#[inline]
26const fn convert_axis(axis: &gilrs::Axis) -> Option<Axis> {
27    let converted = match axis {
28        gilrs::Axis::LeftStickX => Axis::LeftStickX,
29        gilrs::Axis::LeftStickY => Axis::LeftStickY,
30        gilrs::Axis::RightStickX => Axis::RightStickX,
31        gilrs::Axis::RightStickY => Axis::RightStickY,
32        _ => return None,
33    };
34    Some(converted)
35}
36
37#[inline]
38const fn convert_button(axis: &gilrs::Button) -> Option<Button> {
39    let converted = match axis {
40        // Action Pad
41        gilrs::Button::South => Button::South,
42        gilrs::Button::East => Button::East,
43        gilrs::Button::North => Button::North,
44        gilrs::Button::West => Button::West,
45
46        // Triggers
47        gilrs::Button::LeftTrigger => Button::LeftTrigger,
48        gilrs::Button::LeftTrigger2 => Button::LeftTrigger2,
49        gilrs::Button::RightTrigger => Button::RightTrigger,
50        gilrs::Button::RightTrigger2 => Button::RightTrigger2,
51
52        // Menu Buttons
53        gilrs::Button::Select => Button::Select,
54        gilrs::Button::Start => Button::Start,
55        gilrs::Button::Mode => Button::Mode,
56
57        // Sticks
58        gilrs::Button::LeftThumb => Button::LeftThumb,
59        gilrs::Button::RightThumb => Button::RightThumb,
60
61        // D-Pad
62        gilrs::Button::DPadUp => Button::DPadUp,
63        gilrs::Button::DPadDown => Button::DPadDown,
64        gilrs::Button::DPadLeft => Button::DPadLeft,
65        gilrs::Button::DPadRight => Button::DPadRight,
66
67        _ => return None,
68    };
69    Some(converted)
70}
71
72#[derive(Debug, LocalResource)]
73pub struct GamepadGilrs {
74    #[allow(dead_code)]
75    gilrs: Gilrs,
76}
77
78impl GamepadGilrs {
79    pub fn new() -> Result<Self, GamepadError> {
80        let gilrs = Gilrs::new()?;
81
82        Ok(Self { gilrs })
83    }
84
85    pub fn debug_output(&self) {
86        for (id, gamepad) in self.gilrs.gamepads() {
87            trace!("{id}:{} is {:?}", gamepad.name(), gamepad.power_info());
88        }
89    }
90
91    pub fn tick(&mut self, gamepads: &mut Gamepads, queue: &mut Messages<GamepadMessage>) {
92        while let Some(event) = self.gilrs.next_event() {
93            trace!(event=?event, "gilrs gamepad event");
94            match event.event {
95                EventType::ButtonPressed(_, _) => {}
96                EventType::ButtonRepeated(_, _) => {}
97                EventType::ButtonReleased(_, _) => {}
98                EventType::ButtonChanged(gilrs_button, button_value, _) => {
99                    if let Some(real_button) = convert_button(&gilrs_button) {
100                        gamepads.set_button(event.id.into(), real_button, button_value, queue);
101                    }
102                }
103                EventType::AxisChanged(gilrs_axis, axis_value, _) => {
104                    if let Some(real_axis) = convert_axis(&gilrs_axis) {
105                        gamepads.set_axis(event.id.into(), real_axis, axis_value, queue);
106                    }
107                }
108                EventType::Connected => {
109                    let gamepad = self.gilrs.gamepad(event.id);
110                    gamepads.connected(event.id.into(), gamepad.name(), queue);
111                }
112                EventType::Disconnected => {
113                    gamepads.disconnected(event.id.into(), queue);
114                }
115                EventType::Dropped => {}
116                EventType::ForceFeedbackEffectCompleted => {}
117                _ => {}
118            }
119        }
120    }
121}
122
123fn check_gamepads(
124    mut gilrs: LoReM<GamepadGilrs>,
125    mut gamepads: ReM<Gamepads>,
126    mut queue: MsgM<GamepadMessage>,
127) {
128    gilrs.tick(&mut gamepads, &mut queue);
129}
130
131pub struct GamepadGilrsPlugin;
132
133impl Plugin for GamepadGilrsPlugin {
134    fn build(&self, app: &mut App) {
135        app.insert_local_resource(GamepadGilrs::new().expect("Failed to initialize GamepadGilrs"));
136
137        app.add_system(First, check_gamepads);
138    }
139}