From b239ca71603f672c835dbe733a84f8a275ebf421 Mon Sep 17 00:00:00 2001 From: Unrud Date: Tue, 25 Apr 2023 01:38:50 +0200 Subject: [PATCH] Use ECMAScript modules --- webdata/app/compat.mjs | 76 ++++ webdata/app/inputcontroller.mjs | 121 ++++++ webdata/app/keyboard.mjs | 74 ++++ webdata/app/main.mjs | 53 +++ webdata/app/mouse.mjs | 74 ++++ webdata/app/sha256.mjs | 9 + webdata/app/socket.mjs | 66 ++++ webdata/app/touchpad.mjs | 232 ++++++++++++ webdata/app/ui.mjs | 173 +++++++++ webdata/fn.js | 635 -------------------------------- webdata/index.html | 3 +- webdata/sha256.js | 27 -- 12 files changed, 879 insertions(+), 664 deletions(-) create mode 100644 webdata/app/compat.mjs create mode 100644 webdata/app/inputcontroller.mjs create mode 100644 webdata/app/keyboard.mjs create mode 100644 webdata/app/main.mjs create mode 100644 webdata/app/mouse.mjs create mode 100644 webdata/app/sha256.mjs create mode 100644 webdata/app/socket.mjs create mode 100644 webdata/app/touchpad.mjs create mode 100644 webdata/app/ui.mjs delete mode 100644 webdata/fn.js delete mode 100644 webdata/sha256.js diff --git a/webdata/app/compat.mjs b/webdata/app/compat.mjs new file mode 100644 index 0000000..e9328a9 --- /dev/null +++ b/webdata/app/compat.mjs @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +export const fullscreenEnabled = () => { + return (document.fullscreenEnabled || + document.webkitFullscreenEnabled || + false); +}; + +export const requestFullscreen = (element, options) => { + if (element.requestFullscreen) { + element.requestFullscreen(options); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(options); + } +}; + +export const exitFullscreen = () => { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } +}; + +export const fullscreenElement = () => { + return (document.fullscreenElement || + document.webkitFullscreenElement || + null); +}; + +export const addFullscreenchangeEventListener = (listener) => { + if ("onfullscreenchange" in document) { + document.addEventListener("fullscreenchange", listener); + } else if ("onwebkitfullscreenchange" in document) { + document.addEventListener("webkitfullscreenchange", listener); + } +}; + +export const requestPointerLock = (element) => { + if (element.requestPointerLock) { + element.requestPointerLock(); + } +}; + +export const exitPointerLock = () => { + if (document.exitPointerLock) { + document.exitPointerLock(); + } +}; + +export const pointerLockElement = () => { + return document.pointerLockElement || null; +}; + +export const addPointerlockchangeEventListener = (listener) => { + if ("onpointerlockchange" in document) { + document.addEventListener("pointerlockchange", listener); + } +}; diff --git a/webdata/app/inputcontroller.mjs b/webdata/app/inputcontroller.mjs new file mode 100644 index 0000000..a2984fb --- /dev/null +++ b/webdata/app/inputcontroller.mjs @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +export const POINTER_BUTTON_LEFT = 0; +export const POINTER_BUTTON_RIGHT = 1; +export const POINTER_BUTTON_MIDDLE = 2; + +export const KEY_VOLUME_MUTE = 0; +export const KEY_VOLUME_DOWN = 1; +export const KEY_VOLUME_UP = 2; +export const KEY_MEDIA_PLAY_PAUSE = 3; +export const KEY_MEDIA_PREV_TRACK = 4; +export const KEY_MEDIA_NEXT_TRACK = 5; +export const KEY_BROWSER_BACK = 6; +export const KEY_BROWSER_FORWARD = 7; +export const KEY_SUPER = 8; +export const KEY_LEFT = 9; +export const KEY_RIGHT = 10; +export const KEY_UP = 11; +export const KEY_DOWN = 12; +export const KEY_HOME = 13; +export const KEY_END = 14; +export const KEY_BACK_SPACE = 15; +export const KEY_DELETE = 16; +export const KEY_RETURN = 17; + +export default class InputController { + #updateRate = 0; + + #moveXSum = 0; + #moveYSum = 0; + #scrollHSum = 0; + #scrollVSum = 0; + #scrolling = false; + #scrollFinish = false; + #updateTimeoutActive = false; + #socket; + + constructor(socket) { + this.#socket = socket; + } + + configure(config) { + this.#updateRate = config.updateRate; + } + + #startUpdate(fromTimeout) { + if (this.#updateTimeoutActive && !fromTimeout) { + return; + } + this.#updateTimeoutActive = false; + let finished = true; + const xInt = Math.trunc(this.#moveXSum); + const yInt = Math.trunc(this.#moveYSum); + if (xInt != 0 || yInt != 0) { + this.#socket.send("m" + xInt + ";" + yInt); + this.#moveXSum -= xInt; + this.#moveYSum -= yInt; + finished = false; + } + const hInt = Math.trunc(this.#scrollHSum); + const vInt = Math.trunc(this.#scrollVSum); + if (hInt != 0 || vInt != 0) { + this.#socket.send((this.#scrollFinish ? "S" : "s") + hInt + ";" + vInt); + this.#scrollHSum -= hInt; + this.#scrollVSum -= vInt; + this.#scrolling = !this.#scrollFinish; + this.#scrollFinish = false; + finished = false; + } else if (this.#scrollFinish && this.#scrolling) { + this.#socket.send("S"); + this.#scrolling = false; + this.#scrollFinish = false; + } + this.#updateTimeoutActive = !finished && this.#updateRate > 0; + if (this.#updateTimeoutActive) { + setTimeout(this.#startUpdate.bind(this), 1000 / this.#updateRate, true); + } + } + + pointerMove(deltaX, deltaY) { + this.#moveXSum += deltaX; + this.#moveYSum += deltaY; + this.#startUpdate(); + } + + pointerScroll(deltaHorizontal, deltaVertical, finish) { + this.#scrollHSum += deltaHorizontal; + this.#scrollVSum += deltaVertical; + this.#scrollFinish |= finish; + this.#startUpdate(); + }; + + pointerButton(button, press) { + this.#socket.send("b" + button + ";" + (press ? 1 : 0)); + } + + keyboardKey(key) { + this.#socket.send("k" + key); + } + + keyboardText(text) { + this.#socket.send("t" + text); + } +} diff --git a/webdata/app/keyboard.mjs b/webdata/app/keyboard.mjs new file mode 100644 index 0000000..84195d9 --- /dev/null +++ b/webdata/app/keyboard.mjs @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +import { + KEY_SUPER, KEY_BACK_SPACE, KEY_RETURN, KEY_DELETE, KEY_HOME, KEY_END, + KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, +} from "./inputcontroller.mjs"; + +export default class Keyboard { + #inputController; + #checkAllowedCallback; + + constructor(inputController, checkAllowedCallback) { + this.#inputController = inputController; + this.#checkAllowedCallback = checkAllowedCallback; + document.addEventListener("keydown", this.#handleKeydown.bind(this)); + } + + configure() {} + + #handleKeydown(event) { + if (!this.#checkAllowedCallback() || + event.ctrlKey || event.altKey || event.isComposing) { + return; + } + let key = null; + if (event.key == "OS" || event.key == "Super" || event.key == "Meta") { + key = KEY_SUPER; + } else if (event.key == "Backspace") { + key = KEY_BACK_SPACE; + } else if (event.key == "Enter") { + key = KEY_RETURN; + } else if (event.key == "Delete") { + key = KEY_DELETE; + } else if (event.key == "Home") { + key = KEY_HOME; + } else if (event.key == "End") { + key = KEY_END; + } else if (event.key == "Left" || event.key == "ArrowLeft") { + key = KEY_LEFT; + } else if (event.key == "Right" || event.key == "ArrowRight") { + key = KEY_RIGHT; + } else if (event.key == "Up" || event.key == "ArrowUp") { + key = KEY_UP; + } else if (event.key == "Down" || event.key == "ArrowDown") { + key = KEY_DOWN; + } + if (key != null) { + if (!event.shiftKey) { + event.preventDefault(); + this.#inputController.keyboardKey(key); + } + } else if (event.key.length == 1) { + event.preventDefault(); + this.#inputController.keyboardText(event.key); + } + } +} diff --git a/webdata/app/main.mjs b/webdata/app/main.mjs new file mode 100644 index 0000000..5477b89 --- /dev/null +++ b/webdata/app/main.mjs @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +import InputController, * as inputcontrollerModule from "./inputcontroller.mjs"; +import Socket from "./socket.mjs"; +import UI from "./ui.mjs"; + +const url = new URL("ws", location.href); +url.protocol = url.protocol == "http:" ? "ws:" : "wss:"; + +const socket = new Socket(url, window.location.hash.substr(1)); +const inputController = new InputController(socket); +const ui = new UI(inputController); + +socket.addEventListener("config", (event) => { + const config = event.detail; + inputController.configure(config); + ui.configure(config); +}); + +socket.addEventListener("close", () => { + ui.close(); +}); + +window.app = { + key: inputController.keyboardKey.bind(inputController), + text: inputController.keyboardText.bind(inputController), + toggleFullscreen: ui.toggleFullscreen.bind(ui), + showTextInput: ui.showTextInput.bind(ui), + showKeys: ui.showKeys.bind(ui), + setKeysPage: ui.setKeysPage.bind(ui), +}; +for (const name in inputcontrollerModule) { + if (name.startsWith("KEY_")) { + window.app[name] = inputcontrollerModule[name]; + } +} diff --git a/webdata/app/mouse.mjs b/webdata/app/mouse.mjs new file mode 100644 index 0000000..8158eb3 --- /dev/null +++ b/webdata/app/mouse.mjs @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +export default class Mouse { + #moveSpeed = 1; + #scrollSpeed = 1; + + #buttons = 0; + #inputController; + + constructor (inputController, element) { + this.#inputController = inputController; + for (const type of ["touchstart", "touchend", "touchcancel", "touchmove"]) { + element.addEventListener(type, (event) => { + event.preventDefault(); + }); + } + element.addEventListener("mousedown", this.#handleMouseDownAndUp.bind(this)); + element.addEventListener("mouseup", this.#handleMouseDownAndUp.bind(this)); + element.addEventListener("mousemove", this.#handleMousemove.bind(this)); + element.addEventListener("wheel", this.#handleWheel.bind(this)); + } + + configure(config) { + this.#moveSpeed = config.mouseMoveSpeed; + this.#scrollSpeed = config.mouseScrollSpeed; + } + + #updateButtons(newButtons) { + for (let button = 0; button < 3; button += 1) { + const flag = 1 << button; + if ((newButtons&flag) != (this.#buttons & flag)) { + this.#inputController.pointerButton(button, newButtons & flag); + } + } + this.#buttons = newButtons; + } + + #handleMouseDownAndUp(event) { + this.#updateButtons(event.buttons); + } + + #handleMousemove(event) { + this.#inputController.pointerMove( + event.movementX * this.#moveSpeed, event.movementY * this.#moveSpeed); + } + + #handleWheel(event) { + if (event.deltaMode == WheelEvent.DOM_DELTA_PIXEL) { + this.#inputController.pointerScroll( + event.deltaX * this.#scrollSpeed, event.deltaY * this.#scrollSpeed, true); + } else if (event.deltaMode == WheelEvent.DOM_DELTA_LINE) { + this.#inputController.pointerScroll( + event.deltaX * 20 * this.#scrollSpeed, event.deltaY * 20 * this.#scrollSpeed, + true); + } + } +} diff --git a/webdata/app/sha256.mjs b/webdata/app/sha256.mjs new file mode 100644 index 0000000..3f7cd3f --- /dev/null +++ b/webdata/app/sha256.mjs @@ -0,0 +1,9 @@ +/** + * A JavaScript implementation of the SHA family of hashes - defined in FIPS PUB 180-4, FIPS PUB 202, + * and SP 800-185 - as well as the corresponding HMAC implementation as defined in FIPS PUB 198-1. + * + * Copyright 2008-2022 Brian Turek, 1998-2009 Paul Johnston & Contributors + * Distributed under the BSD License + * See http://caligatio.github.com/jsSHA/ for more information + */ +const t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function r(t,r,n,i){let e,s,o;const h=r||[0],u=(n=n||0)>>>3,f=-1===i?3:0;for(e=0;e>>2,h.length<=s&&h.push(0),h[s]|=t[e]<<8*(f+i*(o%4));return{value:h,binLen:8*t.length+n}}function n(n,i,e){switch(i){case"UTF8":case"UTF16BE":case"UTF16LE":break;default:throw new Error("encoding must be UTF8, UTF16BE, or UTF16LE")}switch(n){case"HEX":return function(t,r,n){return function(t,r,n,i){let e,s,o,h;if(0!=t.length%2)throw new Error("String of HEX type must be in byte increments");const u=r||[0],f=(n=n||0)>>>3,c=-1===i?3:0;for(e=0;e>>1)+f,o=h>>>2;u.length<=o;)u.push(0);u[o]|=s<<8*(c+i*(h%4))}return{value:u,binLen:4*t.length+n}}(t,r,n,e)};case"TEXT":return function(t,r,n){return function(t,r,n,i,e){let s,o,h,u,f,c,a,w,A=0;const E=n||[0],l=(i=i||0)>>>3;if("UTF8"===r)for(a=-1===e?3:0,h=0;hs?o.push(s):2048>s?(o.push(192|s>>>6),o.push(128|63&s)):55296>s||57344<=s?o.push(224|s>>>12,128|s>>>6&63,128|63&s):(h+=1,s=65536+((1023&s)<<10|1023&t.charCodeAt(h)),o.push(240|s>>>18,128|s>>>12&63,128|s>>>6&63,128|63&s)),u=0;u>>2;E.length<=f;)E.push(0);E[f]|=o[u]<<8*(a+e*(c%4)),A+=1}else for(a=-1===e?2:0,w="UTF16LE"===r&&1!==e||"UTF16LE"!==r&&1===e,h=0;h>>8),c=A+l,f=c>>>2;E.length<=f;)E.push(0);E[f]|=s<<8*(a+e*(c%4)),A+=2}return{value:E,binLen:8*A+i}}(t,i,r,n,e)};case"B64":return function(r,n,i){return function(r,n,i,e){let s,o,h,u,f,c,a,w=0;const A=n||[0],E=(i=i||0)>>>3,l=-1===e?3:0,p=r.indexOf("=");if(-1===r.search(/^[a-zA-Z0-9=+/]+$/))throw new Error("Invalid character in base-64 string");if(r=r.replace(/=/g,""),-1!==p&&pu)throw Error("numRounds must a integer >= 1");if(0===c.lastIndexOf("SHA-",0))if(q=function(b,a){return A(b,a,c)},y=function(b,a,l,f){var g,e;if("SHA-224"===c||"SHA-256"===c)g=(a+65>>>9<<4)+15,e=16;else throw Error("Unexpected error in SHA-2 implementation");for(;b.length<=g;)b.push(0);b[a>>>5]|=128<<24-a%32;a=a+l;b[g]=a&4294967295; -b[g-1]=a/4294967296|0;l=b.length;for(a=0;a>>3;g=e/4-1;if(eb/8){for(;a.length<=g;)a.push(0);a[g]&=4294967040}for(b=0;b<=g;b+=1)t[b]=a[b]^909522486,r[b]=a[b]^1549556828;n=q(t,n);l=h;m=!0};this.update=function(a){var c,f,e,d=0,p=h>>>5;c=k(a,b,g);a=c.binLen;f=c.value;c=a>>>5;for(e=0;e>> -5);g=a%h;z=!0};this.getHash=function(a,f){var d,h,k,q;if(!0===m)throw Error("Cannot call getHash after setting HMAC key");k=C(f);switch(a){case "HEX":d=function(a){return D(a,e,k)};break;case "B64":d=function(a){return E(a,e,k)};break;case "BYTES":d=function(a){return F(a,e)};break;case "ARRAYBUFFER":try{h=new ArrayBuffer(0)}catch(v){throw Error("ARRAYBUFFER not supported by this environment");}d=function(a){return G(a,e)};break;default:throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER"); -}q=y(b.slice(),g,l,p(n));for(h=1;h>>2]>>>8*(3+b%4*-1),l+="0123456789abcdef".charAt(g>>>4&15)+"0123456789abcdef".charAt(g&15);return d.outputUpper?l.toUpperCase():l}function E(c,a,d){var l="",b=a/8,g,f,n;for(g=0;g>>2]:0,n=g+2>>2]:0,n=(c[g>>>2]>>>8*(3+g%4*-1)&255)<<16|(f>>>8*(3+(g+1)%4*-1)&255)<<8|n>>>8*(3+(g+2)%4*-1)&255,f=0;4>f;f+=1)8*g+6*f<=a?l+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(n>>> -6*(3-f)&63):l+=d.b64Pad;return l}function F(c,a){var d="",l=a/8,b,g;for(b=0;b>>2]>>>8*(3+b%4*-1)&255,d+=String.fromCharCode(g);return d}function G(c,a){var d=a/8,l,b=new ArrayBuffer(d),g;g=new Uint8Array(b);for(l=0;l>>2]>>>8*(3+l%4*-1)&255;return b}function C(c){var a={outputUpper:!1,b64Pad:"=",shakeLen:-1};c=c||{};a.outputUpper=c.outputUpper||!1;!0===c.hasOwnProperty("b64Pad")&&(a.b64Pad=c.b64Pad);if("boolean"!==typeof a.outputUpper)throw Error("Invalid outputUpper formatting option"); -if("string"!==typeof a.b64Pad)throw Error("Invalid b64Pad formatting option");return a}function B(c,a){var d;switch(a){case "UTF8":case "UTF16BE":case "UTF16LE":break;default:throw Error("encoding must be UTF8, UTF16BE, or UTF16LE");}switch(c){case "HEX":d=function(a,b,c){var f=a.length,d,k,e,h,q;if(0!==f%2)throw Error("String of HEX type must be in byte increments");b=b||[0];c=c||0;q=c>>>3;for(d=0;d>>1)+q;for(e=h>>>2;b.length<=e;)b.push(0);b[e]|=k<<8*(3+h%4*-1)}return{value:b,binLen:4*f+c}};break;case "TEXT":d=function(c,b,d){var f,n,k=0,e,h,q,m,p,r;b=b||[0];d=d||0;q=d>>>3;if("UTF8"===a)for(r=3,e=0;ef?n.push(f):2048>f?(n.push(192|f>>>6),n.push(128|f&63)):55296>f||57344<=f?n.push(224|f>>>12,128|f>>>6&63,128|f&63):(e+=1,f=65536+((f&1023)<<10|c.charCodeAt(e)&1023),n.push(240|f>>>18,128|f>>>12&63,128|f>>>6&63,128|f&63)),h=0;h>>2;b.length<=m;)b.push(0);b[m]|=n[h]<<8*(r+p%4*-1);k+=1}else if("UTF16BE"===a||"UTF16LE"===a)for(r=2,n="UTF16LE"===a&&!0||"UTF16LE"!==a&&!1,e=0;e>>8);p=k+q;for(m=p>>>2;b.length<=m;)b.push(0);b[m]|=f<<8*(r+p%4*-1);k+=2}return{value:b,binLen:8*k+d}};break;case "B64":d=function(a,b,c){var f=0,d,k,e,h,q,m,p;if(-1===a.search(/^[a-zA-Z0-9=+\/]+$/))throw Error("Invalid character in base-64 string");k=a.indexOf("=");a=a.replace(/\=/g, -"");if(-1!==k&&k