Pre-bevy minigame commit

This commit is contained in:
2026-02-10 12:32:01 +01:00
commit 498d0b1ba8
14 changed files with 1316 additions and 0 deletions

241
driver/src/lib.rs Normal file
View File

@@ -0,0 +1,241 @@
pub mod joystick;
use std::{
io::{Read, Write},
sync::{Arc, RwLock},
time::Duration,
};
use embedded_graphics_core::{
pixelcolor::BinaryColor,
prelude::{Dimensions, DrawTarget, Point, Size},
primitives::Rectangle,
};
use nusb::{
Interface, MaybeFuture,
transfer::{ControlOut, ControlType, In, Interrupt, Out, Recipient},
};
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;
#[derive(Clone)]
pub struct G13 {
interface: Interface,
img_buffer: [u8; G13_LCD_BUF_SIZE as usize + 8],
state: Arc<RwLock<State>>,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct State {
pub x: i32,
pub y: i32,
pub buttons: Buttons,
pub previous_buttons: Buttons,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Buttons {
pub action: bool,
pub screen1: bool,
pub screen2: bool,
pub screen3: bool,
pub screen4: bool,
pub light: bool,
pub m1: bool,
pub m2: bool,
pub m3: bool,
pub mr: bool,
pub g1: bool,
pub g2: bool,
pub g3: bool,
pub g4: bool,
pub g5: bool,
pub g6: bool,
pub g7: bool,
pub g8: bool,
pub g9: bool,
pub g10: bool,
pub g11: bool,
pub g12: bool,
pub g13: bool,
pub g14: bool,
pub g15: bool,
pub g16: bool,
pub g17: bool,
pub g18: bool,
pub g19: bool,
pub g20: bool,
pub g21: bool,
pub g22: bool,
pub stick1: bool,
pub stick2: bool,
pub stick3: bool,
}
impl G13 {
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
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 img_buffer = [0u8; G13_LCD_BUF_SIZE as usize + 8];
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 = interface.endpoint::<Interrupt, Out>(0x02)?.writer(64);
w.write_all(&buffer)?;
w.flush()?;
Ok(Self {
interface,
img_buffer,
state: Arc::new(RwLock::new(Default::default())),
})
}
pub fn state(&self) -> State {
*self.state.read().expect("Poisoned")
}
pub fn read(&self) -> Result<State, Box<dyn std::error::Error>> {
let mut state = self.state();
state.previous_buttons = state.buttons;
let mut reader = self.interface.endpoint::<Interrupt, In>(0x81)?.reader(8);
let mut buf = [0; 8];
reader.read_exact(&mut buf)?;
state.x = (((buf[1] as f64) / 256.0 * 1024.0) - 512.0) as i32;
state.y = (((buf[2] as f64) / 256.0 * 1024.0) - 512.0) as i32;
state.buttons.action = buf[6] & 0b00000001 != 0;
state.buttons.screen1 = buf[6] & 0b00000010 != 0;
state.buttons.screen2 = buf[6] & 0b00000100 != 0;
state.buttons.screen3 = buf[6] & 0b00001000 != 0;
state.buttons.screen4 = buf[6] & 0b00010000 != 0;
state.buttons.light = buf[7] & 0b0100000 != 0;
state.buttons.m1 = buf[6] & 0b00100000 != 0;
state.buttons.m2 = buf[6] & 0b01000000 != 0;
state.buttons.m3 = buf[6] & 0b10000000 != 0;
state.buttons.mr = buf[7] & 0b00000001 != 0;
state.buttons.g1 = buf[3] & 0b00000001 != 0;
state.buttons.g2 = buf[3] & 0b00000010 != 0;
state.buttons.g3 = buf[3] & 0b00000100 != 0;
state.buttons.g4 = buf[3] & 0b00001000 != 0;
state.buttons.g5 = buf[3] & 0b00010000 != 0;
state.buttons.g6 = buf[3] & 0b00100000 != 0;
state.buttons.g7 = buf[3] & 0b01000000 != 0;
state.buttons.g8 = buf[3] & 0b10000000 != 0;
state.buttons.g9 = buf[4] & 0b00000001 != 0;
state.buttons.g10 = buf[4] & 0b00000010 != 0;
state.buttons.g11 = buf[4] & 0b00000100 != 0;
state.buttons.g12 = buf[4] & 0b00001000 != 0;
state.buttons.g13 = buf[4] & 0b00010000 != 0;
state.buttons.g14 = buf[4] & 0b00100000 != 0;
state.buttons.g15 = buf[4] & 0b01000000 != 0;
state.buttons.g16 = buf[4] & 0b10000000 != 0;
state.buttons.g17 = buf[5] & 0b00000001 != 0;
state.buttons.g18 = buf[5] & 0b00000010 != 0;
state.buttons.g19 = buf[5] & 0b00000100 != 0;
state.buttons.g20 = buf[5] & 0b00001000 != 0;
state.buttons.g21 = buf[5] & 0b00010000 != 0;
state.buttons.g22 = buf[5] & 0b00100000 != 0;
state.buttons.stick1 = buf[7] & 0b00000010 != 0;
state.buttons.stick2 = buf[7] & 0b00000100 != 0;
state.buttons.stick3 = buf[7] & 0b00001000 != 0;
*self.state.write().expect("Poisoned") = state;
Ok(state)
}
pub fn set_lcd_color(&self, r: u8, g: u8, b: u8) -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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(&self.img_buffer);
let mut w = self.interface.endpoint::<Interrupt, Out>(0x02)?.writer(64);
w.write_all(&buffer)?;
w.flush()?;
Ok(())
}
}
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<dyn std::error::Error>;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = embedded_graphics_core::Pixel<Self::Color>>,
{
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() {
self.img_buffer[offset as usize] |= mask;
} else {
self.img_buffer[offset as usize] &= !mask;
}
}
Ok(())
}
}