pub mod joystick; use std::{ collections::{HashMap, hash_map::Entry}, io::{Read, Write}, sync::{Arc, RwLock}, time::Duration, }; use crossbeam_channel::{Receiver, bounded}; use embedded_graphics_core::{ pixelcolor::BinaryColor, prelude::{Dimensions, DrawTarget, Point, Size}, primitives::Rectangle, }; use nusb::{ Interface, MaybeFuture, transfer::{ControlOut, ControlType, In, Interrupt, Out, Recipient}, }; use crate::joystick::Button; pub const G13_LCD_COLUMNS: i32 = 160; pub const G13_LCD_ROWS: i32 = 43; pub const G13_LCD_BYTES_PER_ROW: i32 = G13_LCD_COLUMNS / 8; pub const G13_LCD_BUF_SIZE: i32 = (G13_LCD_ROWS + 5) * G13_LCD_BYTES_PER_ROW; const DEADZONE: f32 = 10.0; const CENTER: f32 = 127.5; const RANGE: f32 = 117.5; const BUTTON_MAP: &[(Button, usize, u8)] = &[ // buf[6] (Button::Action, 6, 0b00000001), (Button::Screen1, 6, 0b00000010), (Button::Screen2, 6, 0b00000100), (Button::Screen3, 6, 0b00001000), (Button::Screen4, 6, 0b00010000), (Button::M1, 6, 0b00100000), (Button::M2, 6, 0b01000000), (Button::M3, 6, 0b10000000), // buf[7] (Button::Light, 7, 0b01000000), (Button::MR, 7, 0b00000001), (Button::Stick1, 7, 0b00000010), (Button::Stick2, 7, 0b00000100), (Button::Stick3, 7, 0b00001000), // buf[3] (Button::G1, 3, 0b00000001), (Button::G2, 3, 0b00000010), (Button::G3, 3, 0b00000100), (Button::G4, 3, 0b00001000), (Button::G5, 3, 0b00010000), (Button::G6, 3, 0b00100000), (Button::G7, 3, 0b01000000), (Button::G8, 3, 0b10000000), // buf[4] (Button::G9, 4, 0b00000001), (Button::G10, 4, 0b00000010), (Button::G11, 4, 0b00000100), (Button::G12, 4, 0b00001000), (Button::G13, 4, 0b00010000), (Button::G14, 4, 0b00100000), (Button::G15, 4, 0b01000000), (Button::G16, 4, 0b10000000), // buf[5] (Button::G17, 5, 0b00000001), (Button::G18, 5, 0b00000010), (Button::G19, 5, 0b00000100), (Button::G20, 5, 0b00001000), (Button::G21, 5, 0b00010000), (Button::G22, 5, 0b00100000), ]; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Vec2 where T: Copy, { pub x: T, pub y: T, } impl Vec2 where T: Copy, { pub fn new(x: T, y: T) -> Self { Self { x, y } } } pub struct G13 { rx: Option>, interface: Interface, img_buffer: Arc>, } impl G13 { pub fn new() -> Result> { let img_buffer = [0u8; G13_LCD_BUF_SIZE as usize + 8]; let Ok(mut devices) = nusb::list_devices().wait() else { return Err("No devices found".into()); }; let Some(device) = devices.find(|d| d.vendor_id() == 0x046d && d.product_id() == 0xc21c) else { return Err("No device found".into()); }; let Ok(device) = device.open().wait() else { return Err("Unable to open device".into()); }; let _ = device.detach_kernel_driver(0); let interface = match device.claim_interface(0).wait() { Ok(i) => i, Err(e) => return Err(format!("Unable to claim interface: {:#?}", e).into()), }; let input = G13Input::new(interface.clone()); let (tx, rx) = bounded(0); let _tx = tx.clone(); std::thread::spawn(move || { for event in input { let Some(event) = event else { continue; }; if _tx.send(event).is_err() { break; } } }); Ok(Self { interface, img_buffer: Arc::new(RwLock::new(img_buffer)), rx: Some(rx), }) } pub fn events(&mut self) -> Receiver { self.rx.take().expect("events() can only be called once") } } #[derive(Debug, Copy, Clone)] pub enum G13Event { Axis(f32, f32), Button(Button, (bool, bool)), // (old, new) } pub struct G13Input { interface: Interface, buttons: HashMap, axis: Vec2, } impl G13Input { pub fn new(interface: Interface) -> Self { Self { interface, buttons: HashMap::new(), axis: Default::default(), } } fn handle_button(&mut self, button: Button, value: bool) -> Option { if let Entry::Vacant(e) = self.buttons.entry(button) { e.insert(value); if value { return Some(G13Event::Button(button, (!value, value))); } } let prev = self.buttons[&button]; if self.buttons[&button] == value { return None; } self.buttons.insert(button, value); Some(G13Event::Button(button, (prev, value))) } } impl Iterator for G13Input { type Item = Option; fn next(&mut self) -> Option { let mut reader = self .interface .endpoint::(0x81) .ok()? .reader(8); let mut buf = [0; 8]; reader.read_exact(&mut buf).ok()?; for &(button, index, mask) in BUTTON_MAP { if let Some(event) = self.handle_button(button, buf[index] & mask != 0) { // self.calibrating = false; return Some(Some(event)); } } let axis = Vec2::new(buf[1], buf[2]); let mut input = Vec2::new(axis.x as f32 - CENTER, axis.y as f32 - CENTER); input.x = apply_deadzone(input.x); input.y = apply_deadzone(input.y); if self.axis == input { return Some(None); } self.axis = input; Some(Some(G13Event::Axis(input.x / RANGE, input.y / RANGE))) } } fn apply_deadzone(v: f32) -> f32 { if v.abs() <= DEADZONE { 0.0 } else if v > 0.0 { v - DEADZONE } else { v + DEADZONE } } impl G13 { pub fn set_lcd_color(&self, r: u8, g: u8, b: u8) -> Result<(), Box> { self.interface .control_out( ControlOut { control_type: ControlType::Class, recipient: Recipient::Interface, request: 9, value: 0x307, index: 0, data: &[5, r, g, b, 0], }, Duration::from_secs(2), ) .wait()?; Ok(()) } pub fn render(&mut self) -> Result<(), Box> { let img_buffer = self.img_buffer.read().expect("Poisoned"); let mut buffer = [0u8; G13_LCD_BUF_SIZE as usize + 32 + 8]; buffer[0] = 0x03; buffer[32..G13_LCD_BUF_SIZE as usize + 32 + 8].copy_from_slice(&(*img_buffer)); let mut w = self.interface.endpoint::(0x02)?.writer(64); w.write_all(&buffer)?; w.flush()?; Ok(()) } pub fn size(&self) -> Size { Size::new(G13_LCD_COLUMNS as u32, G13_LCD_ROWS as u32) } } impl Dimensions for G13 { fn bounding_box(&self) -> Rectangle { Rectangle::new( Point::new(0, 0), Size::new(G13_LCD_COLUMNS as u32, G13_LCD_ROWS as u32), ) } } impl DrawTarget for G13 { type Color = BinaryColor; type Error = Box; fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> where I: IntoIterator>, { let mut img_buffer = self.img_buffer.write().expect("Poisoned"); for p in pixels { if p.0.x < 0 || p.0.x > G13_LCD_COLUMNS - 1 || p.0.y < 0 || p.0.y > G13_LCD_ROWS - 1 { continue; } let offset = p.0.x + (p.0.y / 8) * (G13_LCD_BYTES_PER_ROW) * 8; let mask = 1 << (p.0.y.rem_euclid(8)); if p.1.is_on() { img_buffer[offset as usize] |= mask; } else { img_buffer[offset as usize] &= !mask; } } Ok(()) } }