Skip to main content

gcrecomp_runtime/input/backends/
gilrs.rs

1// Gilrs backend for cross-platform gamepad support
2use crate::input::backends::{Backend, ControllerInfo, ControllerType, HatState, RawInput};
3use anyhow::Result;
4use gilrs::{Axis, Gilrs};
5
6pub struct GilrsBackend {
7    gilrs: Gilrs,
8}
9
10impl GilrsBackend {
11    pub fn new() -> Result<Self> {
12        let gilrs =
13            Gilrs::new().map_err(|e| anyhow::anyhow!("Failed to initialize gilrs: {}", e))?;
14
15        Ok(Self { gilrs })
16    }
17}
18
19impl Backend for GilrsBackend {
20    fn update(&mut self) -> Result<()> {
21        // Process events
22        while self.gilrs.next_event().is_some() {}
23        Ok(())
24    }
25
26    fn enumerate_controllers(&mut self) -> Result<Vec<ControllerInfo>> {
27        let mut controllers = Vec::new();
28
29        for (id, gamepad) in self.gilrs.gamepads() {
30            let name = gamepad.name();
31            let controller_type = detect_controller_type(name);
32
33            controllers.push(ControllerInfo {
34                id: id.into(),
35                name: name.to_string(),
36                controller_type,
37                button_count: 16, // Standard gamepad button count
38                axis_count: 6,    // Standard gamepad axis count
39            });
40        }
41
42        Ok(controllers)
43    }
44
45    fn get_input(&self, controller_id: usize) -> Result<RawInput> {
46        // Find gamepad by iterating gamepads (gilrs 0.10 API)
47        let gamepad = self.gilrs.gamepads()
48            .find(|(id, _)| usize::from(*id) == controller_id)
49            .map(|(_, g)| g);
50
51        if let Some(gamepad) = gamepad {
52            let mut buttons = Vec::new();
53            let mut axes = Vec::new();
54            let mut triggers = Vec::new();
55
56            // Read standard buttons explicitly (gilrs 0.10 API)
57            use gilrs::Button;
58            buttons.push(gamepad.is_pressed(Button::South));
59            buttons.push(gamepad.is_pressed(Button::East));
60            buttons.push(gamepad.is_pressed(Button::West));
61            buttons.push(gamepad.is_pressed(Button::North));
62            buttons.push(gamepad.is_pressed(Button::LeftTrigger));
63            buttons.push(gamepad.is_pressed(Button::RightTrigger));
64            buttons.push(gamepad.is_pressed(Button::LeftTrigger2));
65            buttons.push(gamepad.is_pressed(Button::RightTrigger2));
66            buttons.push(gamepad.is_pressed(Button::Select));
67            buttons.push(gamepad.is_pressed(Button::Start));
68            buttons.push(gamepad.is_pressed(Button::Mode));
69            buttons.push(gamepad.is_pressed(Button::LeftThumb));
70            buttons.push(gamepad.is_pressed(Button::RightThumb));
71            buttons.push(gamepad.is_pressed(Button::DPadUp));
72            buttons.push(gamepad.is_pressed(Button::DPadDown));
73            buttons.push(gamepad.is_pressed(Button::DPadLeft));
74
75            // Read axes explicitly
76            axes.push(gamepad.value(Axis::LeftStickX));
77            axes.push(gamepad.value(Axis::LeftStickY));
78            axes.push(gamepad.value(Axis::RightStickX));
79            axes.push(gamepad.value(Axis::RightStickY));
80
81            // Read triggers
82            let left_trigger = gamepad.value(Axis::LeftZ);
83            let right_trigger = gamepad.value(Axis::RightZ);
84            triggers.push((left_trigger + 1.0) / 2.0);  // Normalize to 0-1
85            triggers.push((right_trigger + 1.0) / 2.0); // Normalize to 0-1
86
87            Ok(RawInput {
88                buttons,
89                axes,
90                triggers,
91                hat: None,
92            })
93        } else {
94            anyhow::bail!("Controller not found: {}", controller_id);
95        }
96    }
97}
98
99fn detect_controller_type(name: &str) -> ControllerType {
100    let name_lower = name.to_lowercase();
101    if name_lower.contains("xbox") {
102        ControllerType::Xbox
103    } else if name_lower.contains("playstation") || name_lower.contains("dualshock") {
104        ControllerType::PlayStation
105    } else if name_lower.contains("switch") || name_lower.contains("pro controller") {
106        ControllerType::SwitchPro
107    } else {
108        ControllerType::Generic
109    }
110}