diff --git a/Cargo.lock b/Cargo.lock index 8eb6d0a..ca64349 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2767,6 +2767,7 @@ dependencies = [ name = "g13-driver" version = "0.1.0" dependencies = [ + "crossbeam-channel", "embedded-graphics-core", "nusb", ] @@ -2802,12 +2803,14 @@ dependencies = [ name = "g13-os" version = "0.1.0" dependencies = [ + "crossbeam-channel", "ctrlc", "embedded-graphics", "embedded-graphics-simulator", "g13-driver", "mlua", "time", + "winit", ] [[package]] @@ -5836,6 +5839,7 @@ checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" dependencies = [ "dlib", "log", + "once_cell", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index be5fcaf..53db1dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2024" [workspace.dependencies] g13-driver.path = "./driver" +crossbeam-channel = "0.5.15" diff --git a/driver/Cargo.toml b/driver/Cargo.toml index 8a50fbb..f1236ae 100644 --- a/driver/Cargo.toml +++ b/driver/Cargo.toml @@ -6,3 +6,4 @@ edition.workspace = true [dependencies] embedded-graphics-core = "0.4.0" nusb = { version = "0.2.1", features = ["tokio"] } +crossbeam-channel.workspace = true diff --git a/driver/src/lib.rs b/driver/src/lib.rs index ffd6f2c..ba68629 100644 --- a/driver/src/lib.rs +++ b/driver/src/lib.rs @@ -7,6 +7,7 @@ use std::{ time::Duration, }; +use crossbeam_channel::{Receiver, bounded}; use embedded_graphics_core::{ pixelcolor::BinaryColor, prelude::{Dimensions, DrawTarget, Point, Size}, @@ -24,6 +25,10 @@ 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), @@ -85,14 +90,16 @@ where } } -#[derive(Clone)] pub struct G13 { - display: G13Display, - input: G13Input, + 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()); }; @@ -113,30 +120,43 @@ impl G13 { 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 { - input: G13Input::new(interface.clone()), - display: G13Display::new(interface), + interface, + img_buffer: Arc::new(RwLock::new(img_buffer)), + rx: Some(rx), }) } - pub fn split(self) -> (G13Display, G13Input) { - (self.display, self.input) + pub fn events(&mut self) -> Receiver { + self.rx.take().expect("events() can only be called once") } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum G13Event { - Axis(u8, u8), + Axis(f32, f32), Button(Button, (bool, bool)), // (old, new) } -#[derive(Clone)] pub struct G13Input { interface: Interface, buttons: HashMap, - axis: Vec2, - calibrated_axis: (Vec2, Vec2), // this needs to be saved/loaded - calibrating: bool, + axis: Vec2, } impl G13Input { @@ -145,25 +165,8 @@ impl G13Input { interface, buttons: HashMap::new(), axis: Default::default(), - calibrated_axis: (Vec2::new(127, 127), Vec2::new(127, 127)), - calibrating: false, } } - pub fn calibrate(mut self) -> Self { - self.calibrating = true; - self - } - - pub fn with_calibration_axis(mut self, min: Vec2, max: Vec2) -> Self { - self.calibrated_axis.0 = min; - self.calibrated_axis.1 = max; - self.calibrating = false; - self - } - - pub fn get_calibration_axis(&self) -> (Vec2, Vec2) { - self.calibrated_axis - } fn handle_button(&mut self, button: Button, value: bool) -> Option { if let Entry::Vacant(e) = self.buttons.entry(button) { @@ -197,60 +200,39 @@ impl Iterator for G13Input { for &(button, index, mask) in BUTTON_MAP { if let Some(event) = self.handle_button(button, buf[index] & mask != 0) { - self.calibrating = false; + // self.calibrating = false; return Some(Some(event)); } } let axis = Vec2::new(buf[1], buf[2]); - if self.axis == axis { + 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 = axis; + self.axis = input; - self.calibrated_axis.0.x = self.calibrated_axis.0.x.min(axis.x); - self.calibrated_axis.1.x = self.calibrated_axis.1.x.max(axis.x); - self.calibrated_axis.0.y = self.calibrated_axis.0.y.min(axis.y); - self.calibrated_axis.1.y = self.calibrated_axis.1.y.max(axis.y); - - let (xmin, xmax) = (self.calibrated_axis.0.x, self.calibrated_axis.1.x); - let (ymin, ymax) = (self.calibrated_axis.0.y, self.calibrated_axis.1.y); - - let x_output = map_range(self.axis.x, xmin, xmax); - let y_output = map_range(self.axis.y, ymin, ymax); - - Some(Some(G13Event::Axis(x_output as u8, y_output as u8))) + Some(Some(G13Event::Axis(input.x / RANGE, input.y / RANGE))) } } -fn map_range(v: u8, in_min: u8, in_max: u8) -> f64 { - if in_min == in_max { - return 0.0; +fn apply_deadzone(v: f32) -> f32 { + if v.abs() <= DEADZONE { + 0.0 + } else if v > 0.0 { + v - DEADZONE + } else { + v + DEADZONE } - 255.0 * (v as f64 - in_min as f64) / (in_max as f64 - in_min as f64) } -#[derive(Clone)] -pub struct G13Display { - interface: Interface, - img_buffer: Arc>, -} - -impl G13Display { - pub fn new(interface: Interface) -> Self { - 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); - - Self { - interface, - img_buffer: Arc::new(RwLock::new(img_buffer)), - } - } - +impl G13 { pub fn set_lcd_color(&self, r: u8, g: u8, b: u8) -> Result<(), Box> { self.interface .control_out( @@ -287,7 +269,7 @@ impl G13Display { } } -impl Dimensions for G13Display { +impl Dimensions for G13 { fn bounding_box(&self) -> Rectangle { Rectangle::new( Point::new(0, 0), @@ -296,7 +278,7 @@ impl Dimensions for G13Display { } } -impl DrawTarget for G13Display { +impl DrawTarget for G13 { type Color = BinaryColor; type Error = Box; diff --git a/g13-os/Cargo.toml b/g13-os/Cargo.toml index 8b4caf7..21935f3 100644 --- a/g13-os/Cargo.toml +++ b/g13-os/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] -# buoyant = "0.6.1" +crossbeam-channel.workspace = true ctrlc = "3.5" embedded-graphics = "0.8" g13-driver.workspace = true @@ -12,3 +12,4 @@ mlua = { version = "0.11.6", features = ["lua54", "async", "macros", "serde"] } time = { version = "0.3.47", features = ["formatting", "macros"] } embedded-graphics-simulator = "0.7.0" +winit = "0.30.12" diff --git a/g13-os/src/app/mod.rs b/g13-os/src/app/mod.rs new file mode 100644 index 0000000..17279c2 --- /dev/null +++ b/g13-os/src/app/mod.rs @@ -0,0 +1,34 @@ +pub(super) mod pages; +pub(super) mod widgets; + +use std::collections::VecDeque; + +use embedded_graphics::{pixelcolor::BinaryColor, prelude::DrawTarget}; +use g13_driver::G13Event; + +use super::app::widgets::Page; + +type Color = BinaryColor; + +pub struct App { + pages: VecDeque>, +} + +impl App { + pub fn new() -> Self { + Self { + pages: VecDeque::new(), + } + } + + pub fn update(&mut self, event: G13Event) { + println!("{:?}", event); + } + + pub fn draw(&mut self, display: &mut D) -> Result<(), Box> + where + D: DrawTarget, + { + Ok(()) + } +} diff --git a/g13-os/src/app/pages/mod.rs b/g13-os/src/app/pages/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/g13-os/src/app/widgets/mod.rs b/g13-os/src/app/widgets/mod.rs new file mode 100644 index 0000000..b6b17fa --- /dev/null +++ b/g13-os/src/app/widgets/mod.rs @@ -0,0 +1,2 @@ +pub trait Page: Widget {} +pub trait Widget {} diff --git a/g13-os/src/main.rs b/g13-os/src/main.rs index 18f2a46..e9e29f1 100644 --- a/g13-os/src/main.rs +++ b/g13-os/src/main.rs @@ -1,73 +1,40 @@ -// pub mod mouse; -// mod os; +mod app; -// use embedded_graphics::prelude::{Dimensions, DrawTarget}; -// use embedded_graphics::{pixelcolor::BinaryColor, prelude::Size}; -// use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window}; -// use mlua::{Error, ExternalResult, Lua}; -// use rsact_ui::prelude::*; -// use rsact_ui::{ -// col, -// event::simulator::simulator_single_encoder, -// render::{AntiAliasing, RendererOptions}, -// row, -// style::accent::AccentStyler, -// ui::UI, -// }; -// use std::fmt::Binary; -// use std::{ -// fs, -// sync::{ -// Arc, -// atomic::{AtomicBool, Ordering}, -// }, -// thread::sleep, -// time::{Duration, Instant}, -// }; - -// use crate::os::G13Os; - -use embedded_graphics::{ - Drawable, - pixelcolor::BinaryColor, - prelude::{DrawTarget, Point}, +use std::{ + thread::sleep, + time::{Duration, Instant}, }; -use g13_driver::{G13, G13Event}; + +use embedded_graphics::pixelcolor::BinaryColor; +use embedded_graphics::prelude::*; + +use g13_driver::G13; + +use crate::app::App; fn main() -> Result<(), Box> { - let g13 = G13::new()?; - let (mut display, input) = g13.split(); + let mut g13 = G13::new()?; - let character_style = embedded_graphics::mono_font::MonoTextStyle::new( - &embedded_graphics::mono_font::ascii::FONT_10X20, - embedded_graphics::pixelcolor::BinaryColor::On, - ); - let textstyle = embedded_graphics::text::TextStyleBuilder::new() - .alignment(embedded_graphics::text::Alignment::Left) - .baseline(embedded_graphics::text::Baseline::Top) - .build(); + let mut app = App::new(); - for event in input { - let Some(event) = event else { - continue; - }; + let events = g13.events(); - println!("{:?}", event); + loop { + let start = Instant::now(); + if let Ok(event) = events.try_recv() { + app.update(event); + } + g13.clear(BinaryColor::Off)?; - display.clear(BinaryColor::Off)?; - - if let G13Event::Axis(x, y) = event { - embedded_graphics::text::Text::with_text_style( - &format!("{}x{}", x, y), - Point::new(5, 5), - character_style, - textstyle, - ) - .draw(&mut display)?; + if let Err(e) = app.draw(&mut g13) { + eprintln!("{:?}", e); } - display.render()?; - } + g13.render()?; - Ok(()) + let elapsed = Instant::now() - start; + if elapsed.as_millis() < 33 { + sleep(Duration::from_millis(33) - (elapsed)); + } + } }