Can i haz Structure?
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -2767,6 +2767,7 @@ dependencies = [
|
|||||||
name = "g13-driver"
|
name = "g13-driver"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
"embedded-graphics-core",
|
"embedded-graphics-core",
|
||||||
"nusb",
|
"nusb",
|
||||||
]
|
]
|
||||||
@@ -2802,12 +2803,14 @@ dependencies = [
|
|||||||
name = "g13-os"
|
name = "g13-os"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"embedded-graphics",
|
"embedded-graphics",
|
||||||
"embedded-graphics-simulator",
|
"embedded-graphics-simulator",
|
||||||
"g13-driver",
|
"g13-driver",
|
||||||
"mlua",
|
"mlua",
|
||||||
"time",
|
"time",
|
||||||
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5836,6 +5839,7 @@ checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dlib",
|
"dlib",
|
||||||
"log",
|
"log",
|
||||||
|
"once_cell",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ edition = "2024"
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
g13-driver.path = "./driver"
|
g13-driver.path = "./driver"
|
||||||
|
crossbeam-channel = "0.5.15"
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ edition.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-graphics-core = "0.4.0"
|
embedded-graphics-core = "0.4.0"
|
||||||
nusb = { version = "0.2.1", features = ["tokio"] }
|
nusb = { version = "0.2.1", features = ["tokio"] }
|
||||||
|
crossbeam-channel.workspace = true
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crossbeam_channel::{Receiver, bounded};
|
||||||
use embedded_graphics_core::{
|
use embedded_graphics_core::{
|
||||||
pixelcolor::BinaryColor,
|
pixelcolor::BinaryColor,
|
||||||
prelude::{Dimensions, DrawTarget, Point, Size},
|
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_BYTES_PER_ROW: i32 = G13_LCD_COLUMNS / 8;
|
||||||
pub const G13_LCD_BUF_SIZE: i32 = (G13_LCD_ROWS + 5) * G13_LCD_BYTES_PER_ROW;
|
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)] = &[
|
const BUTTON_MAP: &[(Button, usize, u8)] = &[
|
||||||
// buf[6]
|
// buf[6]
|
||||||
(Button::Action, 6, 0b00000001),
|
(Button::Action, 6, 0b00000001),
|
||||||
@@ -85,14 +90,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct G13 {
|
pub struct G13 {
|
||||||
display: G13Display,
|
rx: Option<Receiver<G13Event>>,
|
||||||
input: G13Input,
|
interface: Interface,
|
||||||
|
img_buffer: Arc<RwLock<[u8; G13_LCD_BUF_SIZE as usize + 8]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl G13 {
|
impl G13 {
|
||||||
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let img_buffer = [0u8; G13_LCD_BUF_SIZE as usize + 8];
|
||||||
|
|
||||||
let Ok(mut devices) = nusb::list_devices().wait() else {
|
let Ok(mut devices) = nusb::list_devices().wait() else {
|
||||||
return Err("No devices found".into());
|
return Err("No devices found".into());
|
||||||
};
|
};
|
||||||
@@ -113,30 +120,43 @@ impl G13 {
|
|||||||
Err(e) => return Err(format!("Unable to claim interface: {:#?}", e).into()),
|
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 {
|
Ok(Self {
|
||||||
input: G13Input::new(interface.clone()),
|
interface,
|
||||||
display: G13Display::new(interface),
|
img_buffer: Arc::new(RwLock::new(img_buffer)),
|
||||||
|
rx: Some(rx),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split(self) -> (G13Display, G13Input) {
|
pub fn events(&mut self) -> Receiver<G13Event> {
|
||||||
(self.display, self.input)
|
self.rx.take().expect("events() can only be called once")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum G13Event {
|
pub enum G13Event {
|
||||||
Axis(u8, u8),
|
Axis(f32, f32),
|
||||||
Button(Button, (bool, bool)), // (old, new)
|
Button(Button, (bool, bool)), // (old, new)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct G13Input {
|
pub struct G13Input {
|
||||||
interface: Interface,
|
interface: Interface,
|
||||||
buttons: HashMap<Button, bool>,
|
buttons: HashMap<Button, bool>,
|
||||||
axis: Vec2<u8>,
|
axis: Vec2<f32>,
|
||||||
calibrated_axis: (Vec2<u8>, Vec2<u8>), // this needs to be saved/loaded
|
|
||||||
calibrating: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl G13Input {
|
impl G13Input {
|
||||||
@@ -145,25 +165,8 @@ impl G13Input {
|
|||||||
interface,
|
interface,
|
||||||
buttons: HashMap::new(),
|
buttons: HashMap::new(),
|
||||||
axis: Default::default(),
|
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<u8>, max: Vec2<u8>) -> Self {
|
|
||||||
self.calibrated_axis.0 = min;
|
|
||||||
self.calibrated_axis.1 = max;
|
|
||||||
self.calibrating = false;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_calibration_axis(&self) -> (Vec2<u8>, Vec2<u8>) {
|
|
||||||
self.calibrated_axis
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_button(&mut self, button: Button, value: bool) -> Option<G13Event> {
|
fn handle_button(&mut self, button: Button, value: bool) -> Option<G13Event> {
|
||||||
if let Entry::Vacant(e) = self.buttons.entry(button) {
|
if let Entry::Vacant(e) = self.buttons.entry(button) {
|
||||||
@@ -197,60 +200,39 @@ impl Iterator for G13Input {
|
|||||||
|
|
||||||
for &(button, index, mask) in BUTTON_MAP {
|
for &(button, index, mask) in BUTTON_MAP {
|
||||||
if let Some(event) = self.handle_button(button, buf[index] & mask != 0) {
|
if let Some(event) = self.handle_button(button, buf[index] & mask != 0) {
|
||||||
self.calibrating = false;
|
// self.calibrating = false;
|
||||||
return Some(Some(event));
|
return Some(Some(event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let axis = Vec2::new(buf[1], buf[2]);
|
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);
|
return Some(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.axis = axis;
|
self.axis = input;
|
||||||
|
|
||||||
self.calibrated_axis.0.x = self.calibrated_axis.0.x.min(axis.x);
|
Some(Some(G13Event::Axis(input.x / RANGE, input.y / RANGE)))
|
||||||
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)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_range(v: u8, in_min: u8, in_max: u8) -> f64 {
|
fn apply_deadzone(v: f32) -> f32 {
|
||||||
if in_min == in_max {
|
if v.abs() <= DEADZONE {
|
||||||
return 0.0;
|
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)]
|
impl G13 {
|
||||||
pub struct G13Display {
|
|
||||||
interface: Interface,
|
|
||||||
img_buffer: Arc<RwLock<[u8; G13_LCD_BUF_SIZE as usize + 8]>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_lcd_color(&self, r: u8, g: u8, b: u8) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn set_lcd_color(&self, r: u8, g: u8, b: u8) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
self.interface
|
self.interface
|
||||||
.control_out(
|
.control_out(
|
||||||
@@ -287,7 +269,7 @@ impl G13Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dimensions for G13Display {
|
impl Dimensions for G13 {
|
||||||
fn bounding_box(&self) -> Rectangle {
|
fn bounding_box(&self) -> Rectangle {
|
||||||
Rectangle::new(
|
Rectangle::new(
|
||||||
Point::new(0, 0),
|
Point::new(0, 0),
|
||||||
@@ -296,7 +278,7 @@ impl Dimensions for G13Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawTarget for G13Display {
|
impl DrawTarget for G13 {
|
||||||
type Color = BinaryColor;
|
type Color = BinaryColor;
|
||||||
type Error = Box<dyn std::error::Error>;
|
type Error = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version.workspace = true
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# buoyant = "0.6.1"
|
crossbeam-channel.workspace = true
|
||||||
ctrlc = "3.5"
|
ctrlc = "3.5"
|
||||||
embedded-graphics = "0.8"
|
embedded-graphics = "0.8"
|
||||||
g13-driver.workspace = true
|
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"] }
|
time = { version = "0.3.47", features = ["formatting", "macros"] }
|
||||||
|
|
||||||
embedded-graphics-simulator = "0.7.0"
|
embedded-graphics-simulator = "0.7.0"
|
||||||
|
winit = "0.30.12"
|
||||||
|
|||||||
34
g13-os/src/app/mod.rs
Normal file
34
g13-os/src/app/mod.rs
Normal file
@@ -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<Box<dyn Page>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
pages: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, event: G13Event) {
|
||||||
|
println!("{:?}", event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw<D>(&mut self, display: &mut D) -> Result<(), Box<dyn std::error::Error>>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = Color>,
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
0
g13-os/src/app/pages/mod.rs
Normal file
0
g13-os/src/app/pages/mod.rs
Normal file
2
g13-os/src/app/widgets/mod.rs
Normal file
2
g13-os/src/app/widgets/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub trait Page: Widget {}
|
||||||
|
pub trait Widget {}
|
||||||
@@ -1,73 +1,40 @@
|
|||||||
// pub mod mouse;
|
mod app;
|
||||||
// mod os;
|
|
||||||
|
|
||||||
// use embedded_graphics::prelude::{Dimensions, DrawTarget};
|
use std::{
|
||||||
// use embedded_graphics::{pixelcolor::BinaryColor, prelude::Size};
|
thread::sleep,
|
||||||
// use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, Window};
|
time::{Duration, Instant},
|
||||||
// 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 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<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let g13 = G13::new()?;
|
let mut g13 = G13::new()?;
|
||||||
let (mut display, input) = g13.split();
|
|
||||||
|
|
||||||
let character_style = embedded_graphics::mono_font::MonoTextStyle::new(
|
let mut app = App::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();
|
|
||||||
|
|
||||||
for event in input {
|
let events = g13.events();
|
||||||
let Some(event) = event else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 Err(e) = app.draw(&mut g13) {
|
||||||
|
eprintln!("{:?}", e);
|
||||||
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)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
display.render()?;
|
g13.render()?;
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
let elapsed = Instant::now() - start;
|
||||||
|
if elapsed.as_millis() < 33 {
|
||||||
|
sleep(Duration::from_millis(33) - (elapsed));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user