mirror of
https://github.com/4yn/slidershim.git
synced 2024-11-13 17:31:00 +01:00
Frontend polling
This commit is contained in:
parent
bfb7f7dc95
commit
8a67094c9a
117
src-tauri/res/www/index-go.html
Normal file
117
src-tauri/res/www/index-go.html
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>slidershim-brokenithm</title>
|
||||||
|
<meta charset="utf8" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||||
|
/>
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<link rel="apple-touch-icon" href="favicon.ico" />
|
||||||
|
<style>
|
||||||
|
#fullscreen {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
background: #000000;
|
||||||
|
color: hotpink;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
touch-action: none;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.air-container {
|
||||||
|
/* display: flex; */
|
||||||
|
display: none;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
align-items: stretch;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.touch-container {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
align-items: stretch;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grow > * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key {
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key[data-active] {
|
||||||
|
background-color: hotpink;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key.air[data-active] {
|
||||||
|
background-color: skyblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
touch-action: none;
|
||||||
|
margin: 0px -1.5625vw;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="fullscreen">
|
||||||
|
<!-- Offset for LED display -->
|
||||||
|
<div class="container">
|
||||||
|
<div class="air-container grow"></div>
|
||||||
|
<div class="touch-container grow">
|
||||||
|
<canvas id="canvas" width="33" height="1"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Hitbox Divs -->
|
||||||
|
<div class="container" id="main">
|
||||||
|
<div class="air-container grow">
|
||||||
|
<div class="air key" data-air="1" data-kflag="5"></div>
|
||||||
|
<div class="air key" data-air="1" data-kflag="4"></div>
|
||||||
|
<div class="air key" data-air="1" data-kflag="3"></div>
|
||||||
|
<div class="air key" data-air="1" data-kflag="2"></div>
|
||||||
|
<div class="air key" data-air="1" data-kflag="1"></div>
|
||||||
|
<div class="air key" data-air="1" data-kflag="0"></div>
|
||||||
|
</div>
|
||||||
|
<div class="touch-container grow">
|
||||||
|
<div class="key" data-kflag="0"></div>
|
||||||
|
<div class="key" data-kflag="2"></div>
|
||||||
|
<div class="key" data-kflag="4"></div>
|
||||||
|
<div class="key" data-kflag="6"></div>
|
||||||
|
<div class="key" data-kflag="8"></div>
|
||||||
|
<div class="key" data-kflag="10"></div>
|
||||||
|
<div class="key" data-kflag="12"></div>
|
||||||
|
<div class="key" data-kflag="14"></div>
|
||||||
|
<div class="key" data-kflag="16"></div>
|
||||||
|
<div class="key" data-kflag="18"></div>
|
||||||
|
<div class="key" data-kflag="20"></div>
|
||||||
|
<div class="key" data-kflag="22"></div>
|
||||||
|
<div class="key" data-kflag="24"></div>
|
||||||
|
<div class="key" data-kflag="26"></div>
|
||||||
|
<div class="key" data-kflag="28"></div>
|
||||||
|
<div class="key" data-kflag="30"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/config.js"></script>
|
||||||
|
<script src="/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>brokenithm-kb</title>
|
<title>slidershim-brokenithm</title>
|
||||||
<meta charset="utf8" />
|
<meta charset="utf8" />
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
|
@ -2,7 +2,7 @@ extern crate slidershim;
|
|||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use slidershim::slider_io::{Config, Manager};
|
use slidershim::slider_io::{Config, Context};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::Builder::new()
|
env_logger::Builder::new()
|
||||||
@ -57,7 +57,7 @@ fn main() {
|
|||||||
// )
|
// )
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
let manager = Manager::new(config);
|
let manager = Context::new(config);
|
||||||
|
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
let string = io::stdin().read_line(&mut input).unwrap();
|
let string = io::stdin().read_line(&mut input).unwrap();
|
||||||
|
@ -18,10 +18,12 @@ use tauri::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn show_window<R: Runtime>(handle: &AppHandle<R>) {
|
fn show_window<R: Runtime>(handle: &AppHandle<R>) {
|
||||||
|
handle.emit_all("ackShow", "");
|
||||||
handle.get_window("main").unwrap().show().ok();
|
handle.get_window("main").unwrap().show().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hide_window<R: Runtime>(handle: &AppHandle<R>) {
|
fn hide_window<R: Runtime>(handle: &AppHandle<R>) {
|
||||||
|
handle.emit_all("ackHide", "");
|
||||||
handle.get_window("main").unwrap().hide().ok();
|
handle.get_window("main").unwrap().hide().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,14 +38,13 @@ fn main() {
|
|||||||
.init();
|
.init();
|
||||||
|
|
||||||
let config = Arc::new(Mutex::new(Some(slider_io::Config::default())));
|
let config = Arc::new(Mutex::new(Some(slider_io::Config::default())));
|
||||||
let manager: Arc<Mutex<Option<slider_io::Manager>>> = Arc::new(Mutex::new(None));
|
let manager = Arc::new(Mutex::new(slider_io::Manager::new()));
|
||||||
{
|
{
|
||||||
let config_handle = config.lock().unwrap();
|
let config_handle = config.lock().unwrap();
|
||||||
let config_handle_ref = config_handle.as_ref().unwrap();
|
let config_handle_ref = config_handle.as_ref().unwrap();
|
||||||
config_handle_ref.save();
|
config_handle_ref.save();
|
||||||
// let mut manager_handle = manager.lock().unwrap();
|
let manager_handle = manager.lock().unwrap();
|
||||||
// manager_handle.take();
|
manager_handle.update_config(config_handle_ref.clone());
|
||||||
// manager_handle.replace(slider_io::Manager::new(config_handle_ref.clone()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
@ -95,12 +96,9 @@ fn main() {
|
|||||||
// UI ready event
|
// UI ready event
|
||||||
let app_handle = app.handle();
|
let app_handle = app.handle();
|
||||||
let config_clone = Arc::clone(&config);
|
let config_clone = Arc::clone(&config);
|
||||||
app.listen_global("heartbeat", move |_| {
|
app.listen_global("ready", move |_| {
|
||||||
let handle = AsyncHandle::try_current();
|
|
||||||
println!("handle, {:?}", handle);
|
|
||||||
|
|
||||||
let config_handle = config_clone.lock().unwrap();
|
let config_handle = config_clone.lock().unwrap();
|
||||||
info!("Heartbeat received");
|
info!("Start signal received");
|
||||||
app_handle
|
app_handle
|
||||||
.emit_all(
|
.emit_all(
|
||||||
"showConfig",
|
"showConfig",
|
||||||
@ -109,6 +107,23 @@ fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// UI update event
|
||||||
|
let app_handle = app.handle();
|
||||||
|
let manager_clone = Arc::clone(&manager);
|
||||||
|
app.listen_global("queryState", move |event| {
|
||||||
|
// app_handle.emit_all("showState", "@@@");
|
||||||
|
let snapshot = {
|
||||||
|
let manager_handle = manager_clone.lock().unwrap();
|
||||||
|
manager_handle.try_get_state().map(|x| x.snapshot())
|
||||||
|
};
|
||||||
|
match snapshot {
|
||||||
|
Some(snapshot) => {
|
||||||
|
app_handle.emit_all("showState", snapshot);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Config set event
|
// Config set event
|
||||||
let config_clone = Arc::clone(&config);
|
let config_clone = Arc::clone(&config);
|
||||||
let manager_clone = Arc::clone(&manager);
|
let manager_clone = Arc::clone(&manager);
|
||||||
@ -121,9 +136,8 @@ fn main() {
|
|||||||
config_handle.replace(new_config);
|
config_handle.replace(new_config);
|
||||||
let config_handle_ref = config_handle.as_ref().unwrap();
|
let config_handle_ref = config_handle.as_ref().unwrap();
|
||||||
config_handle_ref.save();
|
config_handle_ref.save();
|
||||||
let mut manager_handle = manager_clone.lock().unwrap();
|
let manager_handle = manager_clone.lock().unwrap();
|
||||||
manager_handle.take();
|
manager_handle.update_config(config_handle_ref.clone());
|
||||||
manager_handle.replace(slider_io::Manager::new(config_handle_ref.clone()));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use hyper::{
|
|||||||
};
|
};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use path_clean::PathClean;
|
use path_clean::PathClean;
|
||||||
use std::{convert::Infallible, future::Future, net::SocketAddr, path::PathBuf};
|
use std::{convert::Infallible, env::current_exe, future::Future, net::SocketAddr};
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio_tungstenite::WebSocketStream;
|
use tokio_tungstenite::WebSocketStream;
|
||||||
use tokio_util::codec::{BytesCodec, FramedRead};
|
use tokio_util::codec::{BytesCodec, FramedRead};
|
||||||
@ -29,15 +29,17 @@ async fn error_response() -> Result<Response<Body>, Infallible> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn serve_file(path: &str) -> Result<Response<Body>, Infallible> {
|
async fn serve_file(path: &str) -> Result<Response<Body>, Infallible> {
|
||||||
let mut pb = PathBuf::from("res/www/");
|
let mut pb = current_exe().unwrap();
|
||||||
|
pb.pop();
|
||||||
|
pb.push("res/www");
|
||||||
pb.push(path);
|
pb.push(path);
|
||||||
pb.clean();
|
pb.clean();
|
||||||
|
|
||||||
// println!("CWD {:?}", std::env::current_dir());
|
// println!("CWD {:?}", std::env::current_dir());
|
||||||
// println!("Serving file {:?}", pb);
|
|
||||||
|
|
||||||
match File::open(pb).await {
|
match File::open(&pb).await {
|
||||||
Ok(f) => {
|
Ok(f) => {
|
||||||
|
info!("Serving file {:?}", pb);
|
||||||
let stream = FramedRead::new(f, BytesCodec::new());
|
let stream = FramedRead::new(f, BytesCodec::new());
|
||||||
let body = Body::wrap_stream(stream);
|
let body = Body::wrap_stream(stream);
|
||||||
Ok(Response::new(body))
|
Ok(Response::new(body))
|
||||||
@ -154,6 +156,7 @@ async fn handle_request(
|
|||||||
request: Request<Body>,
|
request: Request<Body>,
|
||||||
remote_addr: SocketAddr,
|
remote_addr: SocketAddr,
|
||||||
state: FullState,
|
state: FullState,
|
||||||
|
ground_only: bool,
|
||||||
) -> Result<Response<Body>, Infallible> {
|
) -> Result<Response<Body>, Infallible> {
|
||||||
let method = request.method();
|
let method = request.method();
|
||||||
let path = request.uri().path();
|
let path = request.uri().path();
|
||||||
@ -170,7 +173,10 @@ async fn handle_request(
|
|||||||
request.uri().path(),
|
request.uri().path(),
|
||||||
request.headers().contains_key(header::UPGRADE),
|
request.headers().contains_key(header::UPGRADE),
|
||||||
) {
|
) {
|
||||||
("/", false) | ("/index.html", false) => serve_file("index.html").await,
|
("/", false) | ("/index.html", false) => match ground_only {
|
||||||
|
false => serve_file("index.html").await,
|
||||||
|
true => serve_file("index-go.html").await,
|
||||||
|
},
|
||||||
(filename, false) => serve_file(&filename[1..]).await,
|
(filename, false) => serve_file(&filename[1..]).await,
|
||||||
("/ws", true) => handle_websocket(request, state).await,
|
("/ws", true) => handle_websocket(request, state).await,
|
||||||
_ => error_response().await,
|
_ => error_response().await,
|
||||||
@ -179,12 +185,14 @@ async fn handle_request(
|
|||||||
|
|
||||||
pub struct BrokenithmJob {
|
pub struct BrokenithmJob {
|
||||||
state: FullState,
|
state: FullState,
|
||||||
|
ground_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BrokenithmJob {
|
impl BrokenithmJob {
|
||||||
pub fn new(state: &FullState) -> Self {
|
pub fn new(state: &FullState, ground_only: &bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
|
ground_only: *ground_only,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,13 +201,14 @@ impl BrokenithmJob {
|
|||||||
impl AsyncJob for BrokenithmJob {
|
impl AsyncJob for BrokenithmJob {
|
||||||
async fn run<F: Future<Output = ()> + Send>(self, stop_signal: F) {
|
async fn run<F: Future<Output = ()> + Send>(self, stop_signal: F) {
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
|
let ground_only = self.ground_only;
|
||||||
let make_svc = make_service_fn(|conn: &AddrStream| {
|
let make_svc = make_service_fn(|conn: &AddrStream| {
|
||||||
let remote_addr = conn.remote_addr();
|
let remote_addr = conn.remote_addr();
|
||||||
let make_svc_state = state.clone();
|
let make_svc_state = state.clone();
|
||||||
async move {
|
async move {
|
||||||
Ok::<_, Infallible>(service_fn(move |request: Request<Body>| {
|
Ok::<_, Infallible>(service_fn(move |request: Request<Body>| {
|
||||||
let svc_state = make_svc_state.clone();
|
let svc_state = make_svc_state.clone();
|
||||||
handle_request(request, remote_addr, svc_state)
|
handle_request(request, remote_addr, svc_state, ground_only)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
64
src-tauri/src/slider_io/context.rs
Normal file
64
src-tauri/src/slider_io/context.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::slider_io::{
|
||||||
|
brokenithm::BrokenithmJob,
|
||||||
|
config::{Config, DeviceMode},
|
||||||
|
controller_state::FullState,
|
||||||
|
device::HidDeviceJob,
|
||||||
|
led::LedJob,
|
||||||
|
output::OutputJob,
|
||||||
|
worker::{AsyncWorker, ThreadWorker},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Context {
|
||||||
|
state: FullState,
|
||||||
|
config: Config,
|
||||||
|
device_worker: Option<ThreadWorker>,
|
||||||
|
brokenithm_worker: Option<AsyncWorker>,
|
||||||
|
output_worker: ThreadWorker,
|
||||||
|
led_worker: ThreadWorker,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new(config: Config) -> Self {
|
||||||
|
info!("Context creating");
|
||||||
|
info!("Device config {:?}", config.device_mode);
|
||||||
|
info!("Output config {:?}", config.output_mode);
|
||||||
|
info!("LED config {:?}", config.led_mode);
|
||||||
|
|
||||||
|
let state = FullState::new();
|
||||||
|
|
||||||
|
let (device_worker, brokenithm_worker) = match &config.device_mode {
|
||||||
|
DeviceMode::Brokenithm { ground_only } => (
|
||||||
|
None,
|
||||||
|
Some(AsyncWorker::new(
|
||||||
|
"brokenithm",
|
||||||
|
BrokenithmJob::new(&state, ground_only),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
other => (
|
||||||
|
Some(ThreadWorker::new(
|
||||||
|
"device",
|
||||||
|
HidDeviceJob::from_config(&state, other),
|
||||||
|
)),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let output_worker = ThreadWorker::new("output", OutputJob::new(&state, &config.output_mode));
|
||||||
|
let led_worker = ThreadWorker::new("led", LedJob::new(&state, &config.led_mode));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
state,
|
||||||
|
config,
|
||||||
|
device_worker,
|
||||||
|
brokenithm_worker,
|
||||||
|
output_worker,
|
||||||
|
led_worker,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone_state(&self) -> FullState {
|
||||||
|
self.state.clone()
|
||||||
|
}
|
||||||
|
}
|
@ -74,6 +74,21 @@ impl FullState {
|
|||||||
pub fn clone_led(&self) -> Arc<Mutex<LedState>> {
|
pub fn clone_led(&self) -> Arc<Mutex<LedState>> {
|
||||||
Arc::clone(&self.led_state)
|
Arc::clone(&self.led_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn snapshot(&self) -> Vec<u8> {
|
||||||
|
let mut buf: Vec<u8> = vec![];
|
||||||
|
{
|
||||||
|
let controller_state_handle = self.controller_state.lock().unwrap();
|
||||||
|
buf.extend(controller_state_handle.ground_state);
|
||||||
|
buf.extend(controller_state_handle.air_state);
|
||||||
|
};
|
||||||
|
{
|
||||||
|
let led_state_handle = self.led_state.lock().unwrap();
|
||||||
|
buf.extend(led_state_handle.led_state);
|
||||||
|
};
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for FullState {
|
impl Clone for FullState {
|
||||||
|
@ -31,8 +31,8 @@ const YUANCON_KB_MAP: [usize; 41] = [
|
|||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
const DEEMO_KB_MAP: [usize; 41] = [
|
const DEEMO_KB_MAP: [usize; 41] = [
|
||||||
0x41, 0x41, 0x41, 0x41, // A
|
0x41, 0x41, 0x41, 0x41, // A
|
||||||
0x35, 0x35, 0x35, 0x35, // S
|
0x53, 0x53, 0x53, 0x53, // S
|
||||||
0x4f, 0x4f, 0x4f, 0x4f, // D
|
0x44, 0x44, 0x44, 0x44, // D
|
||||||
0x46, 0x46, 0x46, 0x46, // F
|
0x46, 0x46, 0x46, 0x46, // F
|
||||||
0x4a, 0x4a, 0x4a, 0x4a, // J
|
0x4a, 0x4a, 0x4a, 0x4a, // J
|
||||||
0x4b, 0x4b, 0x4b, 0x4b, // K
|
0x4b, 0x4b, 0x4b, 0x4b, // K
|
||||||
|
@ -1,57 +1,94 @@
|
|||||||
use log::info;
|
use log::info;
|
||||||
|
use std::{
|
||||||
use crate::slider_io::{
|
sync::{Arc, Mutex},
|
||||||
brokenithm::BrokenithmJob,
|
thread::{self, JoinHandle},
|
||||||
config::{Config, DeviceMode},
|
};
|
||||||
controller_state::FullState,
|
use tokio::{
|
||||||
device::HidDeviceJob,
|
select,
|
||||||
led::LedJob,
|
sync::{mpsc, oneshot},
|
||||||
output::OutputJob,
|
|
||||||
worker::{AsyncWorker, ThreadWorker},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
use crate::slider_io::{config::Config, context::Context};
|
||||||
|
|
||||||
|
use super::controller_state::FullState;
|
||||||
|
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
state: FullState,
|
state: Arc<Mutex<Option<FullState>>>,
|
||||||
config: Config,
|
join_handle: Option<JoinHandle<()>>,
|
||||||
device_worker: Option<ThreadWorker>,
|
tx_config: mpsc::UnboundedSender<Config>,
|
||||||
brokenithm_worker: Option<AsyncWorker>,
|
tx_stop: Option<oneshot::Sender<()>>,
|
||||||
output_worker: ThreadWorker,
|
|
||||||
led_worker: ThreadWorker,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn new(config: Config) -> Self {
|
pub fn new() -> Self {
|
||||||
info!("Starting manager");
|
let state = Arc::new(Mutex::new(None));
|
||||||
info!("Device config {:?}", config.device_mode);
|
let (tx_config, mut rx_config) = mpsc::unbounded_channel::<Config>();
|
||||||
info!("Output config {:?}", config.output_mode);
|
let (tx_stop, rx_stop) = oneshot::channel::<()>();
|
||||||
info!("LED config {:?}", config.led_mode);
|
|
||||||
|
|
||||||
let state = FullState::new();
|
let context: Arc<Mutex<Option<Context>>> = Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
let (device_worker, brokenithm_worker) = match &config.device_mode {
|
let state_cloned = Arc::clone(&state);
|
||||||
DeviceMode::Brokenithm { .. } => (
|
let context_cloned = Arc::clone(&context);
|
||||||
None,
|
|
||||||
Some(AsyncWorker::new("brokenithm", BrokenithmJob::new(&state))),
|
let join_handle = thread::spawn(move || {
|
||||||
),
|
info!("Manager thread started");
|
||||||
other => (
|
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||||
Some(ThreadWorker::new(
|
.worker_threads(1)
|
||||||
"device",
|
.enable_all()
|
||||||
HidDeviceJob::from_config(&state, other),
|
.build()
|
||||||
)),
|
.unwrap();
|
||||||
None,
|
runtime.block_on(async move {
|
||||||
),
|
info!("Manager runtime started");
|
||||||
};
|
|
||||||
let output_worker = ThreadWorker::new("output", OutputJob::new(&state, &config.output_mode));
|
select! {
|
||||||
let led_worker = ThreadWorker::new("led", LedJob::new(&state, &config.led_mode));
|
_ = async {
|
||||||
|
loop {
|
||||||
|
match rx_config.recv().await {
|
||||||
|
Some(config) => {
|
||||||
|
info!("Rebuilding context");
|
||||||
|
let mut context_handle = context_cloned.lock().unwrap();
|
||||||
|
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();
|
||||||
|
state_handle.replace(new_state);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let mut context_handle = context_cloned.lock().unwrap();
|
||||||
|
context_handle.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} => {},
|
||||||
|
_ = rx_stop => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
state,
|
state,
|
||||||
config,
|
join_handle: Some(join_handle),
|
||||||
device_worker,
|
tx_config,
|
||||||
brokenithm_worker,
|
tx_stop: Some(tx_stop),
|
||||||
output_worker,
|
|
||||||
led_worker,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_config(&self, config: Config) {
|
||||||
|
self.tx_config.send(config).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_get_state(&self) -> Option<FullState> {
|
||||||
|
let state_handle = self.state.lock().unwrap();
|
||||||
|
state_handle.as_ref().map(|x| x.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Manager {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.tx_stop.take().unwrap().send(()).unwrap();
|
||||||
|
self.join_handle.take().unwrap().join().unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ mod device;
|
|||||||
mod led;
|
mod led;
|
||||||
mod output;
|
mod output;
|
||||||
|
|
||||||
|
mod context;
|
||||||
mod manager;
|
mod manager;
|
||||||
|
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
|
@ -9,7 +9,7 @@ use std::{
|
|||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tokio::{runtime::Runtime, sync::oneshot, task};
|
use tokio::{sync::oneshot, task};
|
||||||
|
|
||||||
pub trait ThreadJob: Send {
|
pub trait ThreadJob: Send {
|
||||||
fn setup(&mut self) -> bool;
|
fn setup(&mut self) -> bool;
|
||||||
@ -68,7 +68,6 @@ pub trait AsyncJob: Send + 'static {
|
|||||||
|
|
||||||
pub struct AsyncWorker {
|
pub struct AsyncWorker {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
runtime: Runtime,
|
|
||||||
task: Option<task::JoinHandle<()>>,
|
task: Option<task::JoinHandle<()>>,
|
||||||
stop_signal: Option<oneshot::Sender<()>>,
|
stop_signal: Option<oneshot::Sender<()>>,
|
||||||
}
|
}
|
||||||
@ -81,13 +80,8 @@ impl AsyncWorker {
|
|||||||
info!("Async worker starting {}", name);
|
info!("Async worker starting {}", name);
|
||||||
|
|
||||||
let (send_stop, recv_stop) = oneshot::channel::<()>();
|
let (send_stop, recv_stop) = oneshot::channel::<()>();
|
||||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
||||||
.worker_threads(1)
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let task = runtime.spawn(async move {
|
let task = tokio::spawn(async move {
|
||||||
job
|
job
|
||||||
.run(async move {
|
.run(async move {
|
||||||
recv_stop.await.unwrap();
|
recv_stop.await.unwrap();
|
||||||
@ -97,7 +91,6 @@ impl AsyncWorker {
|
|||||||
|
|
||||||
AsyncWorker {
|
AsyncWorker {
|
||||||
name,
|
name,
|
||||||
runtime,
|
|
||||||
task: Some(task),
|
task: Some(task),
|
||||||
stop_signal: Some(send_stop),
|
stop_signal: Some(send_stop),
|
||||||
}
|
}
|
||||||
@ -108,20 +101,7 @@ impl Drop for AsyncWorker {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
info!("Async worker stopping {}", self.name);
|
info!("Async worker stopping {}", self.name);
|
||||||
|
|
||||||
if self.stop_signal.is_some() {
|
self.stop_signal.take().unwrap().send(()).unwrap();
|
||||||
let send_stop = self.stop_signal.take().unwrap();
|
self.task.take();
|
||||||
send_stop.send(()).unwrap();
|
|
||||||
// self.runtime.block_on(async move {
|
|
||||||
// send_stop.send(()).unwrap();
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.task.is_some() {
|
|
||||||
// let task = self.task.take().unwrap();
|
|
||||||
// self.runtime.block_on(async move {
|
|
||||||
// task.await;
|
|
||||||
// info!("Async worker stopping internal {}", name);
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,27 @@
|
|||||||
|
|
||||||
let debugstr = "";
|
let debugstr = "";
|
||||||
|
|
||||||
|
let polling = null;
|
||||||
|
let tick = 0;
|
||||||
|
let previewData = Array(131).fill(0);
|
||||||
|
|
||||||
|
function updatePolling(enabled) {
|
||||||
|
if (!!polling) {
|
||||||
|
clearInterval(polling);
|
||||||
|
polling = null;
|
||||||
|
}
|
||||||
|
if (enabled) {
|
||||||
|
polling = setInterval(async () => {
|
||||||
|
tick += 1;
|
||||||
|
await emit("queryState", "");
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
// console.log(enabled, polling, tick);
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// console.log(emit, listen);
|
// console.log(emit, listen);
|
||||||
await listen("showConfig", (event) => {
|
await listen("showConfig", (event) => {
|
||||||
console.log("heartbeat", event);
|
|
||||||
debugstr = event.payload;
|
debugstr = event.payload;
|
||||||
const payload: any = JSON.parse(event.payload as any);
|
const payload: any = JSON.parse(event.payload as any);
|
||||||
deviceMode = payload.deviceMode || "none";
|
deviceMode = payload.deviceMode || "none";
|
||||||
@ -32,7 +49,22 @@
|
|||||||
ledWebsocketUrl = payload.ledWebsocketUrl || "http://localhost:3001";
|
ledWebsocketUrl = payload.ledWebsocketUrl || "http://localhost:3001";
|
||||||
ledSerialPort = payload.ledSerialPort || "COM5";
|
ledSerialPort = payload.ledSerialPort || "COM5";
|
||||||
});
|
});
|
||||||
await emit("heartbeat", "");
|
|
||||||
|
await listen("showState", (event) => {
|
||||||
|
previewData = event.payload;
|
||||||
|
});
|
||||||
|
|
||||||
|
await emit("ready", "");
|
||||||
|
|
||||||
|
updatePolling(true);
|
||||||
|
await listen("ackShow", (event) => {
|
||||||
|
console.log("ackShow");
|
||||||
|
updatePolling(true);
|
||||||
|
});
|
||||||
|
await listen("ackHide", (event) => {
|
||||||
|
console.log("ackHide");
|
||||||
|
updatePolling(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function setConfig() {
|
async function setConfig() {
|
||||||
@ -73,7 +105,7 @@
|
|||||||
{debugstr}
|
{debugstr}
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<Preview />
|
<Preview data={previewData} />
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="label">Input Device</div>
|
<div class="label">Input Device</div>
|
||||||
@ -84,7 +116,7 @@
|
|||||||
<option value="tasoller-two">GAMO2 Tasoller, 2.0 HID Firmware</option>
|
<option value="tasoller-two">GAMO2 Tasoller, 2.0 HID Firmware</option>
|
||||||
<option value="yuancon">Yuancon Laverita, HID Firmware</option>
|
<option value="yuancon">Yuancon Laverita, HID Firmware</option>
|
||||||
<option value="brokenithm">Brokenithm</option>
|
<option value="brokenithm">Brokenithm</option>
|
||||||
<option value="brokenithm-ground">Brokenithm, Ground only</option>
|
<option value="brokenithm-ground">Brokenithm, Ground only (WIP)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,22 +1,44 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
export let data: Array<number>;
|
||||||
|
|
||||||
let topDatas = Array(16).fill(0);
|
let topDatas = Array(16).fill(0);
|
||||||
let botDatas = Array(16).fill(0);
|
let botDatas = Array(16).fill(0);
|
||||||
let ledDatas = Array(31).fill(0).map((_, idx) => ({
|
let ledDatas = Array(31)
|
||||||
color: !!(idx % 2) ? "f0f" : "ff0",
|
.fill(0)
|
||||||
spec: idx % 2
|
.map((_, idx) => ({
|
||||||
}))
|
color: !!(idx % 2) ? "#f0f" : "#ff0",
|
||||||
|
spec: idx % 2,
|
||||||
|
}));
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (data.length === 131) {
|
||||||
|
// console.log(data);
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
topDatas[i] = data[i * 2 + 1];
|
||||||
|
botDatas[i] = data[i * 2];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 31; i++) {
|
||||||
|
ledDatas[i].color = `rgb(${data[38 + i * 3]}, ${data[39 + i * 3]}, ${
|
||||||
|
data[40 + i * 3]
|
||||||
|
})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="preview">
|
<main class="preview">
|
||||||
<div class="air"></div>
|
<div class="air" />
|
||||||
<div class="ground">
|
<div class="ground">
|
||||||
<div class="ground-led">
|
<div class="ground-led">
|
||||||
<div class="ground-row">
|
<div class="ground-row">
|
||||||
<div class="ground-led-2"></div>
|
<div class="ground-led-2" />
|
||||||
{#each ledDatas as {color, spec}, idx (idx)}
|
{#each ledDatas as { color, spec }, idx (idx)}
|
||||||
<div class={`ground-led-${spec}`} style={`background-color: #${color}`}></div>
|
<div
|
||||||
|
class={`ground-led-${spec}`}
|
||||||
|
style={`background-color: ${color}`}
|
||||||
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
<div class="ground-led-2"></div>
|
<div class="ground-led-2" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ground-btn">
|
<div class="ground-btn">
|
||||||
|
Loading…
Reference in New Issue
Block a user