g203_lib/
lib.rs

1use std::time::Duration;
2
3use rusb::{DeviceHandle, GlobalContext};
4
5const INTERFACE_ID: u8 = 0x01;
6const CONFIGURATION_ID: u8 = 0;
7const VENDOR_ID: u16 = 0x046D;
8const PRODUCT_ID: u16 = 0xC09D;
9
10// Direction enum to represent the direction of the wave effect
11#[derive(Clone)]
12pub enum Direction {
13    Left = 0x01,
14    Right = 0x06,
15}
16
17#[derive(Debug)]
18pub struct Controller {
19    inner: DeviceHandle<GlobalContext>,
20    timeout: Duration,
21}
22
23impl Controller {
24    pub fn new_with_timeout(timeout: Duration) -> rusb::Result<Self> {
25        let handle = rusb::open_device_with_vid_pid(VENDOR_ID, PRODUCT_ID);
26        match handle {
27            Some(handle) => {
28                handle.set_active_configuration(CONFIGURATION_ID)?;
29                Ok(Self {
30                    inner: handle,
31                    timeout,
32                })
33            }
34            None => Err(rusb::Error::NoDevice),
35        }
36    }
37
38    pub fn new() -> rusb::Result<Self> {
39        Self::new_with_timeout(Duration::from_secs(2))
40    }
41
42    // Takes an array of three 8-bit unsigned integers representing RGB color values.
43    // Returns a Result type from the rusb crate, which will be Ok(()) if the command was successful, or an Err containing the error if not.
44    pub fn set_solid(&self, rgb: [u8; 3]) -> rusb::Result<()> {
45        let [red, green, blue] = rgb;
46        self.command(
47            &[
48                0x11, 0xff, 0x0e, 0x1b, 0x00, 0x01, red, green, blue, 0x00, 0x00, 0x00, 0x00, 0x00,
49                0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
50            ],
51            true,
52        )
53    }
54
55    pub fn set_breathe(&self, rgb: [u8; 3], rate: u16, brightness: u8) -> rusb::Result<()> {
56        let [red, green, blue] = rgb;
57        let rate_bytes = rate.to_be_bytes();
58        self.command(
59            &[
60                0x11,
61                0xff,
62                0x0e,
63                0x1b,
64                0x00,
65                0x04,
66                red,
67                green,
68                blue,
69                rate_bytes[0],
70                rate_bytes[1],
71                0x00,
72                brightness,
73                0x00,
74                0x00,
75                0x00,
76                0x01,
77                0x00,
78                0x00,
79                0x00,
80            ],
81            false,
82        )
83    }
84
85    pub fn set_cycle(&self, rate: u16, brightness: u8) -> rusb::Result<()> {
86        let rate_bytes = rate.to_be_bytes();
87        self.command(
88            &[
89                0x11,
90                0xff,
91                0x0e,
92                0x1b,
93                0x00,
94                0x02,
95                0x00,
96                0x00,
97                0x00,
98                0x00,
99                0x00,
100                rate_bytes[0],
101                rate_bytes[1],
102                brightness,
103                0x00,
104                0x00,
105                0x01,
106                0x00,
107                0x00,
108                0x00,
109            ],
110            true,
111        )
112    }
113
114    pub fn set_triple(&self, colors: [[u8; 3]; 3]) -> rusb::Result<()> {
115        self.command(
116            &[
117                0x11,
118                0xff,
119                0x12,
120                0x1b,
121                0x01,
122                colors[0][0],
123                colors[0][1],
124                colors[0][2],
125                0x02,
126                colors[1][0],
127                colors[1][1],
128                colors[1][2],
129                0x03,
130                colors[2][0],
131                colors[2][1],
132                colors[2][2],
133                0x00,
134                0x00,
135                0x00,
136                0x00,
137            ],
138            false,
139        )
140    }
141
142    pub fn set_wave(&self, rate: u16, brightness: u8, direction: Direction) -> rusb::Result<()> {
143        let rate_bytes = rate.to_be_bytes();
144        self.command(
145            &[
146                0x11,
147                0xff,
148                0x0e,
149                0x1b,
150                0x00,
151                0x03,
152                0x00,
153                0x00,
154                0x00,
155                0x00,
156                0x00,
157                0x00,
158                rate_bytes[0],
159                direction as u8,
160                brightness,
161                rate_bytes[1],
162                0x01,
163                0x00,
164                0x00,
165                0x00,
166            ],
167            true,
168        )
169    }
170
171    pub fn set_blend(&self, rate: u16, brightness: u8) -> rusb::Result<()> {
172        let rate_bytes = rate.to_be_bytes();
173        self.command(
174            &[
175                0x11,
176                0xff,
177                0x0e,
178                0x1b,
179                0x00,
180                0x06,
181                0x00,
182                0x00,
183                0x00,
184                0x00,
185                0x00,
186                0x00,
187                rate_bytes[0],
188                rate_bytes[1],
189                brightness,
190                0x00,
191                0x01,
192                0x00,
193                0x00,
194                0x00,
195            ],
196            true,
197        )
198    }
199
200    // This function is called before sending a command to the device.
201    // It detaches the kernel driver from the interface and claims the interface for the program.
202    // This is necessary to ensure that the program has exclusive access to the device.
203    fn command_prologue(&self) -> rusb::Result<()> {
204        // Detach the kernel driver from the interface.
205        // This allows the program to have exclusive access to the device.
206        self.inner.detach_kernel_driver(INTERFACE_ID)?;
207        // Claim the interface.
208        // This tells the operating system that the program is now in control of the device.
209        self.inner.claim_interface(INTERFACE_ID)?;
210        Ok(())
211    }
212
213    // This function is called after a command has been sent to the device.
214    // It releases the interface and reattaches the kernel driver.
215    // This is necessary to allow other programs to access the device.
216    fn command_epilogue(&self) -> rusb::Result<()> {
217        // Release the interface.
218        // This tells the operating system that the program is no longer in control of the device.
219        self.inner.release_interface(INTERFACE_ID)?;
220        // Reattach the kernel driver to the interface.
221        // This allows other programs to access the device.
222        self.inner.attach_kernel_driver(INTERFACE_ID)?;
223        Ok(())
224    }
225
226    pub fn command(&self, data: &[u8], disable_ls_memory: bool) -> rusb::Result<()> {
227        // Call the command prologue function to prepare the device for the command.
228        self.command_prologue()?;
229
230        // If the disable_ls_memory flag is true, send a specific command to the device to disable LS memory.
231        if disable_ls_memory {
232            self.inner
233                .write_control(
234                    0x21,
235                    0x09,
236                    0x210,
237                    0x01,
238                    &[0x10, 0xff, 0x0e, 0x5b, 0x01, 0x03, 0x05],
239                    self.timeout,
240                )
241                .unwrap();
242        }
243
244        // Send the command data to the device.
245        self.inner
246            .write_control(0x21, 0x09, 0x211, 0x01, data, self.timeout)
247            .unwrap();
248
249        // Check if the first four bytes of the command data matches a specific sequence.
250        // If it does, send an additional command to the device in order to apply the command.
251        let is_triple_command = data[0..4] == [0x11, 0xff, 0x12, 0x1b];
252        if is_triple_command {
253            self.inner.write_control(
254                0x21,
255                0x09,
256                0x211,
257                0x01,
258                &[
259                    0x11, 0xff, 0x12, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
260                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
261                ],
262                self.timeout,
263            )?;
264        }
265
266        self.command_epilogue()?;
267        Ok(())
268    }
269}