add window.js + autolaunch + update isoterminal scale
/ mirror_to_github (push) Successful in 27s Details
/ test (push) Successful in 3s Details

This commit is contained in:
Leon van Kammen 2024-06-28 18:01:24 +02:00
parent 07564881fd
commit 8b978848ff
4 changed files with 80 additions and 125 deletions

View File

@ -95,7 +95,7 @@ AFRAME.registerComponent('dom',{
assignUniqueID: function(){ assignUniqueID: function(){
// assign unique app id so it's easy to reference (by html-mesh component e.g.) // assign unique app id so it's easy to reference (by html-mesh component e.g.)
if( !this.el.uid ) this.el.uid = '_'+String(Math.random()).substr(10) if( !this.el.uid ) this.el.uid = this.el.dom.id = '_'+String(Math.random()).substr(10)
return this return this
}, },

View File

@ -11,6 +11,7 @@ AFRAME.registerComponent('isoterminal', {
}, },
requires:{ requires:{
'window': "com/window.js",
html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME html: "https://unpkg.com/aframe-htmlmesh@2.1.0/build/aframe-html.js", // html to AFRAME
winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox winboxjs: "https://unpkg.com/winbox@0.2.82/dist/winbox.bundle.min.js", // deadsimple windows: https://nextapps-de.github.io/winbox
winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", // winboxcss: "https://unpkg.com/winbox@0.2.82/dist/css/winbox.min.css", //
@ -21,11 +22,11 @@ AFRAME.registerComponent('isoterminal', {
}, },
dom: { dom: {
scale: 3, scale: 0.7,
events: ['click','keydown'], events: ['click','keydown'],
html: (me) => `<div class="isoterminal"> html: (me) => `<div class="isoterminal">
<div style="white-space: pre; font: 14px monospace; line-height: 14px"></div> <div style="white-space: pre; font: 14px monospace; line-height: 14px"></div>
<canvas/> <canvas></canvas>
</div>`, </div>`,
css: (me) => `.isoterminal{ css: (me) => `.isoterminal{
@ -44,48 +45,6 @@ AFRAME.registerComponent('isoterminal', {
}` }`
}, },
createTerminal: async function(instance){
const dom = instance.dom
//this.el.object3D.visible = true
const term = this.term = new Terminal({
allowTransparency: this.data.transparent,
cursorBlink: true,
disableStdin: false,
rows: this.data.rows,
cols: this.data.cols,
fontSize: 16
})
term.open(dom)
this.canvas = dom.querySelector('.xterm-text-layer')
this.canvas.id = 'terminal-' + instance.uid
this.canvasContext = this.canvas.getContext('2d')
this.cursorCanvas = dom.querySelector('.xterm-cursor-layer')
//this.el.setAttribute('material', `transparent: ${this.data.transparent?'true':'false'}; src: #${this.canvas.id}` )
term.on('refresh', () => {
console.log("refresh")
})
term.on('data', (data) => {
console.log(data)
this.el.emit('xterm-data', data)
})
this.el.addEventListener('click', () => {
term.focus()
})
const message = 'Hello from \x1B[1;3;31mWebVR\x1B[0m !\r\n$ '
term.write(message)
this.runISO()
return {width: this.canvas.width, height: this.canvas.height }
},
runISO: function(dom){ runISO: function(dom){
var emulator = window.emulator = new V86({ var emulator = window.emulator = new V86({
wasm_path: "com/isoterminal/v86.wasm", wasm_path: "com/isoterminal/v86.wasm",
@ -102,12 +61,12 @@ AFRAME.registerComponent('isoterminal', {
cdrom: { cdrom: {
url: this.data.iso, url: this.data.iso,
}, },
network_relay_url: "<UNUSED>",
cmdline: "rw root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose modules=virtio_pci tsc=reliable init_on_free=on",
//bzimage:{ //bzimage:{
// url: "com/isoterminal/images/buildroot-bzimage.bin" // url: "com/isoterminal/images/buildroot-bzimage.bin"
//}, //},
network_relay_url: "<UNUSED>",
//bzimage_initrd_from_filesystem: true, //bzimage_initrd_from_filesystem: true,
cmdline: "rw root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose modules=virtio_pci tsc=reliable init_on_free=on",
//filesystem: { //filesystem: {
// baseurl: "com/isoterminal/v86/images/alpine-rootfs-flat", // baseurl: "com/isoterminal/v86/images/alpine-rootfs-flat",
// basefs: "com/isoterminal/v86/images/alpine-fs.json", // basefs: "com/isoterminal/v86/images/alpine-fs.json",
@ -135,56 +94,23 @@ AFRAME.registerComponent('isoterminal', {
// instance this component // instance this component
const instance = this.el.cloneNode(false) const instance = this.el.cloneNode(false)
this.el.sceneEl.appendChild( instance ) this.el.sceneEl.appendChild( instance )
// instance.addEventListener('DOMready', () => {
// console.dir(instance) instance.addEventListener('DOMready', () => {
// debugger this.runISO(instance.dom)
// this.runISO(instance.dom) instance.setAttribute("window", `title: ${this.data.iso}; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}`)
// }) })
instance.addEventListener('window.onclose', (e) => {
if( !confirm('do you want to kill this virtual machine and all its processes?') ) e.halt = true
})
instance.setAttribute("dom", "") instance.setAttribute("dom", "")
instance.setAttribute("xd", "") // allows flipping between DOM/WebGL when toggling XD-button instance.setAttribute("xd", "") // allows flipping between DOM/WebGL when toggling XD-button
instance.setAttribute("visible", AFRAME.utils.XD() == '3D' ? 'true' : 'false' ) instance.setAttribute("visible", AFRAME.utils.XD() == '3D' ? 'true' : 'false' )
instance.setAttribute("position", AFRAME.utils.XD.getPositionInFrontOfCamera(0.5) ) instance.setAttribute("position", AFRAME.utils.XD.getPositionInFrontOfCamera(0.5) )
// instance.setAttribute("grabbable","") instance.setAttribute("grabbable","")
instance.object3D.quaternion.copy( AFRAME.scenes[0].camera.quaternion ) // face towards camera instance.object3D.quaternion.copy( AFRAME.scenes[0].camera.quaternion ) // face towards camera
const setupWindow = () => {
this.runISO(instance.dom)
const com = instance.components['isoterminal']
instance.dom.style.display = 'none'
let winbox = new WinBox( this.data.iso, {
height:'50px',
x:"center",
y:"center",
id: instance.uid, // important hint for html-mesh
root: document.querySelector("#overlay"),
mount: instance.dom,
onclose: () => {
if( !confirm('do you want to kill this virtual machine and all its processes?') ) return true
instance.dom.style.display = 'none';
return false
},
oncreate: () => {
setTimeout( () => {
winbox.resize( winbox.width+'px', (instance.dom.offsetHeight+(2*15))+'px' )
setTimeout( () => instance.setAttribute("html",`html:#${instance.uid}; cursor:#cursor`), 1000)
},100)
}
});
instance.dom.style.display = '' // show
// hint grabbable's obb-collider to track the window-object
instance.components['obb-collider'].data.trackedObject3D = 'components.html.el.object3D.children.0'
instance.components['obb-collider'].update()
// data2event demo
//instance.setAttribute("data2event","")
//com.data.myvalue = 1
//com.data.foo = `instance ${instance.uid}: `
//setInterval( () => com.data.myvalue++, 500 )
}
setTimeout( () => setupWindow(), 10 ) // give new components time to init
}, },
}, },
@ -242,29 +168,3 @@ in above's case "\nHelloworld application\n" will qualify as header.
} }
}); });
AFRAME.registerComponent('xterm-shell', {
dependencies: ['xterm'],
init: function() {
const message = 'Run \x1B[1;3;31m\'node server.js\'\x1B[0m to open a shell\r\n'
const xterm = this.el.components['xterm']
xterm.write(message)
const socket = new WebSocket('ws://localhost:8080/')
// Listen on data, write it to the terminal
socket.onmessage = ({data}) => {
xterm.write(data)
}
socket.onclose = () => {
xterm.write('\r\nConnection closed.\r\n')
}
// Listen on user input, send it to the connection
this.el.addEventListener('xterm-data', ({detail}) => {
socket.send(detail)
})
}
})

View File

@ -80,8 +80,8 @@ AFRAME.registerComponent('launcher', {
dom: { dom: {
scale: 3, scale: 3,
events: ['click'], events: ['click'],
html: (me) => `<div id="iconmenu">loading components..</div>`, html: (me) => `<div class="iconmenu">loading components..</div>`,
css: (me) => `#iconmenu { css: (me) => `.iconmenu {
z-index: 1000; z-index: 1000;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -98,7 +98,7 @@ AFRAME.registerComponent('launcher', {
pointer-events: none; pointer-events: none;
visibility: visible !important; visibility: visible !important;
} }
#iconmenu > button { .iconmenu > button {
line-height:0px; line-height:0px;
pointer-events:all; pointer-events:all;
width: 58px; width: 58px;
@ -112,27 +112,27 @@ AFRAME.registerComponent('launcher', {
font-size:18px; font-size:18px;
} }
#iconmenu > button:first-child { .iconmenu > button:first-child {
border-radius: 5px 0px 0px 5px; border-radius: 5px 0px 0px 5px;
border-bottom: 2px solid #BBB; border-bottom: 2px solid #BBB;
border-left: 2px solid #BBB; border-left: 2px solid #BBB;
padding-bottom:16px; padding-bottom:16px;
} }
#iconmenu > button:last-child { .iconmenu > button:last-child {
border-radius:0px 5px 5px 0px; border-radius:0px 5px 5px 0px;
border-top: 2px solid #BBB; border-top: 2px solid #BBB;
border-right: 2px solid #BBB; border-right: 2px solid #BBB;
padding-top:13px; padding-top:13px;
} }
#iconmenu > button > img { .iconmenu > button > img {
transform: translate(0px,-14px); transform: translate(0px,-14px);
opacity:0.5; opacity:0.5;
padding: 5px; padding: 5px;
border-radius: 0px; border-radius: 0px;
} }
#iconmenu > button > img:hover{ .iconmenu > button > img:hover{
background: #AAA; background: #AAA;
transition:0.gg3s; transition:0.gg3s;
border-radius: 50%; border-radius: 50%;

55
com/window.js Normal file
View File

@ -0,0 +1,55 @@
AFRAME.registerComponent('window', {
schema:{
title: {type:'string',"default":"title"},
width: {type:'string'}, // wrap
height: {type:'string',"default":'50px'},
uid: {type:'string'},
attach: {type:'selector'},
dom: {type:'selector'},
x: {type:'string',"default":"center"},
y: {type:'string',"default":"center"}
},
dependencies:['dom'],
init: function(){
setTimeout( () => this.setupWindow(), 10 ) // init after other components
},
setupWindow: function(){
if( !this.el.dom ) return console.error('window element requires dom-component as dependency')
this.el.dom.style.display = 'none'
let winbox = new WinBox( this.data.title, {
height:this.data.height,
height:this.data.width,
x: this.data.x,
y: this.data.y,
id: this.data.uid || String(Math.random()).substr(4), // important hint for html-mesh
root: this.data.attach || document.body,
mount: this.data.dom,
oncreate: () => {
this.el.emit('window.oncreate',{})
// resize after the dom content has been rendered & updated
setTimeout( () => {
winbox.resize( winbox.width+'px', (this.data.dom.offsetHeight+(2*15))+'px' )
setTimeout( () => this.el.setAttribute("html",`html:#${this.data.uid}; cursor:#cursor`), 1000)
this.el.setAttribute("grabbable","")
},1000)
},
onclose: () => {
let e = {halt:false}
this.el.emit('window.onclose',e)
if( e.halt ) return true
this.data.dom.style.display = 'none';
return false
},
});
this.data.dom.style.display = '' // show
// hint grabbable's obb-collider to track the window-object
this.el.components['obb-collider'].data.trackedObject3D = 'components.html.el.object3D.children.0'
this.el.components['obb-collider'].update()
}
})