From e64e68e9d9e3e5905fc225c632c73d1c0f4245f6 Mon Sep 17 00:00:00 2001 From: 4yn Date: Sat, 29 Jan 2022 01:10:12 +0800 Subject: [PATCH] migrate to worker --- src-tauri/src/bin/{test.rs => test_usb.rs} | 0 src-tauri/src/bin/test_worker.rs | 31 ++++ src-tauri/src/slider_io/config.rs | 7 +- src-tauri/src/slider_io/controller_state.rs | 9 ++ src-tauri/src/slider_io/device.rs | 3 +- src-tauri/src/slider_io/device_job.rs | 170 ++++++++++++++++++++ src-tauri/src/slider_io/manager.rs | 12 +- src-tauri/src/slider_io/mod.rs | 2 + src-tauri/src/slider_io/worker.rs | 48 ++++++ 9 files changed, 274 insertions(+), 8 deletions(-) rename src-tauri/src/bin/{test.rs => test_usb.rs} (100%) create mode 100644 src-tauri/src/bin/test_worker.rs create mode 100644 src-tauri/src/slider_io/device_job.rs create mode 100644 src-tauri/src/slider_io/worker.rs diff --git a/src-tauri/src/bin/test.rs b/src-tauri/src/bin/test_usb.rs similarity index 100% rename from src-tauri/src/bin/test.rs rename to src-tauri/src/bin/test_usb.rs diff --git a/src-tauri/src/bin/test_worker.rs b/src-tauri/src/bin/test_worker.rs new file mode 100644 index 0000000..cda6116 --- /dev/null +++ b/src-tauri/src/bin/test_worker.rs @@ -0,0 +1,31 @@ +extern crate slidershim; + +use std::io; + +use slidershim::slider_io::worker::{Job, Worker}; + +struct TestJob { + data: i64, +} + +impl Job for TestJob { + fn setup(&mut self) { + self.data = 10; + println!("setup {}", self.data); + } + fn tick(&mut self) { + self.data -= 1; + println!("tick {}", self.data); + } + fn teardown(&mut self) { + self.data = 11; + println!("teardown {}", self.data); + } +} + +fn main() { + let worker = Worker::new(TestJob { data: 1 }); + + let mut input = String::new(); + let string = io::stdin().read_line(&mut input).unwrap(); +} diff --git a/src-tauri/src/slider_io/config.rs b/src-tauri/src/slider_io/config.rs index 5b8b37b..79f02dc 100644 --- a/src-tauri/src/slider_io/config.rs +++ b/src-tauri/src/slider_io/config.rs @@ -4,7 +4,8 @@ use std::convert::TryFrom; #[derive(Debug, Clone)] pub enum DeviceMode { None, - Tasoller { version: i64 }, + TasollerOne, + TasollerTwo, Yuancon, Brokenithm { ground_only: bool }, } @@ -60,8 +61,8 @@ impl Config { raw: s.to_string(), device_mode: match v["deviceMode"].as_str().unwrap() { "none" => DeviceMode::None, - "tasoller-one" => DeviceMode::Tasoller { version: 1 }, - "tasoller-two" => DeviceMode::Tasoller { version: 2 }, + "tasoller-one" => DeviceMode::TasollerOne, + "tasoller-two" => DeviceMode::TasollerTwo, "yuancon" => DeviceMode::Yuancon, "brokenithm" => DeviceMode::Brokenithm { ground_only: false }, "brokenithm-ground" => DeviceMode::Brokenithm { ground_only: true }, diff --git a/src-tauri/src/slider_io/controller_state.rs b/src-tauri/src/slider_io/controller_state.rs index 7710cf8..22a1b2c 100644 --- a/src-tauri/src/slider_io/controller_state.rs +++ b/src-tauri/src/slider_io/controller_state.rs @@ -71,3 +71,12 @@ impl FullState { Arc::clone(&self.led_state) } } + +impl Clone for FullState { + fn clone(&self) -> Self { + Self { + controller_state: Arc::clone(&self.controller_state), + led_state: Arc::clone(&self.led_state), + } + } +} diff --git a/src-tauri/src/slider_io/device.rs b/src-tauri/src/slider_io/device.rs index 672a180..b1e963b 100644 --- a/src-tauri/src/slider_io/device.rs +++ b/src-tauri/src/slider_io/device.rs @@ -23,7 +23,8 @@ impl DeviceThread { Self { thread: Some(match mode { DeviceMode::None => thread::spawn(|| {}), - DeviceMode::Tasoller { .. } => thread::spawn(|| {}), + DeviceMode::TasollerOne => thread::spawn(|| {}), + DeviceMode::TasollerTwo => thread::spawn(|| {}), DeviceMode::Yuancon => thread::spawn(move || { hid::poll_controller( 0x1973, diff --git a/src-tauri/src/slider_io/device_job.rs b/src-tauri/src/slider_io/device_job.rs new file mode 100644 index 0000000..073a8c3 --- /dev/null +++ b/src-tauri/src/slider_io/device_job.rs @@ -0,0 +1,170 @@ +use std::{error, ops::DerefMut, time::Duration}; + +use rusb::{self, DeviceHandle, GlobalContext}; + +use crate::slider_io::{ + config::DeviceMode, + controller_state::{ControllerState, FullState, LedState}, + hid, + worker::{Job, Worker}, +}; + +pub struct Buffer { + pub data: [u8; 128], + pub len: usize, +} + +impl Buffer { + pub fn new() -> Self { + Buffer { + data: [0; 128], + len: 0, + } + } + + fn slice(&self) -> &[u8] { + &self.data[0..self.len] + } +} + +type HidReadCallback = fn(&Buffer, &mut ControllerState) -> (); +type HidLedCallback = fn(&mut Buffer, &mut LedState) -> (); + +pub struct HidDeviceJob { + state: FullState, + vid: u16, + pid: u16, + read_endpoint: u8, + led_endpoint: u8, + + read_callback: HidReadCallback, + read_buf: Buffer, + + led_callback: HidLedCallback, + led_buf: Buffer, + + handle: Option>, +} + +impl HidDeviceJob { + fn new( + state: FullState, + vid: u16, + pid: u16, + read_endpoint: u8, + led_endpoint: u8, + read_callback: HidReadCallback, + led_callback: HidLedCallback, + ) -> Self { + Self { + state, + vid, + pid, + read_endpoint, + led_endpoint, + read_callback, + read_buf: Buffer::new(), + led_callback, + led_buf: Buffer::new(), + handle: None, + } + } + + pub fn from_config(mode: &DeviceMode, state: &FullState) -> Self { + match mode { + DeviceMode::Yuancon => Self::new( + state.clone(), + 0x1973, + 0x2001, + 0x81, + 0x02, + |buf, controller_state| { + if buf.len != 34 { + return; + } + + controller_state + .ground_state + .clone_from_slice(&buf.data[2..34]); + for i in 0..6 { + controller_state.air_state[i ^ 1] = if buf.data[0] & (1 << i) == 0 { 1 } else { 0 }; + } + for i in 0..3 { + controller_state.extra_state[i] = if buf.data[1] & (1 << i) == 0 { 1 } else { 0 }; + } + }, + |buf, led_state| { + if !led_state.dirty { + return; + } + buf.len = 31 * 2; + buf + .data + .chunks_mut(2) + .take(31) + .zip(led_state.led_state.chunks(3).rev()) + .for_each(|(buf_chunk, state_chunk)| { + buf_chunk[0] = (state_chunk[0] << 3 & 0xe0) | (state_chunk[2] >> 3); + buf_chunk[1] = (state_chunk[1] & 0xf8) | (state_chunk[0] >> 5); + }); + led_state.dirty = false; + }, + ), + _ => panic!("Not implemented"), + } + } + + fn setup_impl(&mut self) -> Result<(), Box> { + let mut handle = rusb::open_device_with_vid_pid(self.vid, self.pid).unwrap(); + if handle.kernel_driver_active(0).unwrap_or(false) { + handle.detach_kernel_driver(0)?; + } + handle.set_active_configuration(1)?; + handle.claim_interface(0)?; + self.handle = Some(handle); + Ok(()) + } +} + +const timeout: Duration = Duration::from_millis(20); + +impl Job for HidDeviceJob { + fn setup(&mut self) { + self.setup_impl().unwrap(); + } + + fn tick(&mut self) { + // Input loop + let handle = self.handle.as_mut().unwrap(); + + { + let res = handle + .read_interrupt(self.read_endpoint, &mut self.read_buf.data, timeout) + .unwrap_or(0); + self.read_buf.len = res; + if self.read_buf.len != 0 { + let mut controller_state_handle = self.state.controller_state.lock().unwrap(); + (self.read_callback)(&self.read_buf, controller_state_handle.deref_mut()); + } + } + + // Led loop + { + let mut led_state_handle = self.state.led_state.lock().unwrap(); + (self.led_callback)(&mut self.led_buf, led_state_handle.deref_mut()); + if self.led_buf.len != 0 { + let res = handle + .write_interrupt(self.led_endpoint, &self.led_buf.data, timeout) + .unwrap_or(0); + if res == self.led_buf.len + 1 { + self.led_buf.len = 0; + } + } + } + } + + fn teardown(&mut self) { + let handle = self.handle.as_mut().unwrap(); + handle.release_interface(0).ok(); + } +} diff --git a/src-tauri/src/slider_io/manager.rs b/src-tauri/src/slider_io/manager.rs index 245b5c6..e75610e 100644 --- a/src-tauri/src/slider_io/manager.rs +++ b/src-tauri/src/slider_io/manager.rs @@ -1,18 +1,21 @@ use crate::slider_io::{ - config::Config, controller_state::FullState, device::DeviceThread, led::LedThread, + config::Config, controller_state::FullState, device::DeviceThread, device_job::HidDeviceJob, + led::LedThread, worker::Worker, }; pub struct Manager { state: FullState, config: Config, - device_thread: DeviceThread, + // device_thread: DeviceThread, + device_worker: Worker, led_thread: LedThread, } impl Manager { pub fn new(config: Config) -> Self { let state = FullState::new(); - let device_thread = DeviceThread::new(&state, config.device_mode.clone()); + // 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()); println!("Starting manager with config: {:?}", config); @@ -20,7 +23,8 @@ impl Manager { Self { state, config, - device_thread, + // device_thread, + device_worker, led_thread, } } diff --git a/src-tauri/src/slider_io/mod.rs b/src-tauri/src/slider_io/mod.rs index f4c35d7..4abd2c5 100644 --- a/src-tauri/src/slider_io/mod.rs +++ b/src-tauri/src/slider_io/mod.rs @@ -1,8 +1,10 @@ pub mod config; pub mod controller_state; pub mod device; +pub mod device_job; pub mod hid; pub mod keyboard; pub mod led; pub mod manager; pub mod output; +pub mod worker; diff --git a/src-tauri/src/slider_io/worker.rs b/src-tauri/src/slider_io/worker.rs new file mode 100644 index 0000000..8ce017f --- /dev/null +++ b/src-tauri/src/slider_io/worker.rs @@ -0,0 +1,48 @@ +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread, +}; + +pub trait Job: Send { + fn setup(&mut self); + fn tick(&mut self); + fn teardown(&mut self); +} + +pub struct Worker { + thread: Option>, + stop_signal: Arc, +} + +impl Worker { + pub fn new(mut job: T) -> Self { + let stop_signal = Arc::new(AtomicBool::new(false)); + + let stop_signal_clone = Arc::clone(&stop_signal); + Self { + thread: Some(thread::spawn(move || { + job.setup(); + loop { + job.tick(); + if stop_signal_clone.load(Ordering::SeqCst) { + break; + } + } + job.teardown(); + })), + stop_signal, + } + } +} + +impl Drop for Worker { + fn drop(&mut self) { + self.stop_signal.swap(true, Ordering::SeqCst); + if self.thread.is_some() { + self.thread.take().unwrap().join().ok(); + } + } +}