diff --git a/src-tauri/src/bin/test_usb.rs b/src-tauri/src/bin/test_usb.rs index 77b26f0..51ebe7f 100644 --- a/src-tauri/src/bin/test_usb.rs +++ b/src-tauri/src/bin/test_usb.rs @@ -8,8 +8,9 @@ fn main() { let config = Config::from_str( r#"{ "deviceMode": "yuancon", - "outputMode": "none", - "ledMode": "reactive-8" + "outputMode": "kb-32-tasoller", + "ledMode": "reactive-8", + "keyboardSensitivity": 50 }"#, ); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6405d83..1bcc806 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3,5 +3,6 @@ windows_subsystem = "windows" )] #![feature(div_duration)] +#![feature(more_qualified_paths)] pub mod slider_io; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 0dc9a7b..5647652 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,6 +3,7 @@ windows_subsystem = "windows" )] #![feature(div_duration)] +#![feature(more_qualified_paths)] mod slider_io; diff --git a/src-tauri/src/slider_io/device.rs b/src-tauri/src/slider_io/device.rs index e8086dd..b5f7e4c 100644 --- a/src-tauri/src/slider_io/device.rs +++ b/src-tauri/src/slider_io/device.rs @@ -69,7 +69,7 @@ impl HidDeviceJob { } } - pub fn from_config(mode: &DeviceMode, state: &FullState) -> Self { + pub fn from_config(state: &FullState, mode: &DeviceMode) -> Self { match mode { DeviceMode::Yuancon => Self::new( state.clone(), diff --git a/src-tauri/src/slider_io/keyboard.rs b/src-tauri/src/slider_io/keyboard.rs index e7e6df3..cb364fe 100644 --- a/src-tauri/src/slider_io/keyboard.rs +++ b/src-tauri/src/slider_io/keyboard.rs @@ -90,14 +90,15 @@ impl KeyboardOutput { } } - pub fn tick(&mut self, controller_state: &ControllerState, sensitivity: &u8) { + pub fn tick(&mut self, flat_controller_state: &Vec) { self .next_keys .iter_mut() - .zip(controller_state.flat(sensitivity)) + .zip(flat_controller_state) .for_each(|(a, b)| { - *a = b; + *a = *b; }); + // println!("{:?}", self.next_keys); self.send(); } @@ -109,7 +110,12 @@ impl KeyboardOutput { fn send(&mut self) { self.n_kb_buf = 0; - for (i, (n, l)) in self.next_keys.iter().zip(self.last_keys.iter()).enumerate() { + for (i, (n, l)) in self + .next_keys + .iter_mut() + .zip(self.last_keys.iter_mut()) + .enumerate() + { match (*n, *l) { (false, true) => { let inner: &mut KEYBDINPUT = unsafe { self.kb_buf[self.n_kb_buf as usize].u.ki_mut() }; @@ -125,6 +131,7 @@ impl KeyboardOutput { } _ => {} } + *l = *n; } if self.n_kb_buf != 0 { diff --git a/src-tauri/src/slider_io/led.rs b/src-tauri/src/slider_io/led.rs index ddd6608..f864018 100644 --- a/src-tauri/src/slider_io/led.rs +++ b/src-tauri/src/slider_io/led.rs @@ -1,11 +1,6 @@ use std::{ - borrow::BorrowMut, - ops::{Deref, DerefMut}, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - thread::{self, JoinHandle}, + ops::DerefMut, + thread, time::{Duration, Instant}, }; @@ -13,148 +8,114 @@ use palette::{FromColor, Hsv, Srgb}; use crate::slider_io::{ config::{LedMode, ReactiveLayout}, - controller_state::{ControllerState, FullState, LedState}, + controller_state::{FullState, LedState}, + worker::Job, }; -fn update_reactive( - controller_state: &ControllerState, - led_state: &mut LedState, - reactive_layout: &ReactiveLayout, - sensitivity: &u8, -) { - let splits = match reactive_layout { - ReactiveLayout::Four => 4, - ReactiveLayout::Eight => 8, - ReactiveLayout::Sixteen => 16, - }; - let buttons_per_split = 32 / splits; +type LedCallback = fn(&Vec, &mut LedState) -> (); - let banks: Vec = controller_state - .flat(sensitivity) - .chunks(buttons_per_split) - .take(splits) - .map(|x| x.iter().any(|x| *x)) - .collect(); +pub struct LedJob { + state: FullState, + mode: LedMode, + sensitivity: u8, - // controller_state - // .ground_state - // .chunks(buttons_per_split) - // .map(|x| x.iter().max().unwrap() > &sensitivity) - // .collect(); + splits: usize, + buttons_per_split: usize, +} - // (0..splits) - // .map(|i| { - // controller_state.ground_state[i * buttons_per_split..(i + 1) * buttons_per_split] - // .iter() - // .max() - // .unwrap() - // > &sensitivity - // }) - // .collect(); - - led_state - .led_state - .chunks_mut(3) - .enumerate() - .for_each(|(idx, chunk)| match (idx + 1) % buttons_per_split { - 0 => { - chunk[0] = 255; - chunk[1] = 0; - chunk[2] = 255; - } - _ => match banks[idx / buttons_per_split] { - true => { - chunk[0] = 255; - chunk[1] = 0; - chunk[2] = 255; - } - false => { - chunk[0] = 255; - chunk[1] = 255; - chunk[2] = 0; - } +impl LedJob { + pub fn new(state: &FullState, mode: &LedMode) -> Self { + let splits = match mode { + LedMode::Reactive { layout } => match layout { + ReactiveLayout::Four => 4, + ReactiveLayout::Eight => 8, + ReactiveLayout::Sixteen => 16, }, - }); + _ => 16, + }; - // println!("{:?}", controller_state.ground_state); - // println!("{:?}", banks); - // println!("{:?}", led_state.led_state); - - led_state.dirty = true; -} - -fn update_attract(led_state: &mut LedState) { - let now = Instant::now(); - let theta = (now - led_state.start).div_duration_f64(Duration::from_secs(4)) % 1.0; - - led_state - .led_state - .chunks_mut(3) - .enumerate() - .for_each(|(idx, chunk)| { - let slice_theta = (&theta + (idx as f64) / 31.0) % 1.0; - let color = Srgb::from_color(Hsv::new(slice_theta * 360.0, 1.0, 1.0)).into_format::(); - chunk[0] = color.red; - chunk[1] = color.green; - chunk[2] = color.blue; - }); - - // println!("{} {:?}", theta, led_state.led_state); - - led_state.dirty = true; -} - -pub struct LedThread { - thread: Option>, - stop_signal: Arc, -} - -impl LedThread { - pub fn new(state: &FullState, mode: LedMode) -> Self { - let stop_signal = Arc::new(AtomicBool::new(false)); - - let stop_signal_clone = Arc::clone(&stop_signal); - let controller_state = state.clone_controller(); - let led_state = state.clone_led(); Self { - thread: Some(thread::spawn(move || loop { - // println!("Led thread: {:?}", mode); - match &mode { - LedMode::Reactive { layout } => { - let controller_state_handle = controller_state.lock().unwrap(); - let mut led_state_handle = led_state.lock().unwrap(); - update_reactive( - controller_state_handle.deref(), - led_state_handle.deref_mut(), - layout, - &20, - ) - } - LedMode::Attract => { - let mut led_state_handle = led_state.lock().unwrap(); - update_attract(led_state_handle.deref_mut()); - } - _ => {} - } + state: state.clone(), + mode: mode.clone(), + sensitivity: 20, - { - if stop_signal_clone.load(Ordering::SeqCst) { - break; - } - } - - thread::sleep(Duration::from_millis(33)) - })), - stop_signal, + splits, + buttons_per_split: 32 / splits, } } + + fn calc_lights(&self, flat_controller_state: &Vec, led_state: &mut LedState) { + match self.mode { + LedMode::Reactive { .. } => { + let banks: Vec = flat_controller_state + .chunks(self.buttons_per_split) + .take(self.splits) + .map(|x| x.iter().any(|x| *x)) + .collect(); + + led_state + .led_state + .chunks_mut(3) + .enumerate() + .for_each(|(idx, chunk)| match (idx + 1) % self.buttons_per_split { + 0 => { + chunk[0] = 255; + chunk[1] = 0; + chunk[2] = 255; + } + _ => match banks[idx / self.buttons_per_split] { + true => { + chunk[0] = 255; + chunk[1] = 0; + chunk[2] = 255; + } + false => { + chunk[0] = 255; + chunk[1] = 255; + chunk[2] = 0; + } + }, + }) + } + LedMode::Attract => { + let now = Instant::now(); + let theta = (now - led_state.start).div_duration_f64(Duration::from_secs(4)) % 1.0; + led_state + .led_state + .chunks_mut(3) + .enumerate() + .for_each(|(idx, chunk)| { + let slice_theta = (&theta + (idx as f64) / 31.0) % 1.0; + let color = + Srgb::from_color(Hsv::new(slice_theta * 360.0, 1.0, 1.0)).into_format::(); + chunk[0] = color.red; + chunk[1] = color.green; + chunk[2] = color.blue; + }); + } + _ => panic!("Not implemented"), + } + + led_state.dirty = true; + } } -impl Drop for LedThread { - fn drop(&mut self) { - self.stop_signal.swap(true, Ordering::SeqCst); - if self.thread.is_some() { - self.thread.take().unwrap().join().ok(); +impl Job for LedJob { + fn setup(&mut self) {} + + fn tick(&mut self) { + let flat_controller_state: Vec; + { + let controller_state_handle = self.state.controller_state.lock().unwrap(); + flat_controller_state = controller_state_handle.flat(&self.sensitivity); } + + { + let mut led_state_handle = self.state.led_state.lock().unwrap(); + self.calc_lights(&flat_controller_state, led_state_handle.deref_mut()); + } + thread::sleep(Duration::from_millis(33)); } + + fn teardown(&mut self) {} } diff --git a/src-tauri/src/slider_io/manager.rs b/src-tauri/src/slider_io/manager.rs index b8c8e6a..50fd35b 100644 --- a/src-tauri/src/slider_io/manager.rs +++ b/src-tauri/src/slider_io/manager.rs @@ -1,30 +1,31 @@ use crate::slider_io::{ - config::Config, controller_state::FullState, device::HidDeviceJob, led::LedThread, worker::Worker, + config::Config, controller_state::FullState, device::HidDeviceJob, led::LedJob, + output::KeyboardOutputJob, worker::Worker, }; pub struct Manager { state: FullState, config: Config, - // device_thread: DeviceThread, device_worker: Worker, - led_thread: LedThread, + output_worker: Worker, + led_worker: Worker, } impl Manager { pub fn new(config: Config) -> Self { let state = FullState::new(); - // let device_thread = DeviceThread::new(&state, config.device_mode.clone()); - let device_worker = Worker::new(HidDeviceJob::from_config(&config.device_mode, &state)); - let led_thread = LedThread::new(&state, config.led_mode.clone()); + let device_worker = Worker::new(HidDeviceJob::from_config(&state, &config.device_mode)); + let output_worker = Worker::new(KeyboardOutputJob::new(&state, &config.output_mode)); + let led_worker = Worker::new(LedJob::new(&state, &config.led_mode)); println!("Starting manager with config: {:?}", config); Self { state, config, - // device_thread, device_worker, - led_thread, + output_worker, + led_worker, } } } diff --git a/src-tauri/src/slider_io/mod.rs b/src-tauri/src/slider_io/mod.rs index 549a2fe..d89887b 100644 --- a/src-tauri/src/slider_io/mod.rs +++ b/src-tauri/src/slider_io/mod.rs @@ -1,8 +1,11 @@ pub mod config; pub mod controller_state; -pub mod device; -pub mod keyboard; -pub mod led; -pub mod manager; -pub mod output; pub mod worker; + +pub mod keyboard; + +pub mod device; +pub mod led; +pub mod output; + +pub mod manager; diff --git a/src-tauri/src/slider_io/output.rs b/src-tauri/src/slider_io/output.rs index 73bb3f3..4ec020d 100644 --- a/src-tauri/src/slider_io/output.rs +++ b/src-tauri/src/slider_io/output.rs @@ -1,64 +1,52 @@ use std::{ - ops::Deref, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - thread::{self, JoinHandle}, - time::Duration, + thread, + time::{Duration, Instant}, }; -use crate::slider_io::{config::OutputMode, controller_state::FullState, keyboard}; +use crate::slider_io::{ + config::{KeyboardLayout, OutputMode}, + controller_state::FullState, + keyboard::KeyboardOutput, + worker::Job, +}; -pub struct OutputThread { - thread: Option>, - stop_signal: Arc, +pub struct KeyboardOutputJob { + state: FullState, + sensitivity: u8, + keyboard_output: KeyboardOutput, } -impl OutputThread { - pub fn new(state: &FullState, mode: OutputMode) -> Self { - let controller_state = state.clone_controller(); - let stop_signal = Arc::new(AtomicBool::new(false)); - - let stop_signal_clone = Arc::clone(&stop_signal); - - Self { - thread: Some(match mode { - OutputMode::None => thread::spawn(|| {}), - OutputMode::Keyboard { - layout, - sensitivity, - } => thread::spawn(move || { - let mut keyboard_output = keyboard::KeyboardOutput::new(layout); - loop { - { - let controller_state_handle = controller_state.lock().unwrap(); - keyboard_output.tick(controller_state_handle.deref(), &sensitivity); - } - - { - if stop_signal_clone.load(Ordering::SeqCst) { - break; - } - } - - thread::sleep(Duration::from_millis(10)); - } - - keyboard_output.reset(); - }), - OutputMode::Websocket { .. } => thread::spawn(|| {}), - }), - stop_signal, +impl KeyboardOutputJob { + pub fn new(state: &FullState, mode: &OutputMode) -> Self { + match mode { + OutputMode::Keyboard { + layout, + sensitivity, + } => Self { + state: state.clone(), + sensitivity: *sensitivity, + keyboard_output: KeyboardOutput::new(layout.clone()), + }, + _ => panic!("Not implemented"), } } } -impl Drop for OutputThread { - fn drop(&mut self) { - self.stop_signal.swap(true, Ordering::SeqCst); - if self.thread.is_some() { - self.thread.take().unwrap().join().ok(); +impl Job for KeyboardOutputJob { + fn setup(&mut self) {} + + fn tick(&mut self) { + let flat_controller_state: Vec; + { + let controller_state_handle = self.state.controller_state.lock().unwrap(); + flat_controller_state = controller_state_handle.flat(&self.sensitivity); } + + self.keyboard_output.tick(&flat_controller_state); + thread::sleep(Duration::from_millis(10)); + } + + fn teardown(&mut self) { + self.keyboard_output.reset(); } }