From aa5e6849cb93bdfd8391927c776f0b2c1b8d6bf3 Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Fri, 14 Feb 2025 17:18:33 +0100 Subject: [PATCH] remote keyboard improvements --- com/isoterminal/feat/boot.js | 13 +++- com/isoterminal/feat/remotekeyboard.js | 95 ++++++++++++++++++++------ com/isoterminal/feat/term.js | 9 +-- com/isoterminal/term.js | 7 +- 4 files changed, 91 insertions(+), 33 deletions(-) diff --git a/com/isoterminal/feat/boot.js b/com/isoterminal/feat/boot.js index 340ee69..da92c63 100644 --- a/com/isoterminal/feat/boot.js +++ b/com/isoterminal/feat/boot.js @@ -45,7 +45,18 @@ if( typeof window.PromiseWorker != 'undefined' ){ // if xrsh v86 is able to run { key: "1", title: (opts) => `boot ${String(opts.iso || "").replace(/.*\//,'')} Linux ❤️ `, - init: function(){ this.bootISO() }, + init: function(){ + + // hack to notify href clicks + Term.prototype.href = (a) => { + if( a.href ){ + this.exec(`source /etc/profile.sh; hook href "${a.href}"`) + } + return false + } + + this.bootISO() + }, keyHandler: function(ch){ this.send(ch) } // send to v86 webworker } ) diff --git a/com/isoterminal/feat/remotekeyboard.js b/com/isoterminal/feat/remotekeyboard.js index 3b5edf7..1fb92fc 100644 --- a/com/isoterminal/feat/remotekeyboard.js +++ b/com/isoterminal/feat/remotekeyboard.js @@ -1,34 +1,87 @@ ISOTerminal.prototype.enableRemoteKeyboard = function(opts){ + let service = { + ready: false, + reset: (service) => { + service.ip = localStorage.getItem("keyboardIP") || "" + service.ip = service.ip.trim() + service.state = "need-ip" + service.attempts = 0 + }, + init: function init( mainmenu ){ + this.emit('status',"") + this.emit('enable-console',{stdout:true}) + service.reset(service) + setTimeout( () => { + const clearScreen = "\r" + this.send(clearScreen); + this.send(`\n\rfor instructions\n\rsee ${document.location.origin}/index.html#Remote%20keyboard\n\n\r`) + this.send("enter 'm' for mainmenu\n\n\r") + this.send("keyboard ip-adress> ") + // autofill ip + if( service.ip ){ + for( let i = 0; i < service.ip.length; i++ ) this.send(service.ip.charAt(i)) + } + }, 100 ) + }, + server: (term) => { + try{ + service.addr = `ws://${service.ip}:9090/`; + service.ws = new WebSocket(service.addr) + service.ws.addEventListener("open", () => { + if( service.state == 'listening' ){ + this.send(`\n\rconnected to ${service.addr}! \\o/\n\r`) + this.bootMenu() + } + service.state = 'receiving' + }) + service.ws.addEventListener("close", () => { + service.attempts += 1 + if( service.attempts > 3 && service.state == 'listening'){ + service.reset(service) + this.send(`\n\roops..I did not detect any connection :/\n\r`) + localStorage.setItem("keyboardIP","") // reset ip + this.bootMenu() + }else setTimeout( () => service.server(term), 1000 ) // retry connection + }) // retry on EOF + service.ws.onmessage = function(event) { + if( !event.data ) return + event.data.arrayBuffer().then( (buf) => { + const arr = new Uint8Array(buf) + let string = Array.from(arr, byte => String.fromCharCode(byte)).join('') + term.term.handler(string) + service.state = 'receiving' + localStorage.setItem("keyboardIP",service.ip) // save ip for later + }) + }; + }catch(e){ + console.error(e) + service.reset(service) + localStorage.setItem("keyboardIP","") // reset ip + this.bootMenu() + } + } + } + // initialize REPL ISOTerminal.prototype.boot.menu.push( { key: "k", title: (opts) => "connect a remote keyboard", - init: function( mainmenu ){ - this.emit('status',"") - this.emit('enable-console',{stdout:true}) - setTimeout( () => { - this.send("\n\r1. open a terminal on your laptop/desktop\n\r") - this.send("2. run (or install) the keyboard forwarder:\n\n\r") - this.send("\t$ wget https://xrsh.isvery.ninja/xrsh\n\r") - this.send("\t$ chmod +x xrsh\n\r") - this.send("\t$ ./xrsh --keyboard\n\r\n\r") - this.send("\tNOTE: windows-users need WSL\n\n\r") - this.send("press a key to connect.. (or 'm' for mainmenu)\n\r") - }, 100 ) - }, + init: service.init, keyHandler: function(ch){ this.send(ch) - if( ch == 'm'){ - this.bootMenu() - }else if( (ch == "\n" || ch == "\r") ){ - try{ - console.log("running websocket server") + if( service.state == 'need-ip'){ + if( ch == 'm'){ this.send("\n\r") - }catch(e){ - reset() - throw e // re throw + this.bootMenu() + }else if( ch == '\n' || ch == '\r'){ + this.send("\n\rwaiting for connection..") + service.server(this) + service.state = 'listening' + }else{ + service.ip = ch == '\b' ? service.ip.substr(0,this.service.ip.length-1) + : service.ip + ch } } } diff --git a/com/isoterminal/feat/term.js b/com/isoterminal/feat/term.js index ea6b84c..46b2ad5 100644 --- a/com/isoterminal/feat/term.js +++ b/com/isoterminal/feat/term.js @@ -40,13 +40,8 @@ ISOTerminal.prototype.TermInit = function(){ } }( Term.prototype.keyDownHandler ) - Term.prototype.href = (a) => { - if( a.href ){ - this.exec(`source /etc/profile.sh; hook href "${a.href}"`) - } - return false - } this.term = new Term( opts.termOpts ) + Term.prototype.href = (a) => true this.term.colors = [ /* normal */ "#000000", @@ -93,7 +88,7 @@ ISOTerminal.prototype.TermInit = function(){ }) } }else{ - this.term.write(ch) + this.term.write( ch ) } if( !erase ) this.lastChar = ch }) diff --git a/com/isoterminal/term.js b/com/isoterminal/term.js index 4d5b7cc..3566a5f 100644 --- a/com/isoterminal/term.js +++ b/com/isoterminal/term.js @@ -294,7 +294,7 @@ Term.prototype.refresh = function(ymin, ymax) function is_http_link_char(c) { - var str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=`."; + var str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=%`."; return str.indexOf(String.fromCharCode(c)) >= 0; } @@ -353,7 +353,7 @@ Term.prototype.refresh = function(ymin, ymax) outline += ''; last_attr = this.def_attr; } - outline += ""; + outline += ""; } } if (i == cx) { @@ -1049,9 +1049,8 @@ Term.prototype.interceptBrowserExit = function (ev) Term.prototype.keyDownHandler = function (ev) { var str; - this.interceptBrowserExit(ev); - + str=""; switch(ev.keyCode) { case 8: /* backspace */