mirror of
https://github.com/4yn/slidershim.git
synced 2024-12-18 07:05:52 +01:00
async output and led
This commit is contained in:
parent
afa5a46c65
commit
2ae7a81f22
80
src-tauri/Cargo.lock
generated
80
src-tauri/Cargo.lock
generated
@ -1666,9 +1666,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
@ -2132,7 +2132,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
"parking_lot_core 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2150,10 +2160,17 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "path-clean"
|
||||
version = "0.1.0"
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd"
|
||||
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.2.10",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
@ -2939,7 +2956,7 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
|
||||
|
||||
[[package]]
|
||||
name = "slidershim"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"atomic_float",
|
||||
@ -2954,7 +2971,7 @@ dependencies = [
|
||||
"log",
|
||||
"open",
|
||||
"palette",
|
||||
"path-clean",
|
||||
"parking_lot 0.12.0",
|
||||
"phf 0.10.1",
|
||||
"qrcode",
|
||||
"rusb",
|
||||
@ -3037,7 +3054,7 @@ checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"phf_shared 0.8.0",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
@ -3177,7 +3194,7 @@ dependencies = [
|
||||
"ndk-glue",
|
||||
"ndk-sys",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"raw-window-handle 0.3.4",
|
||||
"scopeguard",
|
||||
"serde",
|
||||
@ -3971,6 +3988,49 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.7.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "slidershim"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
description = "slidershim"
|
||||
authors = ["4yn"]
|
||||
license = ""
|
||||
@ -9,45 +9,54 @@ default-run = "slidershim"
|
||||
edition = "2018"
|
||||
build = "src/build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.0.0-beta.4" }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
# logging
|
||||
log = "0.4.14"
|
||||
env_logger = "0.9.0"
|
||||
simple-logging = "2.0.2"
|
||||
open = "2.0.2"
|
||||
|
||||
# threads
|
||||
parking_lot = "0.12.0"
|
||||
atomic_float = "0.1.0"
|
||||
spin_sleep = "1.0.0"
|
||||
tauri = { version = "1.0.0-beta.8", features = ["shell-open", "system-tray"] }
|
||||
|
||||
# async
|
||||
futures = "0.3.19"
|
||||
futures-util = "0.3.19"
|
||||
async-trait = "0.1.52"
|
||||
tokio = { version="1.16.1", features= ["rt-multi-thread","macros"] }
|
||||
tokio-util = "0.6.9"
|
||||
|
||||
# UI
|
||||
tauri = { version = "1.0.0-beta.8", features = ["shell-open", "system-tray"] }
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
open = "2.0.2"
|
||||
directories = "4.0.1"
|
||||
image = "0.23.14"
|
||||
|
||||
# device and system
|
||||
rusb = "0.9.0"
|
||||
serialport = "4.0.1"
|
||||
vigem-client = "0.1.1"
|
||||
palette = "0.6.0"
|
||||
winapi = "0.3.9"
|
||||
ipconfig = "0.3.0"
|
||||
|
||||
# webserver
|
||||
hyper = { version="0.14.16", features= ["server", "http1", "http2", "tcp", "stream", "runtime"] }
|
||||
phf = { version = "0.10.1", features = ["macros"] }
|
||||
base64 = "0.13.0"
|
||||
image = "0.23.14"
|
||||
qrcode = { version="0.12.0", features= ["image"] }
|
||||
path-clean = "0.1.0"
|
||||
tungstenite = { version="0.16.0", default-features=false }
|
||||
tokio-tungstenite = "0.16.1"
|
||||
|
||||
# webserver utils
|
||||
base64 = "0.13.0"
|
||||
palette = "0.6.0"
|
||||
qrcode = { version="0.12.0", features= ["image"] }
|
||||
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
|
@ -7,7 +7,8 @@
|
||||
|
||||
mod slider_io;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::info;
|
||||
|
||||
@ -46,10 +47,10 @@ fn main() {
|
||||
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();
|
||||
let config_handle = config.lock();
|
||||
let config_handle_ref = config_handle.as_ref().unwrap();
|
||||
config_handle_ref.save();
|
||||
let manager_handle = manager.lock().unwrap();
|
||||
let manager_handle = manager.lock();
|
||||
manager_handle.update_config(config_handle_ref.clone());
|
||||
}
|
||||
|
||||
@ -119,7 +120,7 @@ fn main() {
|
||||
let app_handle = app.handle();
|
||||
let config_clone = Arc::clone(&config);
|
||||
app.listen_global("ready", move |_| {
|
||||
let config_handle = config_clone.lock().unwrap();
|
||||
let config_handle = config_clone.lock();
|
||||
info!("Start signal received");
|
||||
app_handle
|
||||
.emit_all(
|
||||
@ -140,7 +141,7 @@ fn main() {
|
||||
app.listen_global("queryState", move |_| {
|
||||
// app_handle.emit_all("showState", "@@@");
|
||||
let (snapshot, timer) = {
|
||||
let manager_handle = manager_clone.lock().unwrap();
|
||||
let manager_handle = manager_clone.lock();
|
||||
(
|
||||
manager_handle.try_get_state().map(|x| x.snapshot()),
|
||||
manager_handle.get_timer_state(),
|
||||
@ -163,12 +164,12 @@ fn main() {
|
||||
let payload = event.payload().unwrap();
|
||||
info!("Config applied {}", payload);
|
||||
if let Some(new_config) = slider_io::Config::from_str(payload) {
|
||||
let mut config_handle = config_clone.lock().unwrap();
|
||||
let mut config_handle = config_clone.lock();
|
||||
config_handle.take();
|
||||
config_handle.replace(new_config);
|
||||
let config_handle_ref = config_handle.as_ref().unwrap();
|
||||
config_handle_ref.save();
|
||||
let manager_handle = manager_clone.lock().unwrap();
|
||||
let manager_handle = manager_clone.lock();
|
||||
manager_handle.update_config(config_handle_ref.clone());
|
||||
}
|
||||
});
|
||||
|
@ -18,7 +18,7 @@ use tokio::{
|
||||
use tokio_tungstenite::WebSocketStream;
|
||||
use tungstenite::{handshake, Message};
|
||||
|
||||
use crate::slider_io::{controller_state::FullState, worker::AsyncJob};
|
||||
use crate::slider_io::{controller_state::FullState, worker::AsyncHaltableJob};
|
||||
|
||||
// https://levelup.gitconnected.com/handling-websocket-and-http-on-the-same-port-with-rust-f65b770722c9
|
||||
|
||||
@ -114,7 +114,7 @@ async fn handle_brokenithm(
|
||||
}
|
||||
39 => {
|
||||
if chars[0] == 'b' {
|
||||
let mut controller_state_handle = state_handle.controller_state.lock().unwrap();
|
||||
let mut controller_state_handle = state_handle.controller_state.lock();
|
||||
for (idx, c) in chars[0..32].iter().enumerate() {
|
||||
controller_state_handle.ground_state[idx] = match *c == '1' {
|
||||
false => 0,
|
||||
@ -167,7 +167,7 @@ async fn handle_brokenithm(
|
||||
loop {
|
||||
let mut led_data = vec![0; 93];
|
||||
{
|
||||
let led_state_handle = state_handle.led_state.lock().unwrap();
|
||||
let led_state_handle = state_handle.led_state.lock();
|
||||
(&mut led_data).copy_from_slice(&led_state_handle.led_state);
|
||||
}
|
||||
msg_write_handle.send(Message::Binary(led_data)).ok();
|
||||
@ -273,7 +273,7 @@ impl BrokenithmJob {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AsyncJob for BrokenithmJob {
|
||||
impl AsyncHaltableJob for BrokenithmJob {
|
||||
async fn run<F: Future<Output = ()> + Send>(self, stop_signal: F) {
|
||||
let state = self.state.clone();
|
||||
let ground_only = self.ground_only;
|
||||
|
@ -8,11 +8,18 @@ use std::{convert::TryFrom, fs, path::PathBuf};
|
||||
use crate::slider_io::utils::list_ips;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DeviceMode {
|
||||
None,
|
||||
pub enum HardwareSpec {
|
||||
TasollerOne,
|
||||
TasollerTwo,
|
||||
Yuancon,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DeviceMode {
|
||||
None,
|
||||
Hardware {
|
||||
spec: HardwareSpec,
|
||||
},
|
||||
Brokenithm {
|
||||
ground_only: bool,
|
||||
led_enabled: bool,
|
||||
@ -20,7 +27,7 @@ pub enum DeviceMode {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum OutputPolling {
|
||||
pub enum PollingRate {
|
||||
Sixty,
|
||||
Hundred,
|
||||
TwoHundredFifty,
|
||||
@ -28,35 +35,13 @@ pub enum OutputPolling {
|
||||
Thousand,
|
||||
}
|
||||
|
||||
impl OutputPolling {
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"60" => Some(OutputPolling::Sixty),
|
||||
"100" => Some(OutputPolling::Hundred),
|
||||
"250" => Some(OutputPolling::TwoHundredFifty),
|
||||
"500" => Some(OutputPolling::FiveHundred),
|
||||
"1000" => Some(OutputPolling::Thousand),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_t_u64(&self) -> u64 {
|
||||
match self {
|
||||
OutputPolling::Sixty => 16666,
|
||||
OutputPolling::Hundred => 10000,
|
||||
OutputPolling::TwoHundredFifty => 4000,
|
||||
OutputPolling::FiveHundred => 2000,
|
||||
OutputPolling::Thousand => 1000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KeyboardLayout {
|
||||
Tasoller,
|
||||
Yuancon,
|
||||
Deemo,
|
||||
Voltex,
|
||||
Neardayo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -65,22 +50,45 @@ pub enum GamepadLayout {
|
||||
Neardayo,
|
||||
}
|
||||
|
||||
impl PollingRate {
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"60" => Some(PollingRate::Sixty),
|
||||
"100" => Some(PollingRate::Hundred),
|
||||
"250" => Some(PollingRate::TwoHundredFifty),
|
||||
"500" => Some(PollingRate::FiveHundred),
|
||||
"1000" => Some(PollingRate::Thousand),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_t_u64(&self) -> u64 {
|
||||
match self {
|
||||
PollingRate::Sixty => 16666,
|
||||
PollingRate::Hundred => 10000,
|
||||
PollingRate::TwoHundredFifty => 4000,
|
||||
PollingRate::FiveHundred => 2000,
|
||||
PollingRate::Thousand => 1000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OutputMode {
|
||||
None,
|
||||
Keyboard {
|
||||
layout: KeyboardLayout,
|
||||
polling: OutputPolling,
|
||||
polling: PollingRate,
|
||||
sensitivity: u8,
|
||||
},
|
||||
Gamepad {
|
||||
layout: GamepadLayout,
|
||||
polling: OutputPolling,
|
||||
polling: PollingRate,
|
||||
sensitivity: u8,
|
||||
},
|
||||
Websocket {
|
||||
url: String,
|
||||
polling: OutputPolling,
|
||||
polling: PollingRate,
|
||||
},
|
||||
}
|
||||
|
||||
@ -123,9 +131,15 @@ impl Config {
|
||||
raw: s.to_string(),
|
||||
device_mode: match v["deviceMode"].as_str()? {
|
||||
"none" => DeviceMode::None,
|
||||
"tasoller-one" => DeviceMode::TasollerOne,
|
||||
"tasoller-two" => DeviceMode::TasollerTwo,
|
||||
"yuancon" => DeviceMode::Yuancon,
|
||||
"tasoller-one" => DeviceMode::Hardware {
|
||||
spec: HardwareSpec::TasollerOne,
|
||||
},
|
||||
"tasoller-two" => DeviceMode::Hardware {
|
||||
spec: HardwareSpec::TasollerTwo,
|
||||
},
|
||||
"yuancon" => DeviceMode::Hardware {
|
||||
spec: HardwareSpec::Yuancon,
|
||||
},
|
||||
"brokenithm" => DeviceMode::Brokenithm {
|
||||
ground_only: false,
|
||||
led_enabled: false,
|
||||
@ -148,37 +162,42 @@ impl Config {
|
||||
"none" => OutputMode::None,
|
||||
"kb-32-tasoller" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Tasoller,
|
||||
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
},
|
||||
"kb-32-yuancon" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Yuancon,
|
||||
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
},
|
||||
"kb-8-deemo" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Deemo,
|
||||
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
},
|
||||
"kb-voltex" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Voltex,
|
||||
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
},
|
||||
"kb-neardayo" => OutputMode::Keyboard {
|
||||
layout: KeyboardLayout::Neardayo,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
},
|
||||
"gamepad-voltex" => OutputMode::Gamepad {
|
||||
layout: GamepadLayout::Voltex,
|
||||
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
||||
},
|
||||
"gamepad-neardayo" => OutputMode::Gamepad {
|
||||
layout: GamepadLayout::Neardayo,
|
||||
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
||||
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: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
||||
polling: PollingRate::from_str(v["outputPolling"].as_str()?)?,
|
||||
},
|
||||
_ => panic!("Invalid output mode"),
|
||||
},
|
||||
@ -259,11 +278,12 @@ impl Config {
|
||||
Self::from_str(
|
||||
r#"{
|
||||
"deviceMode": "none",
|
||||
"devicePolling": "100",
|
||||
"outputMode": "none",
|
||||
"ledMode": "none",
|
||||
"keyboardSensitivity": 20,
|
||||
"outputWebsocketUrl": "localhost:3000",
|
||||
"outputPolling": "60",
|
||||
"outputPolling": "100",
|
||||
"ledSensitivity": 20,
|
||||
"ledWebsocketUrl": "localhost:3001",
|
||||
"ledSerialPort": "COM5"
|
||||
|
@ -10,7 +10,7 @@ use crate::slider_io::{
|
||||
led::LedJob,
|
||||
output::OutputJob,
|
||||
utils::LoopTimer,
|
||||
worker::{AsyncWorker, ThreadWorker},
|
||||
worker::{AsyncHaltableWorker, AsyncWorker, ThreadWorker},
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -18,9 +18,9 @@ pub struct Context {
|
||||
state: FullState,
|
||||
config: Config,
|
||||
device_worker: Option<ThreadWorker>,
|
||||
brokenithm_worker: Option<AsyncWorker>,
|
||||
output_worker: Option<ThreadWorker>,
|
||||
led_worker: Option<ThreadWorker>,
|
||||
brokenithm_worker: Option<AsyncHaltableWorker>,
|
||||
output_worker: Option<AsyncWorker>,
|
||||
led_worker: Option<AsyncWorker>,
|
||||
timers: Vec<(&'static str, Arc<AtomicF64>)>,
|
||||
}
|
||||
|
||||
@ -41,18 +41,18 @@ impl Context {
|
||||
led_enabled,
|
||||
} => (
|
||||
None,
|
||||
Some(AsyncWorker::new(
|
||||
Some(AsyncHaltableWorker::new(
|
||||
"brokenithm",
|
||||
BrokenithmJob::new(&state, ground_only, led_enabled),
|
||||
)),
|
||||
),
|
||||
_ => (
|
||||
DeviceMode::Hardware { spec } => (
|
||||
{
|
||||
let timer = LoopTimer::new();
|
||||
timers.push(("d", timer.fork()));
|
||||
Some(ThreadWorker::new(
|
||||
"device",
|
||||
HidDeviceJob::from_config(&state, &config.device_mode),
|
||||
HidDeviceJob::from_config(&state, spec),
|
||||
timer,
|
||||
))
|
||||
},
|
||||
@ -64,7 +64,7 @@ impl Context {
|
||||
_ => {
|
||||
let timer = LoopTimer::new();
|
||||
timers.push(("o", timer.fork()));
|
||||
Some(ThreadWorker::new(
|
||||
Some(AsyncWorker::new(
|
||||
"output",
|
||||
OutputJob::new(&state, &config.output_mode),
|
||||
timer,
|
||||
@ -76,7 +76,7 @@ impl Context {
|
||||
_ => {
|
||||
let timer = LoopTimer::new();
|
||||
timers.push(("l", timer.fork()));
|
||||
Some(ThreadWorker::new(
|
||||
Some(AsyncWorker::new(
|
||||
"led",
|
||||
LedJob::new(&state, &config.led_mode),
|
||||
timer,
|
||||
|
@ -1,7 +1,5 @@
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
time::Instant,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
pub struct ControllerState {
|
||||
pub ground_state: [u8; 32],
|
||||
@ -84,13 +82,13 @@ impl FullState {
|
||||
pub fn snapshot(&self) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = vec![];
|
||||
{
|
||||
let controller_state_handle = self.controller_state.lock().unwrap();
|
||||
let controller_state_handle = self.controller_state.lock();
|
||||
buf.extend(controller_state_handle.ground_state);
|
||||
buf.extend(controller_state_handle.air_state);
|
||||
buf.extend(controller_state_handle.extra_state);
|
||||
};
|
||||
{
|
||||
let led_state_handle = self.led_state.lock().unwrap();
|
||||
let led_state_handle = self.led_state.lock();
|
||||
buf.extend(led_state_handle.led_state);
|
||||
};
|
||||
|
||||
|
@ -2,12 +2,13 @@ use log::{error, info};
|
||||
use rusb::{self, DeviceHandle, GlobalContext};
|
||||
use std::{
|
||||
error::Error,
|
||||
mem::swap,
|
||||
ops::{Deref, DerefMut},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::slider_io::{
|
||||
config::DeviceMode,
|
||||
config::HardwareSpec,
|
||||
controller_state::{ControllerState, FullState, LedState},
|
||||
utils::{Buffer, ShimError},
|
||||
worker::ThreadJob,
|
||||
@ -23,6 +24,7 @@ enum WriteType {
|
||||
|
||||
pub struct HidDeviceJob {
|
||||
state: FullState,
|
||||
|
||||
vid: u16,
|
||||
pid: u16,
|
||||
read_endpoint: u8,
|
||||
@ -30,6 +32,7 @@ pub struct HidDeviceJob {
|
||||
|
||||
read_callback: HidReadCallback,
|
||||
read_buf: Buffer,
|
||||
last_read_buf: Buffer,
|
||||
|
||||
led_write_type: WriteType,
|
||||
led_callback: HidLedCallback,
|
||||
@ -57,6 +60,7 @@ impl HidDeviceJob {
|
||||
led_endpoint,
|
||||
read_callback,
|
||||
read_buf: Buffer::new(),
|
||||
last_read_buf: Buffer::new(),
|
||||
led_write_type: led_type,
|
||||
led_callback,
|
||||
led_buf: Buffer::new(),
|
||||
@ -64,9 +68,9 @@ impl HidDeviceJob {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_config(state: &FullState, mode: &DeviceMode) -> Self {
|
||||
match mode {
|
||||
DeviceMode::TasollerOne => Self::new(
|
||||
pub fn from_config(state: &FullState, spec: &HardwareSpec) -> Self {
|
||||
match spec {
|
||||
HardwareSpec::TasollerOne => Self::new(
|
||||
state.clone(),
|
||||
0x1ccf,
|
||||
0x2333,
|
||||
@ -108,7 +112,7 @@ impl HidDeviceJob {
|
||||
buf.data[96..240].fill(0);
|
||||
},
|
||||
),
|
||||
DeviceMode::TasollerTwo => Self::new(
|
||||
HardwareSpec::TasollerTwo => Self::new(
|
||||
state.clone(),
|
||||
0x1ccf,
|
||||
0x2333,
|
||||
@ -146,7 +150,7 @@ impl HidDeviceJob {
|
||||
buf.data[96..240].fill(0);
|
||||
},
|
||||
),
|
||||
DeviceMode::Yuancon => Self::new(
|
||||
HardwareSpec::Yuancon => Self::new(
|
||||
state.clone(),
|
||||
0x1973,
|
||||
0x2001,
|
||||
@ -164,7 +168,7 @@ impl HidDeviceJob {
|
||||
controller_state.air_state[i ^ 1] = (buf.data[0] >> i) & 1;
|
||||
}
|
||||
for i in 0..3 {
|
||||
controller_state.extra_state[i] = (buf.data[1] >> i) & 1;
|
||||
controller_state.extra_state[2 - i] = (buf.data[1] >> i) & 1;
|
||||
}
|
||||
},
|
||||
WriteType::Interrupt,
|
||||
@ -181,7 +185,6 @@ impl HidDeviceJob {
|
||||
}
|
||||
},
|
||||
),
|
||||
_ => panic!("Not implemented"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,17 +242,19 @@ impl ThreadJob for HidDeviceJob {
|
||||
.unwrap_or(0);
|
||||
self.read_buf.len = res;
|
||||
// debug!("{:?}", self.read_buf.slice());
|
||||
if self.read_buf.len != 0 {
|
||||
// if self.read_buf.len != 0 {
|
||||
if (self.read_buf.len != 0) && (self.read_buf.slice() != self.last_read_buf.slice()) {
|
||||
work = true;
|
||||
let mut controller_state_handle = self.state.controller_state.lock().unwrap();
|
||||
let mut controller_state_handle = self.state.controller_state.lock();
|
||||
(self.read_callback)(&self.read_buf, controller_state_handle.deref_mut());
|
||||
swap(&mut self.read_buf, &mut self.last_read_buf);
|
||||
}
|
||||
}
|
||||
|
||||
// Led loop
|
||||
{
|
||||
{
|
||||
let mut led_state_handle = self.state.led_state.lock().unwrap();
|
||||
let mut led_state_handle = self.state.led_state.lock();
|
||||
if led_state_handle.dirty {
|
||||
(self.led_callback)(&mut self.led_buf, led_state_handle.deref());
|
||||
led_state_handle.dirty = false;
|
||||
@ -269,7 +274,7 @@ impl ThreadJob for HidDeviceJob {
|
||||
})
|
||||
.unwrap_or(0);
|
||||
if res == self.led_buf.len + 1 {
|
||||
work = true;
|
||||
// work = true;
|
||||
self.led_buf.len = 0;
|
||||
}
|
||||
}
|
||||
@ -277,8 +282,10 @@ impl ThreadJob for HidDeviceJob {
|
||||
|
||||
work
|
||||
}
|
||||
}
|
||||
|
||||
fn teardown(&mut self) {
|
||||
impl Drop for HidDeviceJob {
|
||||
fn drop(&mut self) {
|
||||
if let Some(handle) = self.handle.as_mut() {
|
||||
handle.release_interface(0).ok();
|
||||
}
|
||||
|
@ -4,10 +4,48 @@ use vigem_client::{Client, TargetId, XButtons, XGamepad, Xbox360Wired};
|
||||
|
||||
use crate::slider_io::{config::GamepadLayout, output::OutputHandler, voltex::VoltexState};
|
||||
|
||||
struct LastWind {
|
||||
left: bool,
|
||||
right: bool,
|
||||
out: i16,
|
||||
}
|
||||
|
||||
impl LastWind {
|
||||
fn new() -> Self {
|
||||
LastWind {
|
||||
left: false,
|
||||
right: false,
|
||||
out: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, left: bool, right: bool) -> i16 {
|
||||
let out = match (left, right) {
|
||||
(false, false) => 0,
|
||||
(true, false) => -1,
|
||||
(false, true) => 1,
|
||||
(true, true) => match (self.left, self.right) {
|
||||
(false, false) => 0,
|
||||
(true, false) => 1,
|
||||
(false, true) => -1,
|
||||
(true, true) => self.out,
|
||||
},
|
||||
};
|
||||
|
||||
self.left = left;
|
||||
self.right = right;
|
||||
self.out = out;
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GamepadOutput {
|
||||
target: Xbox360Wired<Client>,
|
||||
use_air: bool,
|
||||
gamepad: XGamepad,
|
||||
left_wind: LastWind,
|
||||
right_wind: LastWind,
|
||||
}
|
||||
|
||||
impl GamepadOutput {
|
||||
@ -23,6 +61,8 @@ impl GamepadOutput {
|
||||
target,
|
||||
use_air,
|
||||
gamepad: XGamepad::default(),
|
||||
left_wind: LastWind::new(),
|
||||
right_wind: LastWind::new(),
|
||||
}),
|
||||
Err(e) => {
|
||||
error!("Gamepad connection error: {}", e);
|
||||
@ -80,21 +120,17 @@ impl OutputHandler for GamepadOutput {
|
||||
}
|
||||
});
|
||||
|
||||
let lx = (match voltex_state.laser[0] || (self.use_air && flat_controller_state[34]) {
|
||||
true => -30000,
|
||||
false => 0,
|
||||
} + match voltex_state.laser[1] || (self.use_air && flat_controller_state[35]) {
|
||||
true => 30000,
|
||||
false => 0,
|
||||
});
|
||||
let lx = self.left_wind.update(
|
||||
voltex_state.laser[0] || (self.use_air && flat_controller_state[32]),
|
||||
voltex_state.laser[1]
|
||||
|| (self.use_air && (flat_controller_state[33] || flat_controller_state[34])),
|
||||
) * 20000;
|
||||
|
||||
let rx = (match voltex_state.laser[2] || (self.use_air && flat_controller_state[36]) {
|
||||
true => -30000,
|
||||
false => 0,
|
||||
} + match voltex_state.laser[3] || (self.use_air && flat_controller_state[37]) {
|
||||
true => 30000,
|
||||
false => 0,
|
||||
});
|
||||
let rx = self.right_wind.update(
|
||||
voltex_state.laser[2]
|
||||
|| (self.use_air && (flat_controller_state[35] || flat_controller_state[36])),
|
||||
voltex_state.laser[3] || (self.use_air && flat_controller_state[37]),
|
||||
) * 20000;
|
||||
|
||||
let mut dirty = false;
|
||||
if self.gamepad.buttons.raw != buttons {
|
||||
|
@ -60,6 +60,24 @@ const VOLTEX_KB_MAP: [usize; 41] = [
|
||||
0x31, 0x1b, 0x0d, // 1, VK_ESCAPE, VK_RETURN
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
const VOLTEX_KB_MAP_NEARDAYO: [usize; 41] = [
|
||||
0x57, 0x57, 0x57, 0x57, // W
|
||||
0x45, 0x45, 0x45, 0x45, // E
|
||||
0x43, 0x44,
|
||||
0x43, 0x44,
|
||||
0x43, 0x46, // D
|
||||
0x43, 0x46, // C // F
|
||||
0x4d, 0x4a, // M // J
|
||||
0x4d, 0x4a, // K
|
||||
0x4d, 0x4b,
|
||||
0x4d, 0x4b,
|
||||
0x4f, 0x4f, 0x4f, 0x4f, // O
|
||||
0x50, 0x50, 0x50, 0x50, // P
|
||||
0x57, 0x45, 0x45, 0x4f, 0x4f, 0x50, // Disabled
|
||||
0x31, 0x1b, 0x0d, // 1, VK_ESCAPE, VK_RETURN
|
||||
];
|
||||
|
||||
pub struct KeyboardOutput {
|
||||
ground_to_idx: [usize; 41],
|
||||
idx_to_keycode: [u16; 41],
|
||||
@ -78,6 +96,7 @@ impl KeyboardOutput {
|
||||
KeyboardLayout::Yuancon => &YUANCON_KB_MAP,
|
||||
KeyboardLayout::Deemo => &DEEMO_KB_MAP,
|
||||
KeyboardLayout::Voltex => &VOLTEX_KB_MAP,
|
||||
KeyboardLayout::Neardayo => &VOLTEX_KB_MAP_NEARDAYO,
|
||||
};
|
||||
|
||||
let mut ground_to_idx = [0 as usize; 41];
|
||||
|
@ -1,3 +1,4 @@
|
||||
use async_trait::async_trait;
|
||||
use log::{error, info};
|
||||
use palette::{FromColor, Hsv, Srgb};
|
||||
use serialport::{ClearBuffer, SerialPort};
|
||||
@ -5,19 +6,22 @@ use std::{
|
||||
ops::DerefMut,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::time::{interval, Interval};
|
||||
|
||||
use crate::slider_io::{
|
||||
config::{LedMode, ReactiveLayout},
|
||||
controller_state::{FullState, LedState},
|
||||
utils::Buffer,
|
||||
voltex::VoltexState,
|
||||
worker::ThreadJob,
|
||||
worker::AsyncJob,
|
||||
};
|
||||
|
||||
pub struct LedJob {
|
||||
state: FullState,
|
||||
mode: LedMode,
|
||||
serial_port: Option<Box<dyn SerialPort>>,
|
||||
started: Instant,
|
||||
timer: Interval,
|
||||
}
|
||||
|
||||
impl LedJob {
|
||||
@ -26,6 +30,8 @@ impl LedJob {
|
||||
state: state.clone(),
|
||||
mode: mode.clone(),
|
||||
serial_port: None,
|
||||
started: Instant::now(),
|
||||
timer: interval(Duration::from_micros(33333)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,29 +72,29 @@ impl LedJob {
|
||||
led_state.led_state.fill(0);
|
||||
|
||||
// Fixed
|
||||
led_state.paint(3, &[0, 0, 64]);
|
||||
led_state.paint(3, &[10, 100, 180]);
|
||||
for idx in 0..5 {
|
||||
led_state.paint(7 + idx * 4, &[64, 64, 64]);
|
||||
}
|
||||
led_state.paint(27, &[64, 0, 0]);
|
||||
led_state.paint(27, &[180, 10, 110]);
|
||||
|
||||
let voltex_state = VoltexState::from_flat(flat_controller_state);
|
||||
|
||||
// Left laser
|
||||
for (idx, state) in voltex_state.laser[0..2].iter().enumerate() {
|
||||
if *state {
|
||||
led_state.paint(0 + idx * 4, &[0, 0, 255]);
|
||||
led_state.paint(1 + idx * 4, &[0, 0, 255]);
|
||||
led_state.paint(2 + idx * 4, &[0, 0, 255]);
|
||||
led_state.paint(0 + idx * 4, &[70, 230, 250]);
|
||||
led_state.paint(1 + idx * 4, &[70, 230, 250]);
|
||||
led_state.paint(2 + idx * 4, &[70, 230, 250]);
|
||||
}
|
||||
}
|
||||
|
||||
// Right laser
|
||||
for (idx, state) in voltex_state.laser[2..4].iter().enumerate() {
|
||||
if *state {
|
||||
led_state.paint(24 + idx * 4, &[255, 0, 0]);
|
||||
led_state.paint(25 + idx * 4, &[255, 0, 0]);
|
||||
led_state.paint(26 + idx * 4, &[255, 0, 0]);
|
||||
led_state.paint(24 + idx * 4, &[250, 60, 200]);
|
||||
led_state.paint(25 + idx * 4, &[255, 60, 200]);
|
||||
led_state.paint(26 + idx * 4, &[255, 60, 200]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,17 +109,20 @@ impl LedJob {
|
||||
// Fx
|
||||
for (idx, state) in voltex_state.fx.iter().enumerate() {
|
||||
if *state {
|
||||
led_state.paint(9 + idx * 8, &[255, 0, 0]);
|
||||
led_state.paint(11 + idx * 8, &[255, 0, 0]);
|
||||
led_state.paint(13 + idx * 8, &[255, 0, 0]);
|
||||
led_state.paint(9 + idx * 8, &[250, 100, 30]);
|
||||
led_state.paint(11 + idx * 8, &[250, 100, 30]);
|
||||
led_state.paint(13 + idx * 8, &[250, 100, 30]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LedMode::Attract => {
|
||||
let now = Instant::now();
|
||||
let theta = (now - led_state.start).div_duration_f64(Duration::from_secs(4)) % 1.0;
|
||||
let theta = self
|
||||
.started
|
||||
.elapsed()
|
||||
.div_duration_f64(Duration::from_secs(4))
|
||||
% 1.0;
|
||||
for idx in 0..31 {
|
||||
let slice_theta = (&theta + (idx as f64) / 32.0) % 1.0;
|
||||
let color = Srgb::from_color(Hsv::new(slice_theta * 360.0, 1.0, 1.0)).into_format::<u8>();
|
||||
@ -147,8 +156,9 @@ impl LedJob {
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadJob for LedJob {
|
||||
fn setup(&mut self) -> bool {
|
||||
#[async_trait]
|
||||
impl AsyncJob for LedJob {
|
||||
async fn setup(&mut self) -> bool {
|
||||
match &self.mode {
|
||||
LedMode::Serial { port } => {
|
||||
info!(
|
||||
@ -173,14 +183,14 @@ impl ThreadJob for LedJob {
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) -> bool {
|
||||
async fn tick(&mut self) -> bool {
|
||||
let mut flat_controller_state: Option<Vec<bool>> = None;
|
||||
let mut serial_buffer: Option<Buffer> = None;
|
||||
|
||||
// Do the IO here
|
||||
match self.mode {
|
||||
LedMode::Reactive { sensitivity, .. } => {
|
||||
let controller_state_handle = self.state.controller_state.lock().unwrap();
|
||||
let controller_state_handle = self.state.controller_state.lock();
|
||||
flat_controller_state = Some(controller_state_handle.flat(&sensitivity));
|
||||
}
|
||||
LedMode::Serial { .. } => {
|
||||
@ -209,7 +219,7 @@ impl ThreadJob for LedJob {
|
||||
|
||||
// Then calculate and transfer
|
||||
{
|
||||
let mut led_state_handle = self.state.led_state.lock().unwrap();
|
||||
let mut led_state_handle = self.state.led_state.lock();
|
||||
self.calc_lights(
|
||||
flat_controller_state.as_ref(),
|
||||
serial_buffer.as_ref(),
|
||||
@ -217,10 +227,9 @@ impl ThreadJob for LedJob {
|
||||
);
|
||||
}
|
||||
// thread::sleep(Duration::from_millis(30));
|
||||
spin_sleep::sleep(Duration::from_micros(33333));
|
||||
// spin_sleep::sleep(Duration::from_micros(33333));
|
||||
self.timer.tick().await;
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn teardown(&mut self) {}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use log::info;
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
sync::Arc,
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
use tokio::{
|
||||
@ -34,7 +35,7 @@ impl Manager {
|
||||
let join_handle = thread::spawn(move || {
|
||||
info!("Manager thread started");
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(2)
|
||||
.worker_threads(4)
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
@ -47,18 +48,18 @@ impl Manager {
|
||||
match rx_config.recv().await {
|
||||
Some(config) => {
|
||||
info!("Rebuilding context");
|
||||
let mut context_handle = context_cloned.lock().unwrap();
|
||||
let mut context_handle = context_cloned.lock();
|
||||
context_handle.take();
|
||||
|
||||
let new_context = Context::new(config);
|
||||
let new_state = new_context.clone_state();
|
||||
context_handle.replace(new_context);
|
||||
|
||||
let mut state_handle = state_cloned.lock().unwrap();
|
||||
let mut state_handle = state_cloned.lock();
|
||||
state_handle.replace(new_state);
|
||||
},
|
||||
None => {
|
||||
let mut context_handle = context_cloned.lock().unwrap();
|
||||
let mut context_handle = context_cloned.lock();
|
||||
context_handle.take();
|
||||
}
|
||||
}
|
||||
@ -83,12 +84,12 @@ impl Manager {
|
||||
}
|
||||
|
||||
pub fn try_get_state(&self) -> Option<FullState> {
|
||||
let state_handle = self.state.lock().unwrap();
|
||||
let state_handle = self.state.lock();
|
||||
state_handle.as_ref().map(|x| x.clone())
|
||||
}
|
||||
|
||||
pub fn get_timer_state(&self) -> String {
|
||||
let context_handle = self.context.lock().unwrap();
|
||||
let context_handle = self.context.lock();
|
||||
context_handle
|
||||
.as_ref()
|
||||
.map(|context| context.timer_state())
|
||||
|
@ -1,9 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use log::error;
|
||||
use std::time::Duration;
|
||||
use tokio::time::{interval, Interval};
|
||||
|
||||
use crate::slider_io::{
|
||||
config::OutputMode, controller_state::FullState, gamepad::GamepadOutput,
|
||||
keyboard::KeyboardOutput, worker::ThreadJob,
|
||||
keyboard::KeyboardOutput, worker::AsyncJob,
|
||||
};
|
||||
|
||||
pub trait OutputHandler: Send {
|
||||
@ -14,9 +16,9 @@ pub trait OutputHandler: Send {
|
||||
pub struct OutputJob {
|
||||
state: FullState,
|
||||
mode: OutputMode,
|
||||
t: u64,
|
||||
sensitivity: u8,
|
||||
handler: Option<Box<dyn OutputHandler>>,
|
||||
timer: Interval,
|
||||
}
|
||||
|
||||
impl OutputJob {
|
||||
@ -24,24 +26,25 @@ impl OutputJob {
|
||||
Self {
|
||||
state: state.clone(),
|
||||
mode: mode.clone(),
|
||||
t: 0,
|
||||
sensitivity: 0,
|
||||
handler: None,
|
||||
timer: interval(Duration::MAX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadJob for OutputJob {
|
||||
fn setup(&mut self) -> bool {
|
||||
#[async_trait]
|
||||
impl AsyncJob for OutputJob {
|
||||
async fn setup(&mut self) -> bool {
|
||||
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())));
|
||||
self.timer = interval(Duration::from_micros(polling.to_t_u64()));
|
||||
|
||||
true
|
||||
}
|
||||
@ -50,9 +53,10 @@ impl ThreadJob for OutputJob {
|
||||
polling,
|
||||
sensitivity,
|
||||
} => {
|
||||
self.t = polling.to_t_u64();
|
||||
self.sensitivity = sensitivity;
|
||||
let handler = GamepadOutput::new(layout.clone());
|
||||
self.timer = interval(Duration::from_micros(polling.to_t_u64()));
|
||||
|
||||
match handler {
|
||||
Some(handler) => {
|
||||
self.handler = Some(Box::new(handler));
|
||||
@ -68,22 +72,24 @@ impl ThreadJob for OutputJob {
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) -> bool {
|
||||
async fn tick(&mut self) -> bool {
|
||||
let flat_controller_state: Vec<bool>;
|
||||
{
|
||||
let controller_state_handle = self.state.controller_state.lock().unwrap();
|
||||
let controller_state_handle = self.state.controller_state.lock();
|
||||
flat_controller_state = controller_state_handle.flat(&self.sensitivity);
|
||||
}
|
||||
|
||||
if let Some(handler) = self.handler.as_mut() {
|
||||
handler.tick(&flat_controller_state);
|
||||
}
|
||||
spin_sleep::sleep(Duration::from_micros(self.t));
|
||||
self.timer.tick().await;
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn teardown(&mut self) {
|
||||
impl Drop for OutputJob {
|
||||
fn drop(&mut self) {
|
||||
if let Some(handler) = self.handler.as_mut() {
|
||||
handler.reset();
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ use crate::slider_io::utils::LoopTimer;
|
||||
pub trait ThreadJob: Send {
|
||||
fn setup(&mut self) -> bool;
|
||||
fn tick(&mut self) -> bool;
|
||||
fn teardown(&mut self);
|
||||
}
|
||||
|
||||
pub struct ThreadWorker {
|
||||
@ -46,8 +45,7 @@ impl ThreadWorker {
|
||||
timer.tick();
|
||||
}
|
||||
}
|
||||
info!("Thread worker stopping internal {}", name);
|
||||
job.teardown();
|
||||
info!("Thread worker received stop {}", name);
|
||||
})),
|
||||
stop_signal,
|
||||
}
|
||||
@ -56,32 +54,88 @@ impl ThreadWorker {
|
||||
|
||||
impl Drop for ThreadWorker {
|
||||
fn drop(&mut self) {
|
||||
info!("Thread worker stopping {}", self.name);
|
||||
info!("Thread worker stopping gracefully {}", self.name);
|
||||
|
||||
self.stop_signal.store(true, Ordering::SeqCst);
|
||||
if let Some(thread) = self.thread.take() {
|
||||
thread.join().ok();
|
||||
};
|
||||
|
||||
info!("Thread worker stopped {}", self.name);
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait AsyncJob: Send + 'static {
|
||||
async fn run<F: Future<Output = ()> + Send>(self, stop_signal: F);
|
||||
async fn setup(&mut self) -> bool;
|
||||
async fn tick(&mut self) -> bool;
|
||||
}
|
||||
|
||||
pub struct AsyncWorker {
|
||||
name: &'static str,
|
||||
task: Option<task::JoinHandle<()>>,
|
||||
stop_signal: Option<oneshot::Sender<()>>,
|
||||
stop_signal: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl AsyncWorker {
|
||||
pub fn new<T>(name: &'static str, job: T) -> AsyncWorker
|
||||
pub fn new<T>(name: &'static str, mut job: T, mut timer: LoopTimer) -> Self
|
||||
where
|
||||
T: AsyncJob,
|
||||
{
|
||||
info!("Async worker starting {}", name);
|
||||
let stop_signal = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let stop_signal_clone = Arc::clone(&stop_signal);
|
||||
let task = tokio::spawn(async move {
|
||||
let setup_res = job.setup().await;
|
||||
stop_signal_clone.store(!setup_res, Ordering::SeqCst);
|
||||
|
||||
loop {
|
||||
if stop_signal_clone.load(Ordering::SeqCst) {
|
||||
break;
|
||||
}
|
||||
if job.tick().await {
|
||||
timer.tick();
|
||||
}
|
||||
}
|
||||
info!("Async worker received stop {}", name);
|
||||
});
|
||||
|
||||
Self {
|
||||
name,
|
||||
task: Some(task),
|
||||
stop_signal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AsyncWorker {
|
||||
fn drop(&mut self) {
|
||||
info!("Async worker stopping gracefully {}", self.name);
|
||||
|
||||
self.stop_signal.store(true, Ordering::SeqCst);
|
||||
drop(self.task.take());
|
||||
|
||||
info!("Async worker stopped {}", self.name);
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait AsyncHaltableJob: Send + 'static {
|
||||
async fn run<F: Future<Output = ()> + Send>(self, stop_signal: F);
|
||||
}
|
||||
|
||||
pub struct AsyncHaltableWorker {
|
||||
name: &'static str,
|
||||
task: Option<task::JoinHandle<()>>,
|
||||
stop_signal: Option<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
impl AsyncHaltableWorker {
|
||||
pub fn new<T>(name: &'static str, job: T) -> Self
|
||||
where
|
||||
T: AsyncHaltableJob,
|
||||
{
|
||||
info!("AsyncHaltable worker starting {}", name);
|
||||
|
||||
let (send_stop, recv_stop) = oneshot::channel::<()>();
|
||||
|
||||
@ -89,11 +143,12 @@ impl AsyncWorker {
|
||||
job
|
||||
.run(async move {
|
||||
recv_stop.await.ok();
|
||||
info!("AsyncHaltable worker received stop {}", name);
|
||||
})
|
||||
.await;
|
||||
});
|
||||
|
||||
AsyncWorker {
|
||||
Self {
|
||||
name,
|
||||
task: Some(task),
|
||||
stop_signal: Some(send_stop),
|
||||
@ -101,13 +156,14 @@ impl AsyncWorker {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AsyncWorker {
|
||||
impl Drop for AsyncHaltableWorker {
|
||||
fn drop(&mut self) {
|
||||
info!("Async worker stopping {}", self.name);
|
||||
info!("AsyncHaltable worker stopping gracefully {}", self.name);
|
||||
|
||||
if let Some(stop_signal) = self.stop_signal.take() {
|
||||
stop_signal.send(()).ok();
|
||||
}
|
||||
self.task.take();
|
||||
info!("AsyncHaltable worker stopped {}", self.name);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "slidershim",
|
||||
"version": "0.1.3"
|
||||
"version": "0.1.4"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../public",
|
||||
|
@ -179,6 +179,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="row">
|
||||
<div class="label">Output Mode</div>
|
||||
<div class="input">
|
||||
@ -189,6 +190,7 @@
|
||||
<option value="kb-32-yuancon">Keyboard 32-zone, Yuancon Layout</option>
|
||||
<option value="kb-8-deemo">Keyboard 8-zone, Deemo Layout</option>
|
||||
<option value="kb-voltex">Keyboard 10-zone, Voltex Layout</option>
|
||||
<option value="kb-neardayo">Keyboard 10-zone, Neardayo Layout</option>
|
||||
<option value="gamepad-voltex">XBOX 360 Gamepad, Voltex Layout</option>
|
||||
<option value="gamepad-neardayo"
|
||||
>XBOX 360 Gamepad, Neardayo Layout</option
|
||||
@ -206,7 +208,7 @@
|
||||
<option value="100">100 Hz</option>
|
||||
<option value="250">250 Hz</option>
|
||||
<option value="500">500 Hz</option>
|
||||
<option value="1000">1000 Hz (Unstable, may use a lot of CPU)</option>
|
||||
<option value="1000">1000 Hz</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -263,6 +265,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="row">
|
||||
<div class="label">LED Mode</div>
|
||||
<div class="input">
|
||||
|
Loading…
Reference in New Issue
Block a user