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 slider_io::{
device::brokenithm::BrokenithmJob, shared::worker::AsyncHaltableWorker, state::SliderState,
device::{brokenithm::BrokenithmJob, config::BrokenithmSpec},
shared::worker::AsyncHaltableWorker,
state::SliderState,
};
#[tokio::main]
@ -14,7 +16,10 @@ async fn main() {
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();
io::stdin().read_line(&mut input).unwrap();
}

View File

@ -40,14 +40,14 @@ impl Context {
match &config.device_mode {
DeviceMode::None => (None, None, None),
DeviceMode::Brokenithm {
ground_only,
spec,
lights_enabled,
} => (
None,
None,
Some(AsyncHaltableWorker::new(
"brokenithm",
BrokenithmJob::new(&state, ground_only, lights_enabled),
BrokenithmJob::new(&state, spec, lights_enabled),
)),
),
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,
almostRight: !!next ? key.offsetLeft + (key.offsetWidth * 3) / 4 : 99999,
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,
prevKeyKflag: prev
? parseInt(prev.dataset.kflag) + (parseInt(prev.dataset.air) ? 32 : 0)
@ -70,9 +70,9 @@ const compileKeys = () => {
keys = document.getElementsByClassName("key");
airKeys = [];
touchKeys = [];
for (var i = 0, key; i < keys.length; i++) {
for (var i = 0; i < keys.length; i++) {
const compiledKey = compileKey(keys[i]);
if (!compiledKey.isAir) {
if (compiledKey.kflag < 32) {
touchKeys.push(compiledKey);
} else {
airKeys.push(compiledKey);
@ -80,6 +80,38 @@ const compileKeys = () => {
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) {
// Not inverted
topKeys = airKeys;
@ -95,15 +127,9 @@ const compileKeys = () => {
const getKey = (x, y) => {
if (y < midline) {
for (var i = 0; i < topKeys.length; i++) {
if (isInside(x, y, topKeys[i])) return topKeys[i];
}
return topKeys.getKey(x, y);
} else {
for (var i = 0; i < bottomKeys.length; i++) {
if (isInside(x, y, bottomKeys[i])) {
return bottomKeys[i];
}
}
return bottomKeys.getKey(x, y);
}
return null;
};
@ -241,9 +267,9 @@ const updateLed = (data) => {
canvasData.data[i * 4 + 5] = buf[i * 3 + 1]; // g
canvasData.data[i * 4 + 6] = buf[i * 3 + 2]; // b
}
canvasData.data[0] = buf[0]
canvasData.data[1] = buf[1]
canvasData.data[2] = buf[2]
canvasData.data[0] = buf[0];
canvasData.data[1] = buf[1];
canvasData.data[2] = buf[2];
canvasData.data[128] = buf[90];
canvasData.data[129] = buf[91];
canvasData.data[130] = buf[92];

View File

@ -18,7 +18,7 @@ use tokio::{
use tokio_tungstenite::WebSocketStream;
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
@ -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! {
"app.js" => (include_str!("./brokenithm-www/app.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.html" => (include_str!("./brokenithm-www/index.html"), "text/html"),
};
@ -234,7 +235,7 @@ async fn handle_request(
request: Request<Body>,
remote_addr: SocketAddr,
state: SliderState,
ground_only: bool,
spec: BrokenithmSpec,
lights_enabled: bool,
) -> Result<Response<Body>, Infallible> {
let method = request.method();
@ -252,9 +253,10 @@ async fn handle_request(
request.uri().path(),
request.headers().contains_key(header::UPGRADE),
) {
("/", false) | ("/index.html", false) => match ground_only {
false => serve_file("index.html").await,
true => serve_file("index-go.html").await,
("/", false) | ("/index.html", false) => match spec {
BrokenithmSpec::Basic => serve_file("index.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,
("/ws", true) => handle_websocket(request, state, lights_enabled).await,
@ -264,15 +266,15 @@ async fn handle_request(
pub struct BrokenithmJob {
state: SliderState,
ground_only: bool,
spec: BrokenithmSpec,
lights_enabled: bool,
}
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 {
state: state.clone(),
ground_only: *ground_only,
spec: spec.clone(),
lights_enabled: *lights_enabled,
}
}
@ -282,15 +284,17 @@ impl 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;
let spec = self.spec.clone();
let lights_enabled = self.lights_enabled;
let make_svc = make_service_fn(|conn: &AddrStream| {
let remote_addr = conn.remote_addr();
let make_svc_state = state.clone();
let make_spec = spec.clone();
async move {
Ok::<_, Infallible>(service_fn(move |request: Request<Body>| {
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,
}
#[derive(Debug, Clone)]
pub enum BrokenithmSpec {
Basic,
GroundOnly,
Nostalgia,
}
#[derive(Debug, Clone)]
pub enum DeviceMode {
None,
@ -15,7 +22,7 @@ pub enum DeviceMode {
disable_air: bool,
},
Brokenithm {
ground_only: bool,
spec: BrokenithmSpec,
lights_enabled: bool,
},
DivaSlider {
@ -45,13 +52,23 @@ impl DeviceMode {
brightness: u8::try_from(v["divaBrightness"].as_i64()?).ok()?,
},
"brokenithm" => DeviceMode::Brokenithm {
ground_only: v["disableAirStrings"].as_bool()?,
spec: match v["disableAirStrings"].as_bool()? {
false => BrokenithmSpec::Basic,
true => BrokenithmSpec::GroundOnly,
},
lights_enabled: false,
},
"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,
},
"brokenithm-nostalgia" => DeviceMode::Brokenithm {
spec: BrokenithmSpec::Nostalgia,
lights_enabled: false,
},
_ => return None,
})
}

View File

@ -177,10 +177,11 @@
<option value="diva">Slider over Serial</option>
<option value="brokenithm">Brokenithm</option>
<option value="brokenithm-led">Brokenithm + Led</option>
<option value="brokenithm-nostalgia">Brokestalgia (28k)</option>
</select>
</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="label" />
<div class="input">