Skip to main content

gcrecomp_runtime/input/
switch_pro.rs

1// Nintendo Switch Pro Controller support
2use anyhow::Result;
3use hidapi::HidApi;
4use std::time::Duration;
5
6pub struct SwitchProController {
7    device: Option<hidapi::HidDevice>,
8    connected: bool,
9}
10
11impl SwitchProController {
12    pub fn new() -> Result<Self> {
13        let api = HidApi::new()?;
14
15        // Switch Pro Controller USB vendor/product IDs
16        const NINTENDO_VENDOR_ID: u16 = 0x057e;
17        const PRO_CONTROLLER_PRODUCT_ID: u16 = 0x2009;
18
19        let device = api.open(NINTENDO_VENDOR_ID, PRO_CONTROLLER_PRODUCT_ID).ok();
20        let connected = device.is_some();
21
22        Ok(Self {
23            device,
24            connected,
25        })
26    }
27
28    pub fn is_connected(&self) -> bool {
29        self.connected
30    }
31
32    pub fn read_input(&mut self) -> Result<SwitchProInput> {
33        if let Some(ref device) = self.device {
34            let mut buf = [0u8; 64];
35            let len = device.read_timeout(&mut buf, 16)?;
36
37            if len > 0 {
38                return Self::parse_input(&buf[..len]);
39            }
40        }
41
42        Ok(SwitchProInput::default())
43    }
44
45    fn parse_input(data: &[u8]) -> Result<SwitchProInput> {
46        if data.len() < 10 {
47            return Ok(SwitchProInput::default());
48        }
49
50        let buttons = u16::from_le_bytes([data[3], data[4]]);
51
52        Ok(SwitchProInput {
53            a: (buttons & 0x0001) != 0,
54            b: (buttons & 0x0002) != 0,
55            x: (buttons & 0x0004) != 0,
56            y: (buttons & 0x0008) != 0,
57            minus: (buttons & 0x0010) != 0,
58            plus: (buttons & 0x0020) != 0,
59            l: (buttons & 0x0040) != 0,
60            r: (buttons & 0x0080) != 0,
61            zl: (buttons & 0x0100) != 0,
62            zr: (buttons & 0x0200) != 0,
63            left_stick_x: data[6] as f32 / 128.0 - 1.0,
64            left_stick_y: data[7] as f32 / 128.0 - 1.0,
65            right_stick_x: data[8] as f32 / 128.0 - 1.0,
66            right_stick_y: data[9] as f32 / 128.0 - 1.0,
67        })
68    }
69
70    pub fn set_rumble(&mut self, low_freq: u8, high_freq: u8) -> Result<()> {
71        if let Some(ref device) = self.device {
72            let mut buf = [0u8; 10];
73            buf[0] = 0x10; // Rumble command
74            buf[1] = 0x80;
75            buf[3] = low_freq;
76            buf[4] = high_freq;
77            device.write(&buf)?;
78        }
79        Ok(())
80    }
81}
82
83#[derive(Debug, Clone, Default)]
84pub struct SwitchProInput {
85    pub a: bool,
86    pub b: bool,
87    pub x: bool,
88    pub y: bool,
89    pub minus: bool,
90    pub plus: bool,
91    pub l: bool,
92    pub r: bool,
93    pub zl: bool,
94    pub zr: bool,
95    pub left_stick_x: f32,
96    pub left_stick_y: f32,
97    pub right_stick_x: f32,
98    pub right_stick_y: f32,
99}