feat/vt100: work in progress [might break]
/ test (push) Successful in 7s Details

This commit is contained in:
Leon van Kammen 2024-10-21 12:03:16 +00:00
parent e5a24bbb66
commit 10a3ff6688
5 changed files with 1344 additions and 10 deletions

View File

@ -2,7 +2,8 @@ if( !AFRAME.components['html-as-texture-in-xr'] ){
AFRAME.registerComponent('html-as-texture-in-xr', { AFRAME.registerComponent('html-as-texture-in-xr', {
schema: { schema: {
domid: { type:"string"} domid: { type:"string"},
faceuser: { type: "boolean", default: false}
}, },
dependencies:{ dependencies:{
@ -19,7 +20,9 @@ if( !AFRAME.components['html-as-texture-in-xr'] ){
let s = await AFRAME.utils.require(this.dependencies) let s = await AFRAME.utils.require(this.dependencies)
this.el.setAttribute("html",`html: ${this.data.domid}; cursor:#cursor; xrlayer: true`) this.el.setAttribute("html",`html: ${this.data.domid}; cursor:#cursor; xrlayer: true`)
this.el.setAttribute("visible", AFRAME.utils.XD() == '3D' ? 'true' : 'false' ) this.el.setAttribute("visible", AFRAME.utils.XD() == '3D' ? 'true' : 'false' )
this.el.setAttribute("position", AFRAME.utils.XD.getPositionInFrontOfCamera(0.5) ) if( this.data.faceuser ){
this.el.setAttribute("position", AFRAME.utils.XD.getPositionInFrontOfCamera(0.5) )
}
}, },
manifest: { // HTML5 manifest to identify app to xrsh manifest: { // HTML5 manifest to identify app to xrsh

View File

@ -43,7 +43,7 @@ if( typeof AFRAME != 'undefined '){
muteUntilPrompt:{ type: 'boolean',"default":true}, // mute stdout until a prompt is detected in ISO muteUntilPrompt:{ type: 'boolean',"default":true}, // mute stdout until a prompt is detected in ISO
HUD: { type: 'boolean',"default":false}, // link to camera movement HUD: { type: 'boolean',"default":false}, // link to camera movement
transparent: { type:'boolean', "default":false }, // need good gpu transparent: { type:'boolean', "default":false }, // need good gpu
xterm: { type: 'boolean', "default":true }, // use xterm.js? (=slower) xterm: { type: 'boolean', "default":false }, // use xterm.js? (=slower)
memory: { type: 'number', "default":64 }, // VM memory (in MB) memory: { type: 'number', "default":64 }, // VM memory (in MB)
bufferLatency: { type: 'number', "default":300 }, // in ms: bufferlatency from webworker to xterm (batch-update every char to texture) bufferLatency: { type: 'number', "default":300 }, // in ms: bufferlatency from webworker to xterm (batch-update every char to texture)
canvasLatency: { type: 'number', "default":500 }, // in ms: time between canvas re-draws canvasLatency: { type: 'number', "default":500 }, // in ms: time between canvas re-draws
@ -85,16 +85,20 @@ if( typeof AFRAME != 'undefined '){
scale: 1.0, scale: 1.0,
events: ['click','keydown'], events: ['click','keydown'],
html: (me) => `<div class="isoterminal"> html: (me) => `<div class="isoterminal">
<input type="text" style="opacity:0; position:absolute; width:200px; height:200px;"/>
<pre></pre>
</div>`, </div>`,
css: (me) => `.isoterminal{ css: (me) => `.isoterminal{
padding: ${me.com.data.padding}px; padding: ${me.com.data.padding}px;
width:100%; width:100%;
height:100%; height:100%;
position:relative;
} }
.isoterminal div{ .isoterminal div{
display:block; display:block;
position:relative; position:relative;
line-height:18px;
} }
@font-face { @font-face {
font-family: 'Cousine'; font-family: 'Cousine';
@ -119,7 +123,6 @@ if( typeof AFRAME != 'undefined '){
.wb-body:has(> .isoterminal){ .wb-body:has(> .isoterminal){
background: #000C; background: #000C;
overflow:hidden; overflow:hidden;
border-radius:7px;
} }
.XR .wb-body:has(> .isoterminal){ .XR .wb-body:has(> .isoterminal){
@ -188,6 +191,8 @@ if( typeof AFRAME != 'undefined '){
this.requires.xtermcss = "//unpkg.com/xterm@3.12.0/dist/xterm.css", this.requires.xtermcss = "//unpkg.com/xterm@3.12.0/dist/xterm.css",
this.requires.xtermjs = "//unpkg.com/xterm@3.12.0/dist/xterm.js", this.requires.xtermjs = "//unpkg.com/xterm@3.12.0/dist/xterm.js",
this.requires.xtermcss = "com/xterm.js" this.requires.xtermcss = "com/xterm.js"
}else{
this.requires.vt100 = "com/isoterminal/VT100.js"
} }
await AFRAME.utils.require(this.requires) await AFRAME.utils.require(this.requires)
@ -220,9 +225,10 @@ if( typeof AFRAME != 'undefined '){
this.term = new ISOTerminal(instance,this.data) this.term = new ISOTerminal(instance,this.data)
instance.addEventListener('DOMready', () => { instance.addEventListener('DOMready', () => {
if( this.data.renderer == 'dom' ){ if( this.data.renderer == 'dom' || !this.data.xterm ){
instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}`) instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}; faceuser: true`)
} }
if( !this.data.xterm ) this.setupVT100(instance)
//instance.winbox.resize(720,380) //instance.winbox.resize(720,380)
let size = `width: ${Math.floor(this.data.cols*8.65)}; height: ${Math.floor(this.data.rows*21.1)}` 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}`) instance.setAttribute("window", `title: xrsh.iso; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}; ${size}; min: ${this.data.minimized}; max: ${this.data.maximized}`)
@ -230,8 +236,8 @@ if( typeof AFRAME != 'undefined '){
instance.addEventListener('window.oncreate', (e) => { instance.addEventListener('window.oncreate', (e) => {
instance.dom.classList.add('blink') instance.dom.classList.add('blink')
instance.setAttribute("xterm",`cols: ${this.data.cols}; rows: ${this.data.rows}; canvasLatency: ${this.data.canvasLatency}; XRrenderer: ${this.data.renderer}`) if( this.data.xterm ) this.setupXterm(instance)
instance.addEventListener("xterm-input", (e) => this.term.send(e.detail,0) )
// run iso // run iso
let opts = {dom:instance.dom} let opts = {dom:instance.dom}
for( let i in this.data ) opts[i] = this.data[i] for( let i in this.data ) opts[i] = this.data[i]
@ -305,6 +311,24 @@ if( typeof AFRAME != 'undefined '){
console.test.run() console.test.run()
}, },
setupXterm: function(){
instance.setAttribute("xterm",`cols: ${this.data.cols}; rows: ${this.data.rows}; canvasLatency: ${this.data.canvasLatency}; XRrenderer: ${this.data.renderer}`)
instance.addEventListener("xterm-input", (e) => this.term.send(e.detail,0) )
},
setupVT100: function(instance){
this.vt100 = new VT100(100,50, this.el.dom, 100 )
this.vt100.curs_set(1,true)
this.el.addEventListener('serial-output-byte', (e) => {
const byte = e.detail
var chr = String.fromCharCode(byte);
this.vt100.addchr(chr)
})
this.el.addEventListener('serial-output-string', (e) => {
this.vt100.write(e.detail)
})
},
events:{ events:{
// combined AFRAME+DOM reactive events // combined AFRAME+DOM reactive events

1287
com/isoterminal/VT100.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -63,9 +63,27 @@ AFRAME.registerComponent('window', {
this.el.setAttribute("grabbable","") this.el.setAttribute("grabbable","")
if( this.el.object3D.position.x == 0 &&
this.el.object3D.position.y == 0 &&
this.el.object3D.position.z == 0 ){ // position next to previous window
var els = [...document.querySelectorAll('[window]')]
if( els.length < 2 ) return
let current = els[ els.length-1 ]
let last = els[ els.length-2 ]
AFRAME.utils.positionObjectNextToNeighbor( current.object3D , last.object3D, els.length )
}
}, },
show: function(state){ show: function(state){
this.el.dom.closest('.winbox').style.display = state ? '' : 'none' this.el.dom.closest('.winbox').style.display = state ? '' : 'none'
} }
}) })
AFRAME.utils.positionObjectNextToNeighbor = function positionObjectNextToNeighbor(object, lastNeighbor = null, neighbours, margin = 0.45, degree = 20) {
// *FIXME* this could be more sophisticated :)
object.position.x = lastNeighbor.position.x + ((neighbours-1) * margin)
object.position.y = lastNeighbor.position.y
object.position.z = lastNeighbor.position.z
//object.rotation.y += THREE.MathUtils.degToRad( (neighbours-1) * degree);
}

View File

@ -114,10 +114,12 @@ AFRAME.registerComponent('xterm', {
if( this.data.XRrenderer == 'canvas' ){ if( this.data.XRrenderer == 'canvas' ){
// setup slightly bigger black backdrop (this.el.getObject3D("mesh")) // setup slightly bigger black backdrop (this.el.getObject3D("mesh"))
// and terminal text (this.el.planeText.getObject("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`) const w = 2;
const h = (this.data.rows*5/this.data.cols)
this.el.setAttribute("geometry",`primitive: box; width:${w}; height:${h}; depth: -0.12`)
this.el.setAttribute("material","shader:flat; color:black; opacity:0.5; transparent:true; ") this.el.setAttribute("material","shader:flat; color:black; opacity:0.5; transparent:true; ")
this.el.planeText = document.createElement('a-entity') 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.planeText.setAttribute("geometry",`primitive: plane; width:${w}; height:${h}`)
this.el.appendChild(this.el.planeText) this.el.appendChild(this.el.planeText)
// we switch between dom/canvas rendering because canvas looks pixely in nonimmersive mode // we switch between dom/canvas rendering because canvas looks pixely in nonimmersive mode