1
0
mirror of https://github.com/4yn/slidershim.git synced 2024-11-30 16:24:27 +01:00

add brokestalgia controller

This commit is contained in:
4yn 2022-03-18 20:58:34 +08:00
parent 8fbb9e4d9f
commit d89de5e6fc
8 changed files with 221 additions and 33 deletions

View File

@ -3,7 +3,9 @@ extern crate slider_io;
use std::io; use std::io;
use slider_io::{ use slider_io::{
device::brokenithm::BrokenithmJob, shared::worker::AsyncHaltableWorker, state::SliderState, device::{brokenithm::BrokenithmJob, config::BrokenithmSpec},
shared::worker::AsyncHaltableWorker,
state::SliderState,
}; };
#[tokio::main] #[tokio::main]
@ -14,7 +16,10 @@ async fn main() {
let state = SliderState::new(); let state = SliderState::new();
let _worker = AsyncHaltableWorker::new("brokenithm", BrokenithmJob::new(&state, &false, &false)); let _worker = AsyncHaltableWorker::new(
"brokenithm",
BrokenithmJob::new(&state, &BrokenithmSpec::Nostalgia, &false),
);
let mut input = String::new(); let mut input = String::new();
io::stdin().read_line(&mut input).unwrap(); io::stdin().read_line(&mut input).unwrap();
} }

View File

@ -40,14 +40,14 @@ impl Context {
match &config.device_mode { match &config.device_mode {
DeviceMode::None => (None, None, None), DeviceMode::None => (None, None, None),
DeviceMode::Brokenithm { DeviceMode::Brokenithm {
ground_only, spec,
lights_enabled, lights_enabled,
} => ( } => (
None, None,
None, None,
Some(AsyncHaltableWorker::new( Some(AsyncHaltableWorker::new(
"brokenithm", "brokenithm",
BrokenithmJob::new(&state, ground_only, lights_enabled), BrokenithmJob::new(&state, spec, lights_enabled),
)), )),
), ),
DeviceMode::Hardware { spec, disable_air } => ( DeviceMode::Hardware { spec, disable_air } => (

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,135 @@
<!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" sizes="192x192" href="/icon.png" />
<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 {
-ms-interpolation-mode: nearest-neighbor;
image-rendering: crisp-edges;
image-rendering: pixelated;
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="1"></div>
<div class="key" data-kflag="2"></div>
<div class="key" data-kflag="3"></div>
<div class="key" data-kflag="4"></div>
<div class="key" data-kflag="5"></div>
<div class="key" data-kflag="6"></div>
<div class="key" data-kflag="7"></div>
<div class="key" data-kflag="8"></div>
<div class="key" data-kflag="9"></div>
<div class="key" data-kflag="10"></div>
<div class="key" data-kflag="11"></div>
<div class="key" data-kflag="12"></div>
<div class="key" data-kflag="13"></div>
<div class="key" data-kflag="14"></div>
<div class="key" data-kflag="15"></div>
<div class="key" data-kflag="16"></div>
<div class="key" data-kflag="17"></div>
<div class="key" data-kflag="18"></div>
<div class="key" data-kflag="19"></div>
<div class="key" data-kflag="20"></div>
<div class="key" data-kflag="21"></div>
<div class="key" data-kflag="22"></div>
<div class="key" data-kflag="23"></div>
<div class="key" data-kflag="24"></div>
<div class="key" data-kflag="25"></div>
<div class="key" data-kflag="26"></div>
<div class="key" data-kflag="27"></div>
</div>
</div>
</div>
<script src="/config.js"></script>
<script>
var allAir = true;
</script>
<script src="/app.js"></script>
</body>
</html>

View File

@ -46,7 +46,7 @@ const compileKey = (key) => {
almostLeft: !!prev ? key.offsetLeft + key.offsetWidth / 4 : -99999, almostLeft: !!prev ? key.offsetLeft + key.offsetWidth / 4 : -99999,
almostRight: !!next ? key.offsetLeft + (key.offsetWidth * 3) / 4 : 99999, almostRight: !!next ? key.offsetLeft + (key.offsetWidth * 3) / 4 : 99999,
kflag: parseInt(key.dataset.kflag) + (parseInt(key.dataset.air) ? 32 : 0), kflag: parseInt(key.dataset.kflag) + (parseInt(key.dataset.air) ? 32 : 0),
isAir: parseInt(key.dataset.air) ? true : false, isAir: parseInt(key.dataset.air) ? true : window.allAir || false,
prevKeyRef: prev, prevKeyRef: prev,
prevKeyKflag: prev prevKeyKflag: prev
? parseInt(prev.dataset.kflag) + (parseInt(prev.dataset.air) ? 32 : 0) ? parseInt(prev.dataset.kflag) + (parseInt(prev.dataset.air) ? 32 : 0)
@ -70,9 +70,9 @@ const compileKeys = () => {
keys = document.getElementsByClassName("key"); keys = document.getElementsByClassName("key");
airKeys = []; airKeys = [];
touchKeys = []; touchKeys = [];
for (var i = 0, key; i < keys.length; i++) { for (var i = 0; i < keys.length; i++) {
const compiledKey = compileKey(keys[i]); const compiledKey = compileKey(keys[i]);
if (!compiledKey.isAir) { if (compiledKey.kflag < 32) {
touchKeys.push(compiledKey); touchKeys.push(compiledKey);
} else { } else {
airKeys.push(compiledKey); airKeys.push(compiledKey);
@ -80,6 +80,38 @@ const compileKeys = () => {
allKeys.push(compiledKey); allKeys.push(compiledKey);
} }
touchKeys.memo = {};
airKeys.memo = {};
touchKeys.getAxis = (x, y) => x;
airKeys.getAxis = (x, y) => y;
var getKey = function (x, y) {
var c = this.getAxis(x, y);
var res = this.memo[c];
if (res === undefined) {
for (var i = 0; i < this.length; i++) {
if (isInside(x, y, this[i])) {
res = this[i];
break;
}
}
this.memo[c] = res;
}
return res;
};
touchKeys.getKey = getKey;
airKeys.getKey = getKey;
for (var i = 0; i < window.outerWidth; i++) {
touchKeys.getKey(i, touchKeys[0].top);
}
for (var i = 0; i < window.outerHeight; i++) {
airKeys.getKey(airKeys[0].left, i);
}
if (!config.invert) { if (!config.invert) {
// Not inverted // Not inverted
topKeys = airKeys; topKeys = airKeys;
@ -95,15 +127,9 @@ const compileKeys = () => {
const getKey = (x, y) => { const getKey = (x, y) => {
if (y < midline) { if (y < midline) {
for (var i = 0; i < topKeys.length; i++) { return topKeys.getKey(x, y);
if (isInside(x, y, topKeys[i])) return topKeys[i];
}
} else { } else {
for (var i = 0; i < bottomKeys.length; i++) { return bottomKeys.getKey(x, y);
if (isInside(x, y, bottomKeys[i])) {
return bottomKeys[i];
}
}
} }
return null; return null;
}; };
@ -241,9 +267,9 @@ const updateLed = (data) => {
canvasData.data[i * 4 + 5] = buf[i * 3 + 1]; // g canvasData.data[i * 4 + 5] = buf[i * 3 + 1]; // g
canvasData.data[i * 4 + 6] = buf[i * 3 + 2]; // b canvasData.data[i * 4 + 6] = buf[i * 3 + 2]; // b
} }
canvasData.data[0] = buf[0] canvasData.data[0] = buf[0];
canvasData.data[1] = buf[1] canvasData.data[1] = buf[1];
canvasData.data[2] = buf[2] canvasData.data[2] = buf[2];
canvasData.data[128] = buf[90]; canvasData.data[128] = buf[90];
canvasData.data[129] = buf[91]; canvasData.data[129] = buf[91];
canvasData.data[130] = buf[92]; canvasData.data[130] = buf[92];

View File

@ -18,7 +18,7 @@ use tokio::{
use tokio_tungstenite::WebSocketStream; use tokio_tungstenite::WebSocketStream;
use tungstenite::{handshake, Message}; use tungstenite::{handshake, Message};
use crate::{shared::worker::AsyncHaltableJob, state::SliderState}; use crate::{device::config::BrokenithmSpec, shared::worker::AsyncHaltableJob, state::SliderState};
// https://levelup.gitconnected.com/handling-websocket-and-http-on-the-same-port-with-rust-f65b770722c9 // https://levelup.gitconnected.com/handling-websocket-and-http-on-the-same-port-with-rust-f65b770722c9
@ -36,6 +36,7 @@ async fn error_response() -> Result<Response<Body>, Infallible> {
static BROKENITHM_STR_FILES: phf::Map<&'static str, (&'static str, &'static str)> = phf_map! { static BROKENITHM_STR_FILES: phf::Map<&'static str, (&'static str, &'static str)> = phf_map! {
"app.js" => (include_str!("./brokenithm-www/app.js"), "text/javascript"), "app.js" => (include_str!("./brokenithm-www/app.js"), "text/javascript"),
"config.js" => (include_str!("./brokenithm-www/config.js"), "text/javascript"), "config.js" => (include_str!("./brokenithm-www/config.js"), "text/javascript"),
"index-ns.html" => (include_str!("./brokenithm-www/index-ns.html"), "text/html"),
"index-go.html" => (include_str!("./brokenithm-www/index-go.html"), "text/html"), "index-go.html" => (include_str!("./brokenithm-www/index-go.html"), "text/html"),
"index.html" => (include_str!("./brokenithm-www/index.html"), "text/html"), "index.html" => (include_str!("./brokenithm-www/index.html"), "text/html"),
}; };
@ -234,7 +235,7 @@ async fn handle_request(
request: Request<Body>, request: Request<Body>,
remote_addr: SocketAddr, remote_addr: SocketAddr,
state: SliderState, state: SliderState,
ground_only: bool, spec: BrokenithmSpec,
lights_enabled: bool, lights_enabled: bool,
) -> Result<Response<Body>, Infallible> { ) -> Result<Response<Body>, Infallible> {
let method = request.method(); let method = request.method();
@ -252,9 +253,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) => match ground_only { ("/", false) | ("/index.html", false) => match spec {
false => serve_file("index.html").await, BrokenithmSpec::Basic => serve_file("index.html").await,
true => serve_file("index-go.html").await, BrokenithmSpec::GroundOnly => serve_file("index-go.html").await,
BrokenithmSpec::Nostalgia => serve_file("index-ns.html").await,
}, },
(filename, false) => serve_file(&filename[1..]).await, (filename, false) => serve_file(&filename[1..]).await,
("/ws", true) => handle_websocket(request, state, lights_enabled).await, ("/ws", true) => handle_websocket(request, state, lights_enabled).await,
@ -264,15 +266,15 @@ async fn handle_request(
pub struct BrokenithmJob { pub struct BrokenithmJob {
state: SliderState, state: SliderState,
ground_only: bool, spec: BrokenithmSpec,
lights_enabled: bool, lights_enabled: bool,
} }
impl BrokenithmJob { impl BrokenithmJob {
pub fn new(state: &SliderState, ground_only: &bool, lights_enabled: &bool) -> Self { pub fn new(state: &SliderState, spec: &BrokenithmSpec, lights_enabled: &bool) -> Self {
Self { Self {
state: state.clone(), state: state.clone(),
ground_only: *ground_only, spec: spec.clone(),
lights_enabled: *lights_enabled, lights_enabled: *lights_enabled,
} }
} }
@ -282,15 +284,17 @@ impl BrokenithmJob {
impl AsyncHaltableJob for BrokenithmJob { impl AsyncHaltableJob 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 spec = self.spec.clone();
let lights_enabled = self.lights_enabled; let lights_enabled = self.lights_enabled;
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();
let make_spec = spec.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, ground_only, lights_enabled) let spec = make_spec.clone();
handle_request(request, remote_addr, svc_state, spec, lights_enabled)
})) }))
} }
}); });

View File

@ -7,6 +7,13 @@ pub enum HardwareSpec {
Yuancon, Yuancon,
} }
#[derive(Debug, Clone)]
pub enum BrokenithmSpec {
Basic,
GroundOnly,
Nostalgia,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum DeviceMode { pub enum DeviceMode {
None, None,
@ -15,7 +22,7 @@ pub enum DeviceMode {
disable_air: bool, disable_air: bool,
}, },
Brokenithm { Brokenithm {
ground_only: bool, spec: BrokenithmSpec,
lights_enabled: bool, lights_enabled: bool,
}, },
DivaSlider { DivaSlider {
@ -45,13 +52,23 @@ impl DeviceMode {
brightness: u8::try_from(v["divaBrightness"].as_i64()?).ok()?, brightness: u8::try_from(v["divaBrightness"].as_i64()?).ok()?,
}, },
"brokenithm" => DeviceMode::Brokenithm { "brokenithm" => DeviceMode::Brokenithm {
ground_only: v["disableAirStrings"].as_bool()?, spec: match v["disableAirStrings"].as_bool()? {
false => BrokenithmSpec::Basic,
true => BrokenithmSpec::GroundOnly,
},
lights_enabled: false, lights_enabled: false,
}, },
"brokenithm-led" => DeviceMode::Brokenithm { "brokenithm-led" => DeviceMode::Brokenithm {
ground_only: v["disableAirStrings"].as_bool()?, spec: match v["disableAirStrings"].as_bool()? {
false => BrokenithmSpec::Basic,
true => BrokenithmSpec::GroundOnly,
},
lights_enabled: true, lights_enabled: true,
}, },
"brokenithm-nostalgia" => DeviceMode::Brokenithm {
spec: BrokenithmSpec::Nostalgia,
lights_enabled: false,
},
_ => return None, _ => return None,
}) })
} }

View File

@ -177,10 +177,11 @@
<option value="diva">Slider over Serial</option> <option value="diva">Slider over Serial</option>
<option value="brokenithm">Brokenithm</option> <option value="brokenithm">Brokenithm</option>
<option value="brokenithm-led">Brokenithm + Led</option> <option value="brokenithm-led">Brokenithm + Led</option>
<option value="brokenithm-nostalgia">Brokestalgia (28k)</option>
</select> </select>
</div> </div>
</div> </div>
{#if deviceMode.slice(0, 8) === "tasoller" || deviceMode.slice(0, 7) === "yuancon" || deviceMode.slice(0, 10) === "brokenithm"} {#if deviceMode.slice(0, 8) === "tasoller" || deviceMode.slice(0, 7) === "yuancon" || (deviceMode.slice(0, 10) === "brokenithm" && deviceMode !== "brokenithm-nostalgia")}
<div class="row"> <div class="row">
<div class="label" /> <div class="label" />
<div class="input"> <div class="input">