1#[macro_use]
2extern crate log;
3#[macro_use]
4extern crate quick_error;
5
6use std::convert::TryFrom;
7use std::fmt;
8use std::fmt::Display;
9use std::hash::{Hash, Hasher};
10use std::ops::Deref;
11use std::sync::{mpsc, Arc, Mutex, MutexGuard};
12
13use hex::FromHexError;
14use quick_error::ResultExt;
15use rusb::{Context, Device, Hotplug, HotplugBuilder, Registration, UsbContext};
16
17use crate::config::Config;
18use crate::drivers::g203_lightsync::G203LightsyncDriver;
19use crate::drivers::g213::G213Driver;
20
21pub mod config;
22pub mod drivers;
23pub mod usb_ext;
24
25const LOGITECH_USB_VENDOR_ID: u16 = 0x046d;
26
27#[derive(Clone, Debug)]
29pub struct RgbColor(pub u8, pub u8, pub u8);
30
31impl RgbColor {
32 #[inline]
33 pub fn red(&self) -> u8 {
34 self.0
35 }
36
37 #[inline]
38 pub fn green(&self) -> u8 {
39 self.1
40 }
41
42 #[inline]
43 pub fn blue(&self) -> u8 {
44 self.2
45 }
46
47 pub fn from_hex(rgb_hex: &str) -> Result<Self, FromHexError> {
48 let mut bytes = [0u8; 3];
49 hex::decode_to_slice(rgb_hex, &mut bytes as &mut [u8])?;
50 Ok(RgbColor(bytes[0], bytes[1], bytes[2]))
51 }
52
53 pub fn to_hex(&self) -> String {
54 hex::encode([self.0, self.1, self.2])
55 }
56
57 #[inline]
58 pub fn to_int(&self) -> u32 {
59 ((self.0 as u32) << 16) | ((self.1 as u32) << 8) | (self.2 as u32)
60 }
61}
62
63#[derive(PartialEq, Eq, Debug, Copy, Clone)]
64pub enum Direction {
65 LeftToRight = 1,
66 RightToLeft = 6,
67 CenterToEdge = 3,
68 EdgeToCenter = 8,
69}
70
71impl TryFrom<&str> for Direction {
72 type Error = ();
73
74 fn try_from(value: &str) -> Result<Self, Self::Error> {
75 match value {
76 "left-to-right" => Ok(Direction::LeftToRight),
77 "right-to-left" => Ok(Direction::RightToLeft),
78 "center-to-edge" => Ok(Direction::CenterToEdge),
79 "edge-to-center" => Ok(Direction::EdgeToCenter),
80 _ => Err(()),
81 }
82 }
83}
84
85#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)]
87pub struct Speed(u16);
88
89impl From<u16> for Speed {
90 #[inline]
91 fn from(input: u16) -> Self {
92 Speed(input)
93 }
94}
95
96#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)]
98pub struct Dpi(u16);
99
100impl From<u16> for Dpi {
101 #[inline]
102 fn from(input: u16) -> Self {
103 Dpi(input)
104 }
105}
106
107#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)]
109pub struct Brightness(u8);
110
111impl Default for Brightness {
112 #[inline]
113 fn default() -> Self {
114 Brightness(100)
115 }
116}
117
118impl TryFrom<u8> for Brightness {
119 type Error = CommandError;
120
121 fn try_from(value: u8) -> Result<Self, Self::Error> {
122 if value <= 100 {
123 Ok(Brightness(value))
124 } else {
125 Err(CommandError::InvalidArgument(
126 "brightness",
127 format!("{} < {}", value, 100),
128 ))
129 }
130 }
131}
132
133#[derive(Clone, Debug)]
135pub enum Command {
136 ColorSector(RgbColor, Option<u8>),
137 Breathe(RgbColor, Option<Speed>, Option<Brightness>),
138 Cycle(Option<Speed>, Option<Brightness>),
139 Wave(Direction, Option<Speed>, Option<Brightness>),
140 Blend(Option<Speed>, Option<Brightness>),
141 StartEffect(bool),
142 Dpi(Dpi),
143}
144
145pub type UsbDevice = Device<Context>;
146
147pub enum GDeviceManagerEvent {
148 DevicePluggedIn(UsbDevice),
149 DevicePluggedOut(UsbDevice),
150 Shutdown,
151}
152
153#[derive(Debug)]
154pub enum DeviceType {
155 Keyboard,
156 Mouse,
157}
158
159pub struct GModelId(String);
160
161pub trait GDeviceDriver: Send {
163 fn get_model(&self) -> GDeviceModelRef;
164 fn open_device(&self, device: &UsbDevice) -> Option<Box<dyn GDevice>>;
165}
166
167pub type GDeviceDriverRef = Box<dyn GDeviceDriver>;
168
169pub trait GDeviceModel: Send + Sync {
173 fn get_sectors(&self) -> u8;
174
175 fn get_default_color(&self) -> RgbColor;
176
177 fn get_name(&self) -> &'static str;
178
179 fn get_type(&self) -> DeviceType;
180
181 fn usb_product_id(&self) -> u16;
182}
183
184pub type GDeviceModelRef = Arc<dyn GDeviceModel>;
185
186pub trait GDevice: Display + Send {
190 fn dev(&self) -> &UsbDevice;
192 fn serial_number(&self) -> &str;
194 fn get_model(&self) -> GDeviceModelRef;
196 fn send_command(&mut self, cmd: Command) -> CommandResult<()>;
198}
199
200pub type GDeviceRef = Box<dyn GDevice>;
201
202pub struct GDeviceInfo {
203 pub model: &'static str,
204 pub serial: String,
205}
206
207quick_error! {
208 #[derive(Debug)]
209 pub enum CommandError {
210 Usb(context: String, err: rusb::Error) {
211 display("USB error: {}: {}", context, err)
212 context(message: &'a str, err: rusb::Error)
213 -> (message.to_string(), err)
214 }
215 InvalidArgument(arg: &'static str, msg: String) {
216 display("Invalid argument {}: {}", arg, msg)
217 }
218 InvalidCommand {
219 display("Invalid command")
220 }
221 }
222}
223
224type CommandResult<T> = Result<T, CommandError>;
225
226impl PartialEq for Box<dyn GDeviceModel> {
227 fn eq(&self, other: &Self) -> bool {
228 self.get_name() == other.get_name()
229 }
230}
231
232impl Eq for Box<dyn GDeviceModel> {}
233
234impl Hash for Box<dyn GDeviceModel> {
235 fn hash<H: Hasher>(&self, state: &mut H) {
236 state.write(self.get_name().as_bytes())
237 }
238}
239
240struct GDeviceManagerState {
241 pub context: Context,
242 #[allow(dead_code)]
243 hotplug: Registration<Context>,
244 config: Config,
245 devices: Vec<GDeviceRef>,
246 drivers: Vec<GDeviceDriverRef>,
247}
248
249impl GDeviceManagerState {
250 pub fn new(tx: mpsc::SyncSender<GDeviceManagerEvent>) -> CommandResult<Self> {
251 let context = Context::new().context("creating USB context")?;
252 let config = Config::load();
253 Ok(Self {
254 devices: vec![],
255 config,
256 drivers: vec![
257 Box::<G213Driver>::default(),
258 Box::<G203LightsyncDriver>::default(),
259 ],
260 hotplug: HotplugBuilder::new()
261 .vendor_id(LOGITECH_USB_VENDOR_ID)
262 .register(&context, Box::new(HotPlugHandler { channel: tx }))
263 .context("registering hotplug callback")?,
264 context,
265 })
266 }
267
268 pub fn get_devices(&mut self) -> Vec<GDeviceInfo> {
269 self.devices
270 .iter()
271 .map(|dev| GDeviceInfo {
272 model: dev.get_model().get_name(),
273 serial: dev.serial_number().to_string(),
274 })
275 .collect()
276 }
277
278 pub fn get_drivers(&mut self) -> Vec<&'static str> {
279 self.drivers
280 .iter()
281 .map(|drv| drv.get_model().get_name())
282 .collect()
283 }
284
285 pub fn load_devices(&mut self) -> CommandResult<()> {
286 info!("Scan devices");
287 let usb_devices = self.context.devices().context("listing USB devices")?;
288 self.devices = usb_devices
289 .iter()
290 .filter_map(|device| self.try_open_device(&device))
291 .collect();
292 info!("Found {} device(s)", self.devices.len());
293 self.apply_config();
294 Ok(())
295 }
296
297 fn find_driver_for_device(&self, device: &Device<Context>) -> Option<&dyn GDeviceDriver> {
298 let descriptor = device.device_descriptor().unwrap();
299 if descriptor.vendor_id() == LOGITECH_USB_VENDOR_ID {
300 self.drivers
301 .iter()
302 .find(|driver| descriptor.product_id() == driver.get_model().usb_product_id())
303 .map(|driver| driver.deref())
304 } else {
305 None
306 }
307 }
308
309 fn try_open_device(&self, device: &UsbDevice) -> Option<Box<dyn GDevice>> {
310 if let Some(driver) = self.find_driver_for_device(device) {
311 info!("Found device {}", driver.get_model().get_name());
312 driver.open_device(device)
313 } else {
314 None
315 }
316 }
317
318 pub fn send_command(&mut self, cmd: Command) {
319 for device in &mut self.devices {
320 if let Err(err) = device.send_command(cmd.clone()) {
321 error!("Sending command failed for device: {:?}", err);
322 }
323
324 self.config.save_command(&*device.get_model(), cmd.clone())
325 }
326 }
327
328 fn apply_config(&mut self) {
329 for device in &mut self.devices {
330 Self::apply_device_config(device, &self.config);
331 }
332 }
333
334 fn apply_device_config(device: &mut GDeviceRef, config: &Config) {
335 info!("Setting config for {}", device.get_model().get_name());
336 for command in config.commands_for(&*device.get_model()) {
337 if let Err(err) = device.send_command(command.clone()) {
338 error!("Unable to send command to device {device}: {:?}", err);
339 }
340 }
341 }
342
343 pub fn refresh(&mut self) {
344 info!("Refreshing");
345 self.config = Config::load();
346 self.apply_config();
347 }
348
349 pub fn on_new_usb_device(&mut self, dev: UsbDevice) {
350 if let Some(mut gdev) = self.try_open_device(&dev) {
351 if self.devices.iter().any(|existing| existing.dev() == &dev) {
352 warn!("Plugged in device {} already exists", gdev)
353 } else {
354 info!("Device plugged in: {}", gdev);
355 Self::apply_device_config(&mut gdev, &self.config);
356 self.devices.push(gdev);
357 }
358 }
359 }
360
361 pub fn on_lost_usb_device(&mut self, dev: UsbDevice) {
362 self.devices.retain(|existing| {
363 if existing.dev() == &dev {
364 info!("Device unplugged: {}", existing);
365 false
366 } else {
367 true
368 }
369 });
370 }
371}
372
373pub struct GDeviceManager {
374 state: Mutex<GDeviceManagerState>,
375 rx: Mutex<mpsc::Receiver<GDeviceManagerEvent>>,
376 tx: mpsc::SyncSender<GDeviceManagerEvent>,
377}
378
379impl GDeviceManager {
380 pub fn try_new() -> CommandResult<Self> {
382 let (tx, rx) = mpsc::sync_channel(1024);
383 let state = GDeviceManagerState::new(tx.clone())?;
384 Ok(Self {
385 tx,
386 rx: Mutex::new(rx),
387 state: Mutex::new(state),
388 })
389 }
390
391 pub fn context(&self) -> Context {
392 self.state().context.clone()
393 }
394
395 pub fn channel(&self) -> &mpsc::SyncSender<GDeviceManagerEvent> {
396 &self.tx
397 }
398
399 pub fn load_devices(&self) -> CommandResult<()> {
400 self.state().load_devices()
401 }
402
403 pub fn list(&self) -> Vec<GDeviceInfo> {
405 self.state().get_devices()
406 }
407
408 pub fn list_drivers(&self) -> Vec<&'static str> {
410 self.state().get_drivers()
411 }
412
413 pub fn send_command(&self, cmd: Command) {
415 self.state().send_command(cmd)
416 }
417
418 pub fn apply_config(&mut self) {
420 self.state().apply_config()
421 }
422
423 pub fn refresh(&self) {
425 self.state().refresh()
426 }
427
428 pub fn run(&self) {
429 while let Ok(msg) = self.rx.lock().unwrap().recv() {
430 match msg {
431 GDeviceManagerEvent::DevicePluggedIn(dev) => self.state().on_new_usb_device(dev),
432 GDeviceManagerEvent::DevicePluggedOut(dev) => self.state().on_lost_usb_device(dev),
433 GDeviceManagerEvent::Shutdown => break,
434 }
435 }
436 }
437
438 fn state(&self) -> MutexGuard<'_, GDeviceManagerState> {
439 self.state.lock().unwrap()
440 }
441}
442
443impl fmt::Debug for GDeviceManager {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445 f.write_str("GDeviceManager")
446 }
447}
448
449struct HotPlugHandler {
450 channel: mpsc::SyncSender<GDeviceManagerEvent>,
451}
452
453impl HotPlugHandler {
454 fn send(&self, cmd: GDeviceManagerEvent) {
455 self.channel.send(cmd).expect("channel should be alive");
456 }
457}
458
459impl Hotplug<Context> for HotPlugHandler {
460 fn device_arrived(&mut self, device: UsbDevice) {
461 self.send(GDeviceManagerEvent::DevicePluggedIn(device));
462 }
463
464 fn device_left(&mut self, device: UsbDevice) {
465 self.send(GDeviceManagerEvent::DevicePluggedOut(device));
466 }
467}