1
0
mirror of https://github.com/4yn/slidershim.git synced 2024-09-23 18:58:27 +02:00
This commit is contained in:
4yn 2022-02-07 10:10:36 +08:00
parent a5c89f00cb
commit 98509250d6
14 changed files with 199 additions and 53 deletions

View File

@ -33,7 +33,7 @@ body {
background: #333;
display: flex;
align-items: center;
justify-content: flex-start;
justify-content: space-between;
position: fixed;
top: 0;
left: 0;
@ -42,8 +42,13 @@ body {
height: 2rem;
}
.titlebar-front {
background: #0000;
}
.header-icon {
max-height: 100%;
flex: 0 0 auto;
}
.header-icon img {
@ -54,6 +59,15 @@ body {
.header {
font-size: 1.5rem;
font-weight: 500;
flex: 0 0 auto;
}
.header-space {
flex: 1 0 auto;
}
.header-timer {
flex: 0 0 auto;
}
/* main */

View File

@ -13,12 +13,5 @@
<script defer src="/build/bundle.js"></script>
</head>
<body>
<div data-tauri-drag-region class="titlebar">
<div data-tauri-drag-region class="header-icon">
<img src="/icon.png" />
</div>
<div data-tauri-drag-region class="header">&nbsp;slidershim</div>
</div>
</body>
<body></body>
</html>

20
src-tauri/Cargo.lock generated
View File

@ -115,6 +115,12 @@ dependencies = [
"system-deps 3.2.0",
]
[[package]]
name = "atomic_float"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d"
[[package]]
name = "attohttpc"
version = "0.17.0"
@ -2933,9 +2939,10 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
name = "slidershim"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"async-trait",
"atomic_float",
"base64",
"directories",
"env_logger",
@ -2954,6 +2961,7 @@ dependencies = [
"serde_json",
"serialport",
"simple-logging",
"spin_sleep",
"tauri",
"tauri-build",
"tokio",
@ -2995,6 +3003,16 @@ dependencies = [
"system-deps 1.3.2",
]
[[package]]
name = "spin_sleep"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a98101bdc3833e192713c2af0b0dd2614f50d1cf1f7a97c5221b7aac052acc7"
dependencies = [
"once_cell",
"winapi",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"

View File

@ -1,6 +1,6 @@
[package]
name = "slidershim"
version = "0.1.2"
version = "0.1.3"
description = "slidershim"
authors = ["4yn"]
license = ""
@ -21,8 +21,11 @@ log = "0.4.14"
env_logger = "0.9.0"
simple-logging = "2.0.2"
open = "2.0.2"
atomic_float = "0.1.0"
spin_sleep = "1.0.0"
tauri = { version = "1.0.0-beta.8", features = ["shell-open", "system-tray"] }
futures = "0.3.19"
futures-util = "0.3.19"
async-trait = "0.1.52"

View File

@ -140,9 +140,12 @@ fn main() {
let manager_clone = Arc::clone(&manager);
app.listen_global("queryState", move |_| {
// app_handle.emit_all("showState", "@@@");
let snapshot = {
let (snapshot, timer) = {
let manager_handle = manager_clone.lock().unwrap();
manager_handle.try_get_state().map(|x| x.snapshot())
(
manager_handle.try_get_state().map(|x| x.snapshot()),
manager_handle.get_timer_state(),
)
};
match snapshot {
Some(snapshot) => {
@ -150,6 +153,8 @@ fn main() {
}
_ => {}
}
app_handle.emit_all("showTimerState", timer).ok();
});
// Config set event

View File

@ -1,4 +1,6 @@
use atomic_float::AtomicF64;
use log::info;
use std::sync::{atomic::Ordering, Arc};
use crate::slider_io::{
brokenithm::BrokenithmJob,
@ -7,6 +9,7 @@ use crate::slider_io::{
device::HidDeviceJob,
led::LedJob,
output::OutputJob,
utils::LoopTimer,
worker::{AsyncWorker, ThreadWorker},
};
@ -18,6 +21,7 @@ pub struct Context {
brokenithm_worker: Option<AsyncWorker>,
output_worker: Option<ThreadWorker>,
led_worker: Option<ThreadWorker>,
timers: Vec<(&'static str, Arc<AtomicF64>)>,
}
impl Context {
@ -28,6 +32,7 @@ impl Context {
info!("LED config {:?}", config.led_mode);
let state = FullState::new();
let mut timers = vec![];
let (device_worker, brokenithm_worker) = match &config.device_mode {
DeviceMode::None => (None, None),
@ -42,26 +47,41 @@ impl Context {
)),
),
_ => (
Some(ThreadWorker::new(
"device",
HidDeviceJob::from_config(&state, &config.device_mode),
)),
{
let timer = LoopTimer::new();
timers.push(("d", timer.fork()));
Some(ThreadWorker::new(
"device",
HidDeviceJob::from_config(&state, &config.device_mode),
timer,
))
},
None,
),
};
let output_worker = match &config.output_mode {
OutputMode::None => None,
_ => Some(ThreadWorker::new(
"output",
OutputJob::new(&state, &config.output_mode),
)),
_ => {
let timer = LoopTimer::new();
timers.push(("o", timer.fork()));
Some(ThreadWorker::new(
"output",
OutputJob::new(&state, &config.output_mode),
timer,
))
}
};
let led_worker = match &config.led_mode {
LedMode::None => None,
_ => Some(ThreadWorker::new(
"led",
LedJob::new(&state, &config.led_mode),
)),
_ => {
let timer = LoopTimer::new();
timers.push(("l", timer.fork()));
Some(ThreadWorker::new(
"led",
LedJob::new(&state, &config.led_mode),
timer,
))
}
};
Self {
@ -71,10 +91,20 @@ impl Context {
brokenithm_worker,
output_worker,
led_worker,
timers,
}
}
pub fn clone_state(&self) -> FullState {
self.state.clone()
}
pub fn timer_state(&self) -> String {
self
.timers
.iter()
.map(|(s, f)| format!("{}:{:.1}/s", s, f.load(Ordering::SeqCst)))
.collect::<Vec<String>>()
.join(" ")
}
}

View File

@ -101,9 +101,9 @@ impl HidDeviceJob {
.take(31)
.zip(led_state.led_state.chunks(3).rev())
{
buf_chunk[0] = state_chunk[2];
buf_chunk[0] = state_chunk[1];
buf_chunk[1] = state_chunk[0];
buf_chunk[2] = state_chunk[1];
buf_chunk[2] = state_chunk[2];
}
buf.data[96..240].fill(0);
},
@ -139,9 +139,9 @@ impl HidDeviceJob {
.take(31)
.zip(led_state.led_state.chunks(3).rev())
{
buf_chunk[0] = state_chunk[2];
buf_chunk[0] = state_chunk[1];
buf_chunk[1] = state_chunk[0];
buf_chunk[2] = state_chunk[1];
buf_chunk[2] = state_chunk[2];
}
buf.data[96..240].fill(0);
},
@ -224,9 +224,10 @@ impl ThreadJob for HidDeviceJob {
}
}
fn tick(&mut self) {
fn tick(&mut self) -> bool {
// Input loop
let handle = self.handle.as_mut().unwrap();
let mut work = false;
{
let res = handle
@ -239,6 +240,7 @@ impl ThreadJob for HidDeviceJob {
self.read_buf.len = res;
// debug!("{:?}", self.read_buf.slice());
if self.read_buf.len != 0 {
work = true;
let mut controller_state_handle = self.state.controller_state.lock().unwrap();
(self.read_callback)(&self.read_buf, controller_state_handle.deref_mut());
}
@ -267,12 +269,13 @@ impl ThreadJob for HidDeviceJob {
})
.unwrap_or(0);
if res == self.led_buf.len + 1 {
work = true;
self.led_buf.len = 0;
}
}
}
// thread::sleep(Duration::from_millis(10));
work
}
fn teardown(&mut self) {

View File

@ -174,7 +174,7 @@ impl ThreadJob for LedJob {
}
}
fn tick(&mut self) {
fn tick(&mut self) -> bool {
let mut flat_controller_state: Option<Vec<bool>> = None;
let mut serial_buffer: Option<Buffer> = None;
@ -217,7 +217,10 @@ impl ThreadJob for LedJob {
led_state_handle.deref_mut(),
);
}
thread::sleep(Duration::from_millis(30));
// thread::sleep(Duration::from_millis(30));
spin_sleep::sleep(Duration::from_millis(30));
true
}
fn teardown(&mut self) {}

View File

@ -14,6 +14,7 @@ use super::controller_state::FullState;
pub struct Manager {
state: Arc<Mutex<Option<FullState>>>,
context: Arc<Mutex<Option<Context>>>,
join_handle: Option<JoinHandle<()>>,
tx_config: mpsc::UnboundedSender<Config>,
tx_stop: Option<oneshot::Sender<()>>,
@ -70,6 +71,7 @@ impl Manager {
Self {
state,
context,
join_handle: Some(join_handle),
tx_config,
tx_stop: Some(tx_stop),
@ -84,6 +86,14 @@ impl Manager {
let state_handle = self.state.lock().unwrap();
state_handle.as_ref().map(|x| x.clone())
}
pub fn get_timer_state(&self) -> String {
let context_handle = self.context.lock().unwrap();
context_handle
.as_ref()
.map(|context| context.timer_state())
.unwrap_or("".to_string())
}
}
impl Drop for Manager {

View File

@ -50,7 +50,7 @@ impl ThreadJob for OutputJob {
true
}
fn tick(&mut self) {
fn tick(&mut self) -> bool {
let flat_controller_state: Vec<bool>;
{
let controller_state_handle = self.state.controller_state.lock().unwrap();
@ -58,7 +58,10 @@ impl ThreadJob for OutputJob {
}
self.handler.tick(&flat_controller_state);
thread::sleep(Duration::from_millis(self.t));
// thread::sleep(Duration::from_millis(self.t));
spin_sleep::sleep(Duration::from_millis(self.t));
true
}
fn teardown(&mut self) {

View File

@ -1,4 +1,10 @@
use std::{error::Error, fmt};
use atomic_float::AtomicF64;
use std::{
error::Error,
fmt,
sync::{atomic::Ordering, Arc},
time::{Duration, Instant},
};
pub struct Buffer {
pub data: [u8; 256],
@ -44,3 +50,45 @@ pub fn list_ips() -> Result<Vec<String>, Box<dyn Error>> {
Ok(ips)
}
pub struct LoopTimer {
cap: usize,
cur: usize,
buf: Vec<Instant>,
freq: Arc<AtomicF64>,
}
impl LoopTimer {
pub fn new() -> Self {
Self {
cap: 100,
cur: 0,
buf: vec![Instant::now() - Duration::from_secs(10); 100],
freq: Arc::new(AtomicF64::new(0.0)),
}
}
pub fn tick(&mut self) {
let last = self.buf[self.cur];
let now = Instant::now();
self.buf[self.cur] = now;
let delta = (now - last) / 100 + Duration::from_micros(1);
let freq = Duration::from_millis(1000)
.div_duration_f64(delta)
.clamp(0.0, 9999.0);
self.freq.store(freq, Ordering::SeqCst);
self.cur = match self.cur + 1 {
cur if cur == self.cap => 0,
cur => cur,
}
}
// pub fn reset(&mut self) {
// self.buf = vec![Instant::now(); 100];
// }
pub fn fork(&self) -> Arc<AtomicF64> {
Arc::clone(&self.freq)
}
}

View File

@ -11,9 +11,11 @@ use std::{
use tokio::{sync::oneshot, task};
use crate::slider_io::utils::LoopTimer;
pub trait ThreadJob: Send {
fn setup(&mut self) -> bool;
fn tick(&mut self);
fn tick(&mut self) -> bool;
fn teardown(&mut self);
}
@ -24,7 +26,7 @@ pub struct ThreadWorker {
}
impl ThreadWorker {
pub fn new<T: 'static + ThreadJob>(name: &'static str, mut job: T) -> Self {
pub fn new<T: 'static + ThreadJob>(name: &'static str, mut job: T, mut timer: LoopTimer) -> Self {
info!("Thread worker starting {}", name);
let stop_signal = Arc::new(AtomicBool::new(false));
@ -40,7 +42,9 @@ impl ThreadWorker {
if stop_signal_clone.load(Ordering::SeqCst) {
break;
}
job.tick();
if job.tick() {
timer.tick();
}
}
info!("Thread worker stopping internal {}", name);
job.teardown();

View File

@ -1,7 +1,7 @@
{
"package": {
"productName": "slidershim",
"version": "0.1.2"
"version": "0.1.3"
},
"build": {
"distDir": "../public",

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from "svelte";
import { emit, listen } from "@tauri-apps/api/event";
import { open } from "@tauri-apps/api/shell";
import { getVersion } from "@tauri-apps/api/app";
import Link from "./Link.svelte";
import Preview from "./Preview.svelte";
@ -24,11 +24,12 @@
}
// let debugstr = "";
let versionString = "";
let ips: Array<string> = [];
let polling = null;
let tick = 0;
let previewData = Array(131).fill(0);
let timerData = "";
function updatePolling(enabled) {
if (!!polling) {
@ -65,6 +66,9 @@
await listen("showState", (event) => {
previewData = event.payload as any;
});
await listen("showTimerState", (event) => {
timerData = event.payload as string;
});
await listen("listIps", (event) => {
ips = (event.payload as Array<string>).filter(
@ -83,6 +87,8 @@
console.log("ackHide");
updatePolling(false);
});
versionString = ` ${await getVersion()}`;
});
// Emit events
@ -124,16 +130,20 @@
}
</script>
<div class="titlebar">
<div class="header-icon">
<img src="/icon.png" />
</div>
<div class="header">
&nbsp;slidershim{versionString}
</div>
<div class="header-space" />
<div class="header-timer">
{timerData}
</div>
</div>
<div data-tauri-drag-region class="titlebar titlebar-front" />
<main class="main">
<!-- <div class="row titlebar" data-tauri-drag-region> -->
<!-- <div class="header"> -->
<!-- slidershim by @4yn -->
<!-- slidershim -->
<!-- </div> -->
<!-- </div> -->
<!-- <div>
{debugstr}
</div> -->
<div class="row">
<Preview data={previewData} />
</div>
@ -148,7 +158,9 @@
<option value="brokenithm">Brokenithm</option>
<option value="brokenithm-led">Brokenithm + Led</option>
<option value="brokenithm-ground">Brokenithm, Ground only</option>
<option value="brokenithm-ground-led">Brokenithm + Led, Ground only</option>
<option value="brokenithm-ground-led"
>Brokenithm + Led, Ground only</option
>
</select>
</div>
</div>
@ -192,7 +204,7 @@
<option value="100">100 Hz</option>
<option value="330">330 Hz</option>
<option value="500">500 Hz</option>
<option value="1000">1000 Hz</option>
<option value="1000">1000 Hz (Unstable, may use a lot of CPU)</option>
</select>
</div>
</div>