mirror of
https://github.com/4yn/slidershim.git
synced 2024-11-30 16:24:27 +01:00
add ds4 hori slider
This commit is contained in:
parent
d4d8d8be09
commit
62551a2f88
4
src-slider_io/Cargo.lock
generated
4
src-slider_io/Cargo.lock
generated
@ -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",
|
||||||
]
|
]
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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()?,
|
||||||
},
|
},
|
||||||
|
@ -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)
|
||||||
|
@ -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()?)?,
|
||||||
|
132
src-slider_io/src/output/hori.rs
Normal file
132
src-slider_io/src/output/hori.rs
Normal 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 {}
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
27
src-slider_io/src/shared/hori.rs
Normal file
27
src-slider_io/src/shared/hori.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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
4
src-tauri/Cargo.lock
generated
@ -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",
|
||||||
]
|
]
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user