gcrecomp_runtime/input/backends/
sdl2.rs1use crate::input::backends::{Backend, ControllerInfo, ControllerType, HatState, RawInput};
3use anyhow::Result;
4use sdl2::GameControllerSubsystem;
5use std::collections::HashMap;
6
7pub struct SDL2Backend {
8 sdl_context: sdl2::Sdl,
9 controller_subsystem: GameControllerSubsystem,
10 controllers: HashMap<usize, sdl2::controller::GameController>,
11 next_id: usize,
12}
13
14impl SDL2Backend {
15 pub fn new() -> Result<Self> {
16 let sdl_context = sdl2::init()
17 .map_err(|e| anyhow::anyhow!("Failed to initialize SDL2: {}", e))?;
18
19 let controller_subsystem = sdl_context
20 .game_controller()
21 .map_err(|e| anyhow::anyhow!("Failed to initialize SDL2 game controller subsystem: {}", e))?;
22
23 Ok(Self {
24 sdl_context,
25 controller_subsystem,
26 controllers: HashMap::new(),
27 next_id: 0,
28 })
29 }
30}
31
32impl Backend for SDL2Backend {
33 fn update(&mut self) -> Result<()> {
34 Ok(())
36 }
37
38 fn enumerate_controllers(&mut self) -> Result<Vec<ControllerInfo>> {
39 let mut controllers = Vec::new();
40 let num_joysticks = self
41 .controller_subsystem
42 .num_joysticks()
43 .map_err(|e| anyhow::anyhow!("Failed to get joystick count: {}", e))?;
44
45 for i in 0..num_joysticks {
46 if self.controller_subsystem.is_game_controller(i) {
47 if let Ok(name) = self.controller_subsystem.name_for_index(i) {
48 let controller_type = detect_controller_type(&name);
49 let id = i as usize;
50
51 if let Ok(controller) = self.controller_subsystem.open(i) {
53 self.controllers.insert(id, controller);
54 }
55
56 controllers.push(ControllerInfo {
57 id,
58 name: name.to_string(),
59 controller_type,
60 button_count: 16, axis_count: 6, });
63 }
64 }
65 }
66
67 Ok(controllers)
68 }
69
70 fn get_input(&self, controller_id: usize) -> Result<RawInput> {
71 if let Some(controller) = self.controllers.get(&controller_id) {
72 let mut buttons = Vec::new();
73 let mut axes = Vec::new();
74 let mut triggers = Vec::new();
75
76 use sdl2::controller::Button;
78 buttons.push(controller.button(Button::A));
79 buttons.push(controller.button(Button::B));
80 buttons.push(controller.button(Button::X));
81 buttons.push(controller.button(Button::Y));
82 buttons.push(controller.button(Button::Back));
83 buttons.push(controller.button(Button::Guide));
84 buttons.push(controller.button(Button::Start));
85 buttons.push(controller.button(Button::LeftStick));
86 buttons.push(controller.button(Button::RightStick));
87 buttons.push(controller.button(Button::LeftShoulder));
88 buttons.push(controller.button(Button::RightShoulder));
89 buttons.push(controller.button(Button::DPadUp));
90 buttons.push(controller.button(Button::DPadDown));
91 buttons.push(controller.button(Button::DPadLeft));
92 buttons.push(controller.button(Button::DPadRight));
93 buttons.push(false); use sdl2::controller::Axis;
97 axes.push(controller.axis(Axis::LeftX) as f32 / 32768.0);
98 axes.push(controller.axis(Axis::LeftY) as f32 / 32768.0);
99 axes.push(controller.axis(Axis::RightX) as f32 / 32768.0);
100 axes.push(controller.axis(Axis::RightY) as f32 / 32768.0);
101
102 triggers.push(controller.axis(Axis::TriggerLeft) as f32 / 32768.0);
104 triggers.push(controller.axis(Axis::TriggerRight) as f32 / 32768.0);
105
106 Ok(RawInput {
107 buttons,
108 axes,
109 triggers,
110 hat: None,
111 })
112 } else {
113 anyhow::bail!("Controller not found: {}", controller_id);
114 }
115 }
116}
117
118fn detect_controller_type(name: &str) -> ControllerType {
119 let name_lower = name.to_lowercase();
120 if name_lower.contains("xbox") || name_lower.contains("xinput") {
121 ControllerType::Xbox
122 } else if name_lower.contains("playstation")
123 || name_lower.contains("dualshock")
124 || name_lower.contains("dualsense")
125 {
126 ControllerType::PlayStation
127 } else if name_lower.contains("switch") || name_lower.contains("pro controller") {
128 ControllerType::SwitchPro
129 } else {
130 ControllerType::Generic
131 }
132}