From d89de5e6fca48be45af3f609d4ae8d7dd0a2dea3 Mon Sep 17 00:00:00 2001 From: 4yn <4yn@users.noreply.github.com> Date: Fri, 18 Mar 2022 20:58:34 +0800 Subject: [PATCH] add brokestalgia controller --- src-slider_io/src/bin/test_brokenithm.rs | 9 +- src-slider_io/src/context.rs | 4 +- .../src/device/brokenithm-www/app.js | 2 +- .../src/device/brokenithm-www/index-ns.html | 135 ++++++++++++++++++ .../src/device/brokenithm-www/src.js | 54 +++++-- src-slider_io/src/device/brokenithm.rs | 24 ++-- src-slider_io/src/device/config.rs | 23 ++- src/App.svelte | 3 +- 8 files changed, 221 insertions(+), 33 deletions(-) create mode 100644 src-slider_io/src/device/brokenithm-www/index-ns.html diff --git a/src-slider_io/src/bin/test_brokenithm.rs b/src-slider_io/src/bin/test_brokenithm.rs index 65511d4..dbb7028 100644 --- a/src-slider_io/src/bin/test_brokenithm.rs +++ b/src-slider_io/src/bin/test_brokenithm.rs @@ -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(); } diff --git a/src-slider_io/src/context.rs b/src-slider_io/src/context.rs index fdc0e56..fd5f32f 100644 --- a/src-slider_io/src/context.rs +++ b/src-slider_io/src/context.rs @@ -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 } => ( diff --git a/src-slider_io/src/device/brokenithm-www/app.js b/src-slider_io/src/device/brokenithm-www/app.js index 2d45451..5c3a4fe 100644 --- a/src-slider_io/src/device/brokenithm-www/app.js +++ b/src-slider_io/src/device/brokenithm-www/app.js @@ -1 +1 @@ -"use strict";var throttle=function(e,t){var a=!0,n=null;return function o(){var s=this;a?(a=!1,setTimeout(function(){a=!0,n&&o.apply(s)},t),n?(e.apply(this,n),n=null):e.apply(this,arguments)):n=arguments}},keys=document.getElementsByClassName("key"),airKeys=[],midline=0,touchKeys=[],allKeys=[],topKeys=airKeys,bottomKeys=touchKeys,compileKey=function(e){var t=e.previousElementSibling,a=e.nextElementSibling;return{top:e.offsetTop,bottom:e.offsetTop+e.offsetHeight,left:e.offsetLeft,right:e.offsetLeft+e.offsetWidth,almostLeft:t?e.offsetLeft+e.offsetWidth/4:-99999,almostRight:a?e.offsetLeft+3*e.offsetWidth/4:99999,kflag:parseInt(e.dataset.kflag)+(parseInt(e.dataset.air)?32:0),isAir:!!parseInt(e.dataset.air),prevKeyRef:t,prevKeyKflag:t?parseInt(t.dataset.kflag)+(parseInt(t.dataset.air)?32:0):null,nextKeyRef:a,nextKeyKflag:a?parseInt(a.dataset.kflag)+(parseInt(a.dataset.air)?32:0):null,ref:e}},isInside=function(e,t,a){return a.left<=e&&e2)return wsTimeout=0,ws.close(),wsConnected=!1,void wsConnect();wsConnected&&ws.send("alive?")},canvas=document.getElementById("canvas"),canvasCtx=canvas.getContext("2d"),canvasData=canvasCtx.getImageData(0,0,33,1),setupLed=function(){for(var e=0;e<33;e++)canvasData.data[4*e+3]=255};setupLed();var updateLed=function(e){for(var t=new Uint8Array(e),a=0;a<31;a++)canvasData.data[4*a+4]=t[3*a],canvasData.data[4*a+5]=t[3*a+1],canvasData.data[4*a+6]=t[3*a+2];canvasData.data[0]=t[0],canvasData.data[1]=t[1],canvasData.data[2]=t[2],canvasData.data[128]=t[90],canvasData.data[129]=t[91],canvasData.data[130]=t[92],canvasCtx.putImageData(canvasData,0,0)},fs=document.getElementById("fullscreen"),requestFullscreen=function(){!document.fullscreenElement&&screen.height<=1024&&(fs.requestFullscreen?fs.requestFullscreen():fs.mozRequestFullScreen?fs.mozRequestFullScreen():fs.webkitRequestFullScreen&&fs.webkitRequestFullScreen())},throttledRequestFullscreen=throttle(requestFullscreen,3e3),cnt=document.getElementById("main");cnt.addEventListener("touchstart",updateTouches),cnt.addEventListener("touchmove",updateTouches),cnt.addEventListener("touchend",updateTouches);var readConfig=function(e){var t="";e.invert&&(t+=".container, .air-container {flex-flow: column-reverse nowrap;} ");var a=e.bgColor||"rbga(0, 0, 0, 0.9)";e.bgImage?t+="#fullscreen {background: ".concat(a,' url("').concat(e.bgImage,'") fixed center / cover!important; background-repeat: no-repeat;} '):t+="#fullscreen {background: ".concat(a,";} "),"number"==typeof e.ledOpacity&&(0===e.ledOpacity?t+="#canvas {display: none} ":t+="#canvas {opacity: ".concat(e.ledOpacity,"} ")),"string"==typeof e.keyColor&&(t+=".key[data-active] {background-color: ".concat(e.keyColor,";} ")),"string"==typeof e.keyColor&&(t+=".key.air[data-active] {background-color: ".concat(e.lkeyColor,";} ")),"string"==typeof e.keyBorderColor&&(t+=".key {border: 1px solid ".concat(e.keyBorderColor,";} ")),e.keyColorFade&&"number"==typeof e.keyColorFade&&(t+=".key:not([data-active]) {transition: background ".concat(e.keyColorFade,"ms ease-out;} ")),"number"==typeof e.keyHeight&&(0===e.keyHeight?t+=".touch-container {display: none;} ":t+=".touch-container {flex: ".concat(e.keyHeight,";} ")),"number"==typeof e.lkeyHeight&&(0===e.lkeyHeight?t+=".air-container {display: none;} ":t+=".air-container {flex: ".concat(e.keyHeight,";} "));var n=document.createElement("style");n.innerHTML=t,document.head.appendChild(n)},initialize=function(){readConfig(config),compileKeys(),wsConnect(),setInterval(wsWatch,1e3)};initialize(),window.onresize=compileKeys; \ No newline at end of file +"use strict";var throttle=function(e,t){var a=!0,n=null;return function o(){var s=this;a?(a=!1,setTimeout(function(){a=!0,n&&o.apply(s)},t),n?(e.apply(this,n),n=null):e.apply(this,arguments)):n=arguments}},keys=document.getElementsByClassName("key"),airKeys=[],midline=0,touchKeys=[],allKeys=[],topKeys=airKeys,bottomKeys=touchKeys,compileKey=function(e){var t=e.previousElementSibling,a=e.nextElementSibling;return{top:e.offsetTop,bottom:e.offsetTop+e.offsetHeight,left:e.offsetLeft,right:e.offsetLeft+e.offsetWidth,almostLeft:t?e.offsetLeft+e.offsetWidth/4:-99999,almostRight:a?e.offsetLeft+3*e.offsetWidth/4:99999,kflag:parseInt(e.dataset.kflag)+(parseInt(e.dataset.air)?32:0),isAir:!!parseInt(e.dataset.air)||(window.allAir||!1),prevKeyRef:t,prevKeyKflag:t?parseInt(t.dataset.kflag)+(parseInt(t.dataset.air)?32:0):null,nextKeyRef:a,nextKeyKflag:a?parseInt(a.dataset.kflag)+(parseInt(a.dataset.air)?32:0):null,ref:e}},isInside=function(e,t,a){return a.left<=e&&e2)return wsTimeout=0,ws.close(),wsConnected=!1,void wsConnect();wsConnected&&ws.send("alive?")},canvas=document.getElementById("canvas"),canvasCtx=canvas.getContext("2d"),canvasData=canvasCtx.getImageData(0,0,33,1),setupLed=function(){for(var e=0;e<33;e++)canvasData.data[4*e+3]=255};setupLed();var updateLed=function(e){for(var t=new Uint8Array(e),a=0;a<31;a++)canvasData.data[4*a+4]=t[3*a],canvasData.data[4*a+5]=t[3*a+1],canvasData.data[4*a+6]=t[3*a+2];canvasData.data[0]=t[0],canvasData.data[1]=t[1],canvasData.data[2]=t[2],canvasData.data[128]=t[90],canvasData.data[129]=t[91],canvasData.data[130]=t[92],canvasCtx.putImageData(canvasData,0,0)},fs=document.getElementById("fullscreen"),requestFullscreen=function(){!document.fullscreenElement&&screen.height<=1024&&(fs.requestFullscreen?fs.requestFullscreen():fs.mozRequestFullScreen?fs.mozRequestFullScreen():fs.webkitRequestFullScreen&&fs.webkitRequestFullScreen())},throttledRequestFullscreen=throttle(requestFullscreen,3e3),cnt=document.getElementById("main");cnt.addEventListener("touchstart",updateTouches),cnt.addEventListener("touchmove",updateTouches),cnt.addEventListener("touchend",updateTouches);var readConfig=function(e){var t="";e.invert&&(t+=".container, .air-container {flex-flow: column-reverse nowrap;} ");var a=e.bgColor||"rbga(0, 0, 0, 0.9)";e.bgImage?t+="#fullscreen {background: ".concat(a,' url("').concat(e.bgImage,'") fixed center / cover!important; background-repeat: no-repeat;} '):t+="#fullscreen {background: ".concat(a,";} "),"number"==typeof e.ledOpacity&&(0===e.ledOpacity?t+="#canvas {display: none} ":t+="#canvas {opacity: ".concat(e.ledOpacity,"} ")),"string"==typeof e.keyColor&&(t+=".key[data-active] {background-color: ".concat(e.keyColor,";} ")),"string"==typeof e.keyColor&&(t+=".key.air[data-active] {background-color: ".concat(e.lkeyColor,";} ")),"string"==typeof e.keyBorderColor&&(t+=".key {border: 1px solid ".concat(e.keyBorderColor,";} ")),e.keyColorFade&&"number"==typeof e.keyColorFade&&(t+=".key:not([data-active]) {transition: background ".concat(e.keyColorFade,"ms ease-out;} ")),"number"==typeof e.keyHeight&&(0===e.keyHeight?t+=".touch-container {display: none;} ":t+=".touch-container {flex: ".concat(e.keyHeight,";} ")),"number"==typeof e.lkeyHeight&&(0===e.lkeyHeight?t+=".air-container {display: none;} ":t+=".air-container {flex: ".concat(e.keyHeight,";} "));var n=document.createElement("style");n.innerHTML=t,document.head.appendChild(n)},initialize=function(){readConfig(config),compileKeys(),wsConnect(),setInterval(wsWatch,1e3)};initialize(),window.onresize=compileKeys; \ No newline at end of file diff --git a/src-slider_io/src/device/brokenithm-www/index-ns.html b/src-slider_io/src/device/brokenithm-www/index-ns.html new file mode 100644 index 0000000..694d3b3 --- /dev/null +++ b/src-slider_io/src/device/brokenithm-www/index-ns.html @@ -0,0 +1,135 @@ + + + + slidershim-brokenithm + + + + + + + +
+ +
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + diff --git a/src-slider_io/src/device/brokenithm-www/src.js b/src-slider_io/src/device/brokenithm-www/src.js index 3e0d5e8..19b723a 100644 --- a/src-slider_io/src/device/brokenithm-www/src.js +++ b/src-slider_io/src/device/brokenithm-www/src.js @@ -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]; diff --git a/src-slider_io/src/device/brokenithm.rs b/src-slider_io/src/device/brokenithm.rs index 81d55a8..3d22220 100644 --- a/src-slider_io/src/device/brokenithm.rs +++ b/src-slider_io/src/device/brokenithm.rs @@ -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, 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, remote_addr: SocketAddr, state: SliderState, - ground_only: bool, + spec: BrokenithmSpec, lights_enabled: bool, ) -> Result, 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 + 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| { 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) })) } }); diff --git a/src-slider_io/src/device/config.rs b/src-slider_io/src/device/config.rs index 6c51fd9..03de3f3 100644 --- a/src-slider_io/src/device/config.rs +++ b/src-slider_io/src/device/config.rs @@ -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, }) } diff --git a/src/App.svelte b/src/App.svelte index 2128d58..1b988a4 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -177,10 +177,11 @@ + - {#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")}