From 0e8d173289a7d1cabfd83a9cab703ae177542a8a Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Thu, 3 Oct 2024 10:41:53 +0000 Subject: [PATCH] feat/webworker: work in progress [might break] --- com/isoterminal.js | 19 ++++++++++++------- com/isoterminal/ISOTerminal.js | 22 ++++++++++++++++++---- com/isoterminal/worker.js | 4 ++-- com/window.js | 4 ++++ com/xterm.js | 32 +++++++++++++++----------------- 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/com/isoterminal.js b/com/isoterminal.js index acd9713..4a06672 100644 --- a/com/isoterminal.js +++ b/com/isoterminal.js @@ -42,7 +42,9 @@ if( typeof AFRAME != 'undefined '){ maximized: { type: 'boolean',"default":false}, transparent: { type:'boolean', "default":false }, // need good gpu xterm: { type: 'boolean', "default":true }, // use xterm.js? (=slower) - memory: { type: 'number', "default":48 } // VM memory (in MB) + memory: { type: 'number', "default":48 }, // VM memory (in MB) + bufferLatency:{ type: 'number', "default":200 } // bufferlatency from worker to xterm + // (re-uploading the canvas per character is slooow) }, init: function(){ @@ -210,8 +212,7 @@ if( typeof AFRAME != 'undefined '){ instance.addEventListener('DOMready', () => { //instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}`) //instance.winbox.resize(720,380) - let size = this.data.xterm ? 'width: 1024px; height:600px' - : 'width: 720px; height:455px' + let size = `width: ${Math.floor(this.data.cols*8.65)}; height: ${Math.floor(this.data.rows*21.1)}` instance.setAttribute("window", `title: xrsh.iso; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}; ${size}; min: ${this.data.minimized}; max: ${this.data.maximized}`) }) @@ -257,15 +258,19 @@ if( typeof AFRAME != 'undefined '){ instance.addEventListener('window.onresize', resize ) instance.addEventListener('window.onmaximize', resize ) - const focus = (e) => { + const focus = (showdom) => (e) => { if( this.el.components.xterm ){ this.el.components.xterm.term.focus() } + if( this.el.components.window ){ + this.el.components.window.show( showdom ) + } } - //instance.addEventListener('obbcollisionstarted', focus ) - this.el.sceneEl.addEventListener('enter-vr', focus ) - this.el.sceneEl.addEventListener('enter-ar', focus ) + this.el.sceneEl.addEventListener('enter-vr', focus(false) ) + this.el.sceneEl.addEventListener('enter-ar', focus(false) ) + this.el.sceneEl.addEventListener('exit-vr', focus(true) ) + this.el.sceneEl.addEventListener('exit-ar', focus(true) ) instance.object3D.quaternion.copy( AFRAME.scenes[0].camera.quaternion ) // face towards camera }, diff --git a/com/isoterminal/ISOTerminal.js b/com/isoterminal/ISOTerminal.js index fa1e6b4..f56c10f 100644 --- a/com/isoterminal/ISOTerminal.js +++ b/com/isoterminal/ISOTerminal.js @@ -32,7 +32,6 @@ ISOTerminal.addEventListener = (event,cb) => { } ISOTerminal.prototype.exec = function(shellscript){ - console.log("exec:"+shellscript) this.send(shellscript+"\n",1) } @@ -197,8 +196,9 @@ ISOTerminal.prototype.start = function(opts){ let line = '' let ready = false this.addEventListener(`serial0-output-byte`, async (e) => { - this.emit("serial-output-byte",e.detail) // send to xterm const byte = e.detail + //this.emit("serial-output-byte",byte) // send to xterm + this.bufferOutput(`serial-output-byte`, byte) var chr = String.fromCharCode(byte); if(chr < " " && chr !== "\n" && chr !== "\t" || chr > "~") return @@ -206,8 +206,8 @@ ISOTerminal.prototype.start = function(opts){ { var new_line = line; line = ""; - } - else if(chr >= " " && chr <= "~"){ line += chr } + } else if(chr >= " " && chr <= "~"){ line += chr } + if( !ready && line.match(/^(\/ #|~%|\[.*\]>)/) ){ this.emit('postReady',{}) setTimeout( () => this.emit('ready',{}), 500 ) @@ -217,3 +217,17 @@ ISOTerminal.prototype.start = function(opts){ }); } + +ISOTerminal.prototype.bufferOutput = function(type,byte){ + this.buffer = this.buffer || {str:""} + if( this.buffer.id ) this.buffer.str += String.fromCharCode(byte) + else{ + this.emit(type, byte ) // leading call + this.buffer.id = setTimeout( () => { // trailing calls + if( this.buffer.str ){ + this.emit('serial-output-string', this.buffer.str ) + } + this.buffer = {str:""} + }, this.opts.bufferLatency || 250) + } +} diff --git a/com/isoterminal/worker.js b/com/isoterminal/worker.js index 37470d6..eccdd75 100644 --- a/com/isoterminal/worker.js +++ b/com/isoterminal/worker.js @@ -71,8 +71,8 @@ this.runISO = function(opts){ }) } - //importScripts("feat/javascript.js") - //importScripts("feat/index.html.js") + importScripts("feat/javascript.js") + importScripts("feat/index.html.js") } /* * forward events/functions so non-worker world can reach them diff --git a/com/window.js b/com/window.js index 6a6af9c..d2b8af3 100644 --- a/com/window.js +++ b/com/window.js @@ -63,5 +63,9 @@ AFRAME.registerComponent('window', { this.el.setAttribute("grabbable","") + }, + + show: function(state){ + this.el.dom.closest('.winbox').style.display = state ? '' : 'none' } }) diff --git a/com/xterm.js b/com/xterm.js index cce2f1b..8c5a610 100644 --- a/com/xterm.js +++ b/com/xterm.js @@ -113,7 +113,13 @@ AFRAME.registerComponent('xterm', { overflow: hidden; `) - this.el.setAttribute("geometry",`primitive: plane; width:2; height:${this.data.rows*5/this.data.cols}*2`) + // setup slightly bigger black backdrop (this.el.getObject3D("mesh")) + // and terminal text (this.el.planeText.getObject("mesh")) + this.el.setAttribute("geometry",`primitive: box; width:2.07; height:${this.data.rows*5.3/this.data.cols}*2; depth: -0.12`) + this.el.setAttribute("material","shader:flat; color:black; opacity:0.5; transparent:true; ") + this.el.planeText = document.createElement('a-entity') + this.el.planeText.setAttribute("geometry",`primitive: plane; width:2; height:${this.data.rows*5/this.data.cols}*2`) + this.el.appendChild(this.el.planeText) this.el.terminalElement = terminalElement @@ -148,7 +154,7 @@ AFRAME.registerComponent('xterm', { if( this.el.sceneEl.renderer.xr.isPresenting ){ // workaround // xterm relies on window.requestAnimationFrame (which is not called WebXR immersive mode) - this.term._core.viewport._innerRefresh() + //this.term._core.viewport._innerRefresh() this.term._core.renderer._renderDebouncer._innerRefresh() } },150) @@ -156,15 +162,6 @@ AFRAME.registerComponent('xterm', { this.term.open(terminalElement) this.term.focus() this.setRenderType('dom') - - const refresh = term._core.renderer._renderDebouncer.refresh - let scene = this.el.sceneEl - term._core.renderer._renderDebouncer.refresh = function(){ - refresh.apply(this,arguments) - if( scene.renderer.xr.isPresenting ){ - this._innerRefresh() - } - }.bind(term._core.renderer._renderDebouncer) terminalElement.querySelector('.xterm-viewport').style.background = 'transparent' @@ -191,7 +188,7 @@ AFRAME.registerComponent('xterm', { update: function(){ if( this.renderType == 'canvas' ){ - const material = this.el.getObject3D('mesh').material + const material = this.el.planeText.getObject3D('mesh').material if (!material.map ) return if( this.cursorCanvas ) this.canvasContext.drawImage(this.cursorCanvas, 0,0) material.map.needsUpdate = true @@ -227,16 +224,17 @@ AFRAME.registerComponent('xterm', { //canvasTexture.minFilter = THREE.LinearFilter //canvasTexture.magFilter = THREE.LinearFilter canvasTexture.needsUpdate = true; // Ensure the texture updates - let plane = this.el.getObject3D('mesh') + let plane = this.el.planeText.getObject3D("mesh") //this.el.getObject3D('mesh') if( plane.material ) plane.material.dispose() plane.material = new THREE.MeshBasicMaterial({ map: canvasTexture, // Set the texture from the canvas transparent: false, // Set transparency - side: THREE.DoubleSide // Set to double-sided rendering + //side: THREE.DoubleSide // Set to double-sided rendering + //blending: THREE.AdditiveBlending }); - this.el.getObject3D('mesh').scale.x = 0.3 - this.el.getObject3D('mesh').scale.y = 0.3 - this.el.getObject3D('mesh').scale.z = 0.3 + this.el.object3D.scale.x = 0.2 + this.el.object3D.scale.y = 0.2 + this.el.object3D.scale.z = 0.2 },100) }