1
0
mirror of https://github.com/4yn/slidershim.git synced 2024-11-27 23:10:49 +01:00

add ds4 hori slider

This commit is contained in:
4yn 2022-04-03 03:11:37 +08:00
parent d4d8d8be09
commit 62551a2f88
12 changed files with 251 additions and 9 deletions

View File

@ -1587,9 +1587,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "vigem-client" name = "vigem-client"
version = "0.1.1" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "965e349c8ec4eb36c06878b99952f35b9f459e6912419837ecb85fb5502a6de3" checksum = "5d886912e88b1d3b02a97d933df7829db8fb8561920410805794a48680c212d5"
dependencies = [ dependencies = [
"winapi", "winapi",
] ]

View File

@ -29,7 +29,7 @@ image = "0.23.14"
rusb = "0.9.0" rusb = "0.9.0"
serialport = "4.0.1" serialport = "4.0.1"
wwserial = {path = "../src-wwserial" } wwserial = {path = "../src-wwserial" }
vigem-client = "0.1.1" vigem-client = { version = "0.1.2", features = ["unstable"] }
winapi = "0.3.9" winapi = "0.3.9"
ipconfig = "0.3.0" ipconfig = "0.3.0"

View File

@ -5,6 +5,7 @@ pub enum ReactiveLayout {
Even { splits: usize }, Even { splits: usize },
Six, Six,
Voltex, Voltex,
Hori,
Rainbow, Rainbow,
} }
@ -109,6 +110,12 @@ impl LightsMode {
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?, sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
color: ColorScheme::default(), 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 { "attract" => LightsMode::Attract {
faster: v["ledFaster"].as_bool()?, faster: v["ledFaster"].as_bool()?,
}, },

View File

@ -9,7 +9,7 @@ use std::{
use tokio::time::{interval, Interval}; use tokio::time::{interval, Interval};
use crate::{ use crate::{
shared::{utils::Buffer, voltex::VoltexState, worker::AsyncJob}, shared::{hori::HoriState, utils::Buffer, voltex::VoltexState, worker::AsyncJob},
state::{SliderLights, SliderState}, 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 => { ReactiveLayout::Rainbow => {
let banks: Vec<bool> = flat_input let banks: Vec<bool> = flat_input
.chunks(2) .chunks(2)

View File

@ -40,6 +40,10 @@ pub enum OutputMode {
polling: PollingRate, polling: PollingRate,
sensitivity: u8, sensitivity: u8,
}, },
Hori {
polling: PollingRate,
sensitivity: u8,
},
Websocket { Websocket {
url: String, url: String,
polling: PollingRate, polling: PollingRate,
@ -123,6 +127,10 @@ impl OutputMode {
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?, polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?, 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 { "websocket" => OutputMode::Websocket {
url: v["outputWebsocketUrl"].as_str()?.to_string(), url: v["outputWebsocketUrl"].as_str()?.to_string(),
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?, polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,

View File

@ -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<Client>,
gamepad: DS4Report,
}
impl HoriOutput {
pub fn new() -> Option<Self> {
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<DualShock4Wired<Client>, Box<dyn Error>> {
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>) -> 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 {}

View File

@ -1,6 +1,7 @@
pub mod config; pub mod config;
mod gamepad; mod gamepad;
mod hori;
mod keyboard; mod keyboard;
pub mod output; pub mod output;

View File

@ -5,7 +5,9 @@ use tokio::time::{interval, Interval};
use crate::{shared::worker::AsyncJob, state::SliderState}; 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 { pub trait OutputHandler: Send {
fn tick(&mut self, flat_input: &Vec<bool>) -> bool; fn tick(&mut self, flat_input: &Vec<bool>) -> bool;
@ -64,6 +66,22 @@ impl AsyncJob for OutputJob {
None => false, 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"); error!("Not implemented");
false false

View File

@ -0,0 +1,27 @@
pub struct HoriState {
pub slider: [bool; 16],
pub bt: [bool; 4],
}
impl HoriState {
pub fn from_flat(flat_input: &Vec<bool>) -> 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
}
}

View File

@ -1,3 +1,4 @@
pub mod hori;
pub mod serial; pub mod serial;
pub mod utils; pub mod utils;
pub mod voltex; pub mod voltex;

4
src-tauri/Cargo.lock generated
View File

@ -3753,9 +3753,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "vigem-client" name = "vigem-client"
version = "0.1.1" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "965e349c8ec4eb36c06878b99952f35b9f459e6912419837ecb85fb5502a6de3" checksum = "5d886912e88b1d3b02a97d933df7829db8fb8561920410805794a48680c212d5"
dependencies = [ dependencies = [
"winapi", "winapi",
] ]

View File

@ -304,6 +304,9 @@
<option value="gamepad-neardayo" <option value="gamepad-neardayo"
>XBOX 360 Gamepad, Neardayo Layout</option >XBOX 360 Gamepad, Neardayo Layout</option
> >
<option value="gamepad-hori"
>DS4, HORI DIVA Future Tone ASC Layout</option
>
<!-- <option value="websocket">Websocket</option> --> <!-- <option value="websocket">Websocket</option> -->
</select> </select>
</div> </div>
@ -315,11 +318,11 @@
32 key layout is recommended for Brokestalgia controllers 32 key layout is recommended for Brokestalgia controllers
</div> </div>
</div> </div>
{: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)}
<div class="row"> <div class="row">
<div class="label" /> <div class="label" />
<div class="input comment"> <div class="input comment">
Voltex-like layouts are not recommended for Brokenithm controllers Gamepad layouts are not recommended for Brokenithm controllers
</div> </div>
</div> </div>
{/if} {/if}
@ -401,6 +404,9 @@
<option value="reactive-4">Reactive, 4-Zone</option> <option value="reactive-4">Reactive, 4-Zone</option>
<option value="reactive-rainbow">Reactive, 16-Zone Rainbow</option> <option value="reactive-rainbow">Reactive, 16-Zone Rainbow</option>
<option value="reactive-voltex">Reactive, Voltex Layout</option> <option value="reactive-voltex">Reactive, Voltex Layout</option>
<option value="reactive-hori"
>Reactive, DIVA Future Tone Layout</option
>
<option value="attract">Rainbow Attract Mode</option> <option value="attract">Rainbow Attract Mode</option>
<!-- <option value="websocket">Websocket</option> --> <!-- <option value="websocket">Websocket</option> -->
<option value="serial">Serial</option> <option value="serial">Serial</option>