diff --git a/README.md b/README.md index 389e6cd..2d35597 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ - major - acio support - qol - - proper error handling + - more error handling - comments - when umiguri comes out - ouptut websocket diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 30f93fa..249f022 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -41,10 +41,9 @@ fn main() { { let log_file_path = slider_io::Config::get_log_file_path().unwrap(); simple_logging::log_to_file(log_file_path.as_path(), log::LevelFilter::Debug).unwrap(); - // simple_logging::log_to_file("./log.txt", log::LevelFilter::Debug).unwrap(); } - let config = Arc::new(Mutex::new(Some(slider_io::Config::default()))); + let config = Arc::new(Mutex::new(Some(slider_io::Config::load()))); let manager = Arc::new(Mutex::new(slider_io::Manager::new())); { let config_handle = config.lock().unwrap(); diff --git a/src-tauri/src/slider_io/brokenithm.rs b/src-tauri/src/slider_io/brokenithm.rs index 39b1851..7d70ae4 100644 --- a/src-tauri/src/slider_io/brokenithm.rs +++ b/src-tauri/src/slider_io/brokenithm.rs @@ -46,24 +46,6 @@ static BROKENITHM_BIN_FILES: phf::Map<&'static str, (&'static [u8], &'static str }; async fn serve_file(path: &str) -> Result, Infallible> { - // let mut pb = current_exe().unwrap(); - // pb.pop(); - // pb.push("res/www"); - // pb.push(path); - // pb.clean(); - - // println!("CWD {:?}", std::env::current_dir()); - - // match File::open(&pb).await { - // Ok(f) => { - // info!("Serving file {:?}", pb); - // let stream = FramedRead::new(f, BytesCodec::new()); - // let body = Body::wrap_stream(stream); - // Ok(Response::new(body)) - // } - // Err(_) => error_response().await, - // } - match ( BROKENITHM_STR_FILES.get(path), BROKENITHM_BIN_FILES.get(path), @@ -120,33 +102,30 @@ async fn handle_brokenithm( Some(msg) => match msg { Ok(msg) => match msg { Message::Text(msg) => { - let mut chars = msg.chars(); - let head = chars.next().unwrap(); - match head { - 'a' => { - msg_write_handle - .send(Message::Text("alive".to_string())) - .ok(); - } - 'b' => { - let flat_state: Vec = chars - .map(|x| match x { - '0' => false, - '1' => true, - _ => unreachable!(), - }) - .collect(); - let mut controller_state_handle = state_handle.controller_state.lock().unwrap(); - for (idx, c) in flat_state[0..32].iter().enumerate() { - controller_state_handle.ground_state[idx] = match c { - false => 0, - true => 255, - } + let chars = msg.chars().collect::>(); + + match chars.len() { + 6 => { + if chars[0] == 'a' { + msg_write_handle + .send(Message::Text("alive".to_string())) + .ok(); } - for (idx, c) in flat_state[32..38].iter().enumerate() { - controller_state_handle.air_state[idx] = match c { - false => 0, - true => 1, + } + 39 => { + if chars[0] == 'b' { + let mut controller_state_handle = state_handle.controller_state.lock().unwrap(); + for (idx, c) in chars[0..32].iter().enumerate() { + controller_state_handle.ground_state[idx] = match *c == '1' { + false => 0, + true => 255, + } + } + for (idx, c) in chars[32..38].iter().enumerate() { + controller_state_handle.air_state[idx] = match *c == '1' { + false => 0, + true => 1, + } } } } diff --git a/src-tauri/src/slider_io/config.rs b/src-tauri/src/slider_io/config.rs index 5a8a188..e29996f 100644 --- a/src-tauri/src/slider_io/config.rs +++ b/src-tauri/src/slider_io/config.rs @@ -213,27 +213,10 @@ impl Config { }) } - fn factory() -> Self { - Self::from_str( - r#"{ - "deviceMode": "none", - "outputMode": "none", - "ledMode": "none", - "keyboardSensitivity": 20, - "outputWebsocketUrl": "localhost:3000", - "outputPolling": "60", - "ledSensitivity": 20, - "ledWebsocketUrl": "localhost:3001", - "ledSerialPort": "COM5" - }"#, - ) - .unwrap() - } - pub fn get_log_file_path() -> Option> { let project_dir = ProjectDirs::from("me", "impress labs", "slidershim").unwrap(); let config_dir = project_dir.config_dir(); - fs::create_dir_all(config_dir).unwrap(); + fs::create_dir_all(config_dir).ok()?; let log_path = config_dir.join("log.txt"); @@ -243,7 +226,7 @@ impl Config { pub fn get_brokenithm_qr_path() -> Option> { let project_dir = ProjectDirs::from("me", "impress labs", "slidershim").unwrap(); let config_dir = project_dir.config_dir(); - fs::create_dir_all(config_dir).unwrap(); + fs::create_dir_all(config_dir).ok()?; let brokenithm_qr_path = config_dir.join("brokenithm.png"); @@ -262,18 +245,35 @@ impl Config { return Some(Box::new(brokenithm_qr_path)); } - fn get_saved_path() -> Option> { + fn get_config_path() -> Option> { let project_dir = ProjectDirs::from("me", "impress labs", "slidershim").unwrap(); let config_dir = project_dir.config_dir(); - fs::create_dir_all(config_dir).unwrap(); + fs::create_dir_all(config_dir).ok()?; let config_path = config_dir.join("config.json"); return Some(Box::new(config_path)); } + fn default() -> Self { + Self::from_str( + r#"{ + "deviceMode": "none", + "outputMode": "none", + "ledMode": "none", + "keyboardSensitivity": 20, + "outputWebsocketUrl": "localhost:3000", + "outputPolling": "60", + "ledSensitivity": 20, + "ledWebsocketUrl": "localhost:3001", + "ledSerialPort": "COM5" + }"#, + ) + .unwrap() + } + fn load_saved() -> Option { - let config_path = Self::get_saved_path()?; + let config_path = Self::get_config_path()?; if !config_path.exists() { return None; } @@ -282,22 +282,22 @@ impl Config { return Self::from_str(saved_data.as_str()); } - pub fn default() -> Self { + pub fn load() -> Self { Self::load_saved() .or_else(|| { warn!("Config loading from file failed, using default"); - Some(Self::factory()) + Some(Self::default()) }) .unwrap() } pub fn save(&self) -> Option<()> { info!("Config saving..."); - let config_path = Self::get_saved_path()?; + let config_path = Self::get_config_path()?; info!("Config saving to {:?}", config_path); fs::write(config_path.as_path(), self.raw.as_str()).unwrap(); - info!("Config saved"); + Some(()) } } diff --git a/src-tauri/src/slider_io/device.rs b/src-tauri/src/slider_io/device.rs index 8847e35..8890009 100644 --- a/src-tauri/src/slider_io/device.rs +++ b/src-tauri/src/slider_io/device.rs @@ -185,7 +185,7 @@ impl HidDeviceJob { } } - fn setup_impl(&mut self) -> Result<(), Box> { + fn get_handle(&mut self) -> Result<(), Box> { info!("Device finding vid {} pid {}", self.vid, self.pid); let handle = rusb::open_device_with_vid_pid(self.vid, self.pid); if handle.is_none() { @@ -212,7 +212,7 @@ const TIMEOUT: Duration = Duration::from_millis(20); impl ThreadJob for HidDeviceJob { fn setup(&mut self) -> bool { - match self.setup_impl() { + match self.get_handle() { Ok(_) => { info!("Device OK"); true @@ -279,8 +279,7 @@ impl ThreadJob for HidDeviceJob { } fn teardown(&mut self) { - if self.handle.is_some() { - let handle = self.handle.as_mut().unwrap(); + if let Some(handle) = self.handle.as_mut() { handle.release_interface(0).ok(); } } diff --git a/src-tauri/src/slider_io/gamepad.rs b/src-tauri/src/slider_io/gamepad.rs index b8ccb8b..0bd9c34 100644 --- a/src-tauri/src/slider_io/gamepad.rs +++ b/src-tauri/src/slider_io/gamepad.rs @@ -1,3 +1,5 @@ +use log::error; +use std::error::Error; use vigem_client::{Client, TargetId, XButtons, XGamepad, Xbox360Wired}; use crate::slider_io::{config::GamepadLayout, output::OutputHandler, voltex::VoltexState}; @@ -9,25 +11,49 @@ pub struct GamepadOutput { } impl GamepadOutput { - pub fn new(layout: GamepadLayout) -> Self { - let client = Client::connect().unwrap(); + pub fn new(layout: GamepadLayout) -> Option { + let target = Self::get_target(); let use_air = match layout { GamepadLayout::Neardayo => true, _ => false, }; + + match target { + Ok(target) => Some(Self { + target, + use_air, + gamepad: XGamepad::default(), + }), + Err(e) => { + error!("Gamepad connection error: {}", e); + error!("Gamepad connection error: Is ViGEMBus missing?"); + None + } + } + } + + fn get_target() -> Result, Box> { + let client = Client::connect()?; + let mut target = Xbox360Wired::new(client, TargetId::XBOX360_WIRED); - target.plugin().unwrap(); - target.wait_ready().unwrap(); - Self { - target, - use_air, - gamepad: XGamepad::default(), + target.plugin()?; + target.wait_ready()?; + Ok(target) + } + + fn update(&mut self) -> bool { + match self.target.update(&self.gamepad) { + Ok(_) => true, + Err(e) => { + error!("Gamepad update error: {}", e); + false + } } } } impl OutputHandler for GamepadOutput { - fn tick(&mut self, flat_controller_state: &Vec) { + fn tick(&mut self, flat_controller_state: &Vec) -> bool { let voltex_state = VoltexState::from_flat(flat_controller_state); let buttons = voltex_state @@ -84,20 +110,26 @@ impl OutputHandler for GamepadOutput { dirty = true; } - if dirty { - self.target.update(&self.gamepad).unwrap(); + match dirty { + true => self.update(), + false => true, } } fn reset(&mut self) { self.gamepad = XGamepad::default(); - self.target.update(&self.gamepad).unwrap(); + self.update(); } } impl Drop for GamepadOutput { fn drop(&mut self) { - self.target.unplug().unwrap(); + match self.target.unplug() { + Ok(_) => {} + Err(e) => { + error!("Gamepad unplug error: {}", e); + } + } } } diff --git a/src-tauri/src/slider_io/keyboard.rs b/src-tauri/src/slider_io/keyboard.rs index 12c06d8..d79c560 100644 --- a/src-tauri/src/slider_io/keyboard.rs +++ b/src-tauri/src/slider_io/keyboard.rs @@ -165,7 +165,7 @@ impl KeyboardOutput { } impl OutputHandler for KeyboardOutput { - fn tick(&mut self, flat_controller_state: &Vec) { + fn tick(&mut self, flat_controller_state: &Vec) -> bool { self.next_keys.fill(false); for (idx, x) in flat_controller_state.iter().enumerate() { if *x { @@ -173,6 +173,7 @@ impl OutputHandler for KeyboardOutput { } } self.send(); + true } fn reset(&mut self) { diff --git a/src-tauri/src/slider_io/manager.rs b/src-tauri/src/slider_io/manager.rs index 2b8a1c8..b80c364 100644 --- a/src-tauri/src/slider_io/manager.rs +++ b/src-tauri/src/slider_io/manager.rs @@ -98,7 +98,11 @@ impl Manager { impl Drop for Manager { fn drop(&mut self) { - self.tx_stop.take().unwrap().send(()).unwrap(); - self.join_handle.take().unwrap().join().unwrap(); + if let Some(tx_stop) = self.tx_stop.take() { + tx_stop.send(()).ok(); + } + if let Some(join_handle) = self.join_handle.take() { + join_handle.join().ok(); + } } } diff --git a/src-tauri/src/slider_io/output.rs b/src-tauri/src/slider_io/output.rs index bc68413..1a9f4fb 100644 --- a/src-tauri/src/slider_io/output.rs +++ b/src-tauri/src/slider_io/output.rs @@ -1,3 +1,4 @@ +use log::error; use std::time::Duration; use crate::slider_io::{ @@ -6,48 +7,65 @@ use crate::slider_io::{ }; pub trait OutputHandler: Send { - fn tick(&mut self, flat_controller_state: &Vec); + fn tick(&mut self, flat_controller_state: &Vec) -> bool; fn reset(&mut self); } pub struct OutputJob { state: FullState, + mode: OutputMode, t: u64, sensitivity: u8, - handler: Box, + handler: Option>, } impl OutputJob { pub fn new(state: &FullState, mode: &OutputMode) -> Self { - match mode { - OutputMode::Keyboard { - layout, - polling, - sensitivity, - } => Self { - state: state.clone(), - t: polling.to_t_u64(), - sensitivity: *sensitivity, - handler: Box::new(KeyboardOutput::new(layout.clone())), - }, - OutputMode::Gamepad { - layout, - polling, - sensitivity, - } => Self { - state: state.clone(), - t: polling.to_t_u64(), - sensitivity: *sensitivity, - handler: Box::new(GamepadOutput::new(layout.clone())), - }, - _ => panic!("Not implemented"), + Self { + state: state.clone(), + mode: mode.clone(), + t: 0, + sensitivity: 0, + handler: None, } } } impl ThreadJob for OutputJob { fn setup(&mut self) -> bool { - true + match self.mode { + OutputMode::Keyboard { + layout, + polling, + sensitivity, + } => { + self.t = polling.to_t_u64(); + self.sensitivity = sensitivity; + self.handler = Some(Box::new(KeyboardOutput::new(layout.clone()))); + + true + } + OutputMode::Gamepad { + layout, + polling, + sensitivity, + } => { + self.t = polling.to_t_u64(); + self.sensitivity = sensitivity; + let handler = GamepadOutput::new(layout.clone()); + match handler { + Some(handler) => { + self.handler = Some(Box::new(handler)); + true + } + None => false, + } + } + _ => { + error!("Not implemented"); + false + } + } } fn tick(&mut self) -> bool { @@ -57,14 +75,17 @@ impl ThreadJob for OutputJob { flat_controller_state = controller_state_handle.flat(&self.sensitivity); } - self.handler.tick(&flat_controller_state); - // thread::sleep(Duration::from_millis(self.t)); + if let Some(handler) = self.handler.as_mut() { + handler.tick(&flat_controller_state); + } spin_sleep::sleep(Duration::from_micros(self.t)); true } fn teardown(&mut self) { - self.handler.reset(); + if let Some(handler) = self.handler.as_mut() { + handler.reset(); + } } } diff --git a/src-tauri/src/slider_io/utils.rs b/src-tauri/src/slider_io/utils.rs index 95b9446..62ccf3e 100644 --- a/src-tauri/src/slider_io/utils.rs +++ b/src-tauri/src/slider_io/utils.rs @@ -84,9 +84,11 @@ impl LoopTimer { } } - // pub fn reset(&mut self) { - // self.buf = vec![Instant::now(); 100]; - // } + #[allow(dead_code)] + pub fn reset(&mut self) { + self.buf = vec![Instant::now() - Duration::from_secs(10); 100]; + self.cur = 0; + } pub fn fork(&self) -> Arc { Arc::clone(&self.freq) diff --git a/src-tauri/src/slider_io/worker.rs b/src-tauri/src/slider_io/worker.rs index 4828193..2a53d6a 100644 --- a/src-tauri/src/slider_io/worker.rs +++ b/src-tauri/src/slider_io/worker.rs @@ -59,9 +59,9 @@ impl Drop for ThreadWorker { info!("Thread worker stopping {}", self.name); self.stop_signal.store(true, Ordering::SeqCst); - if self.thread.is_some() { - self.thread.take().unwrap().join().ok(); - } + if let Some(thread) = self.thread.take() { + thread.join().ok(); + }; } } @@ -88,7 +88,7 @@ impl AsyncWorker { let task = tokio::spawn(async move { job .run(async move { - recv_stop.await.unwrap(); + recv_stop.await.ok(); }) .await; }); @@ -105,7 +105,9 @@ impl Drop for AsyncWorker { fn drop(&mut self) { info!("Async worker stopping {}", self.name); - self.stop_signal.take().unwrap().send(()).unwrap(); + if let Some(stop_signal) = self.stop_signal.take() { + stop_signal.send(()).ok(); + } self.task.take(); } }