From 62551a2f88b0900499dcfcbdf253dd9eb7c7d274 Mon Sep 17 00:00:00 2001 From: 4yn <4yn@users.noreply.github.com> Date: Sun, 3 Apr 2022 03:11:37 +0800 Subject: [PATCH] add ds4 hori slider --- src-slider_io/Cargo.lock | 4 +- src-slider_io/Cargo.toml | 2 +- src-slider_io/src/lighting/config.rs | 7 ++ src-slider_io/src/lighting/lighting.rs | 44 ++++++++- src-slider_io/src/output/config.rs | 8 ++ src-slider_io/src/output/hori.rs | 132 +++++++++++++++++++++++++ src-slider_io/src/output/mod.rs | 1 + src-slider_io/src/output/output.rs | 20 +++- src-slider_io/src/shared/hori.rs | 27 +++++ src-slider_io/src/shared/mod.rs | 1 + src-tauri/Cargo.lock | 4 +- src/App.svelte | 10 +- 12 files changed, 251 insertions(+), 9 deletions(-) create mode 100644 src-slider_io/src/output/hori.rs create mode 100644 src-slider_io/src/shared/hori.rs diff --git a/src-slider_io/Cargo.lock b/src-slider_io/Cargo.lock index 5e771a6..1b6b9a3 100644 --- a/src-slider_io/Cargo.lock +++ b/src-slider_io/Cargo.lock @@ -1587,9 +1587,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vigem-client" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965e349c8ec4eb36c06878b99952f35b9f459e6912419837ecb85fb5502a6de3" +checksum = "5d886912e88b1d3b02a97d933df7829db8fb8561920410805794a48680c212d5" dependencies = [ "winapi", ] diff --git a/src-slider_io/Cargo.toml b/src-slider_io/Cargo.toml index af9c642..af5047d 100644 --- a/src-slider_io/Cargo.toml +++ b/src-slider_io/Cargo.toml @@ -29,7 +29,7 @@ image = "0.23.14" rusb = "0.9.0" serialport = "4.0.1" wwserial = {path = "../src-wwserial" } -vigem-client = "0.1.1" +vigem-client = { version = "0.1.2", features = ["unstable"] } winapi = "0.3.9" ipconfig = "0.3.0" diff --git a/src-slider_io/src/lighting/config.rs b/src-slider_io/src/lighting/config.rs index 2893c89..de768d3 100644 --- a/src-slider_io/src/lighting/config.rs +++ b/src-slider_io/src/lighting/config.rs @@ -5,6 +5,7 @@ pub enum ReactiveLayout { Even { splits: usize }, Six, Voltex, + Hori, Rainbow, } @@ -109,6 +110,12 @@ impl LightsMode { sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?, color: ColorScheme::default(), }, + "reactive-hori" => LightsMode::Reactive { + faster: v["ledFaster"].as_bool()?, + layout: ReactiveLayout::Hori, + sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?, + color: ColorScheme::default(), + }, "attract" => LightsMode::Attract { faster: v["ledFaster"].as_bool()?, }, diff --git a/src-slider_io/src/lighting/lighting.rs b/src-slider_io/src/lighting/lighting.rs index 330a389..2c87f14 100644 --- a/src-slider_io/src/lighting/lighting.rs +++ b/src-slider_io/src/lighting/lighting.rs @@ -9,7 +9,7 @@ use std::{ use tokio::time::{interval, Interval}; use crate::{ - shared::{utils::Buffer, voltex::VoltexState, worker::AsyncJob}, + shared::{hori::HoriState, utils::Buffer, voltex::VoltexState, worker::AsyncJob}, state::{SliderLights, SliderState}, }; @@ -154,6 +154,48 @@ impl LightsJob { } } } + ReactiveLayout::Hori => { + lights.ground.fill(0); + + // Fixed + for idx in [7, 15, 23] { + lights.paint(idx, &[64, 64, 64]); + } + + let hori_state = HoriState::from_flat(flat_input); + + for (idx, (bt, color)) in hori_state + .bt + .iter() + .zip([ + &[64, 226, 160], + &[255, 105, 248], + &[124, 178, 232], + &[255, 102, 102], + ]) + .enumerate() + { + let color_f = match bt { + true => 1, + false => 4, + }; + let adjcolor = [color[0] / color_f, color[1] / color_f, color[2] / color_f]; + for i in 0..4 { + lights.paint(idx * 8 + i * 2, &adjcolor); + } + } + + for (idx, (a, b)) in hori_state + .slider + .iter() + .zip(hori_state.slider.iter().skip(1)) + .enumerate() + { + if *a || *b { + lights.paint(1 + idx * 2, &[200, 200, 200]); + } + } + } ReactiveLayout::Rainbow => { let banks: Vec = flat_input .chunks(2) diff --git a/src-slider_io/src/output/config.rs b/src-slider_io/src/output/config.rs index 0aa53a7..5f0a378 100644 --- a/src-slider_io/src/output/config.rs +++ b/src-slider_io/src/output/config.rs @@ -40,6 +40,10 @@ pub enum OutputMode { polling: PollingRate, sensitivity: u8, }, + Hori { + polling: PollingRate, + sensitivity: u8, + }, Websocket { url: String, polling: PollingRate, @@ -123,6 +127,10 @@ impl OutputMode { polling: PollingRate::from_str(v["outputPolling"].as_str()?)?, sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?, }, + "gamepad-hori" => OutputMode::Hori { + polling: PollingRate::from_str(v["outputPolling"].as_str()?)?, + sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?, + }, "websocket" => OutputMode::Websocket { url: v["outputWebsocketUrl"].as_str()?.to_string(), polling: PollingRate::from_str(v["outputPolling"].as_str()?)?, diff --git a/src-slider_io/src/output/hori.rs b/src-slider_io/src/output/hori.rs new file mode 100644 index 0000000..0061d21 --- /dev/null +++ b/src-slider_io/src/output/hori.rs @@ -0,0 +1,132 @@ +use log::error; +use std::error::Error; +use vigem_client::{Client, DS4Report, DualShock4Wired, TargetId}; + +use crate::shared::hori::HoriState; + +use super::output::OutputHandler; + +pub struct HoriOutput { + target: DualShock4Wired, + gamepad: DS4Report, +} + +impl HoriOutput { + pub fn new() -> Option { + let target = Self::get_target(); + + match target { + Ok(target) => Some(Self { + target, + gamepad: DS4Report::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 = DualShock4Wired::new(client, TargetId::DUALSHOCK4_WIRED); + 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 HoriOutput { + fn tick(&mut self, flat_input: &Vec) -> bool { + let hori_state = HoriState::from_flat(flat_input); + + let buttons: u16 = hori_state + .bt + .iter() + .zip([ + // https://github.com/ViGEm/ViGEmClient/blob/master/include/ViGEm/Common.h#L117 + 1 << 7, // triangle + 1 << 4, // square + 1 << 5, // cross + 1 << 6, // circle + ]) + .fold(0x8, |buttons, (state, code)| { + buttons + | match state { + true => code, + false => 0, + } + }); + + let axis: u32 = hori_state + .slider + .iter() + .enumerate() + .fold(0, |axis, (idx, state)| { + axis + | match state { + true => 0b11 << ((15 - idx) * 2), + false => 0, + } + }) + ^ 0x80808080; + + let mut dirty = false; + if self.gamepad.buttons != buttons { + self.gamepad.buttons = buttons; + dirty = true; + } + + for (idx, state) in [ + &mut self.gamepad.thumb_lx, + &mut self.gamepad.thumb_ly, + &mut self.gamepad.thumb_rx, + &mut self.gamepad.thumb_ry, + ] + .into_iter() + .enumerate() + { + let slice: u8 = ((axis >> ((3 - idx) * 8)) & 0xff) as u8; + if *state != slice { + *state = slice; + dirty = true; + } + } + + match dirty { + true => self.update(), + false => true, + } + } + + fn reset(&mut self) { + self.gamepad = DS4Report::default(); + self.update(); + } +} + +impl Drop for HoriOutput { + fn drop(&mut self) { + match self.target.unplug() { + Ok(_) => {} + Err(e) => { + error!("Gamepad unplug error: {}", e); + } + } + } +} + +// dammit vigem_client::Event +unsafe impl Send for HoriOutput {} diff --git a/src-slider_io/src/output/mod.rs b/src-slider_io/src/output/mod.rs index 9457b8e..5619a4d 100644 --- a/src-slider_io/src/output/mod.rs +++ b/src-slider_io/src/output/mod.rs @@ -1,6 +1,7 @@ pub mod config; mod gamepad; +mod hori; mod keyboard; pub mod output; diff --git a/src-slider_io/src/output/output.rs b/src-slider_io/src/output/output.rs index 0a59c3c..8868df2 100644 --- a/src-slider_io/src/output/output.rs +++ b/src-slider_io/src/output/output.rs @@ -5,7 +5,9 @@ use tokio::time::{interval, Interval}; use crate::{shared::worker::AsyncJob, state::SliderState}; -use super::{config::OutputMode, gamepad::GamepadOutput, keyboard::KeyboardOutput}; +use super::{ + config::OutputMode, gamepad::GamepadOutput, hori::HoriOutput, keyboard::KeyboardOutput, +}; pub trait OutputHandler: Send { fn tick(&mut self, flat_input: &Vec) -> bool; @@ -64,6 +66,22 @@ impl AsyncJob for OutputJob { None => false, } } + OutputMode::Hori { + polling, + sensitivity, + } => { + self.sensitivity = sensitivity; + let handler = HoriOutput::new(); + self.timer = interval(Duration::from_micros(polling.to_t_u64())); + + match handler { + Some(handler) => { + self.handler = Some(Box::new(handler)); + true + } + None => false, + } + } _ => { error!("Not implemented"); false diff --git a/src-slider_io/src/shared/hori.rs b/src-slider_io/src/shared/hori.rs new file mode 100644 index 0000000..df2467e --- /dev/null +++ b/src-slider_io/src/shared/hori.rs @@ -0,0 +1,27 @@ +pub struct HoriState { + pub slider: [bool; 16], + pub bt: [bool; 4], +} + +impl HoriState { + pub fn from_flat(flat_input: &Vec) -> Self { + let mut hori_state = Self { + slider: [false; 16], + bt: [false; 4], + }; + + for (idx, i) in flat_input[0..32].iter().enumerate() { + match idx % 2 { + 0 => { + hori_state.bt[idx / 8] |= *i; + } + 1 => { + hori_state.slider[idx / 2] |= *i; + } + _ => unreachable!(), + } + } + + hori_state + } +} diff --git a/src-slider_io/src/shared/mod.rs b/src-slider_io/src/shared/mod.rs index 3f68fc7..4112c1e 100644 --- a/src-slider_io/src/shared/mod.rs +++ b/src-slider_io/src/shared/mod.rs @@ -1,3 +1,4 @@ +pub mod hori; pub mod serial; pub mod utils; pub mod voltex; diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 16e9583..969125c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -3753,9 +3753,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vigem-client" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965e349c8ec4eb36c06878b99952f35b9f459e6912419837ecb85fb5502a6de3" +checksum = "5d886912e88b1d3b02a97d933df7829db8fb8561920410805794a48680c212d5" dependencies = [ "winapi", ] diff --git a/src/App.svelte b/src/App.svelte index 808369e..b85d405 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -304,6 +304,9 @@ + @@ -315,11 +318,11 @@ 32 key layout is recommended for Brokestalgia controllers - {:else if deviceMode.slice(0, 10) === "brokenithm" && ["kb-voltex", "kb-neardayo", "gamepad-voltex", "gamepad-neardayo"].includes(outputMode)} + {:else if deviceMode.slice(0, 10) === "brokenithm" && ["kb-voltex", "kb-neardayo", "gamepad-voltex", "gamepad-neardayo", "gamepad-hori"].includes(outputMode)}
- Voltex-like layouts are not recommended for Brokenithm controllers + Gamepad layouts are not recommended for Brokenithm controllers
{/if} @@ -401,6 +404,9 @@ +