feat/codemirror: work in progress [might break]
/ test (push) Successful in 5s
Details
/ test (push) Successful in 5s
Details
This commit is contained in:
parent
9aff2133a0
commit
2105120819
|
@ -12,7 +12,7 @@ AFRAME.registerComponent('codemirror', {
|
||||||
if( this.data.file && this.data.file[0] != '/'){
|
if( this.data.file && this.data.file[0] != '/'){
|
||||||
this.data.file = "root/"+this.data.file
|
this.data.file = "root/"+this.data.file
|
||||||
}
|
}
|
||||||
this.isoterminal = this.data.term.components.isoterminal.isoterminal
|
this.isoterminal = this.data.term.components.isoterminal.term
|
||||||
//this.el.innerHTML = ` `
|
//this.el.innerHTML = ` `
|
||||||
this.requireAll()
|
this.requireAll()
|
||||||
},
|
},
|
||||||
|
@ -84,16 +84,18 @@ AFRAME.registerComponent('codemirror', {
|
||||||
// we don't do via shellcmd: isoterminal.exec(`echo '${str}' > ${file}`,1)
|
// we don't do via shellcmd: isoterminal.exec(`echo '${str}' > ${file}`,1)
|
||||||
// as it would require all kindof ugly stringescaping
|
// as it would require all kindof ugly stringescaping
|
||||||
console.log("updating "+file)
|
console.log("updating "+file)
|
||||||
await this.isoterminal.emulator.fs9p.update_file( file, str)
|
await this.isoterminal.worker['emulator.create_file'](file, term.convert.toUint8Array(str) )
|
||||||
},
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
||||||
// component events
|
// component events
|
||||||
DOMready: function(e){
|
DOMready: function(e){
|
||||||
this.isoterminal.worker.postMessage.promise({event:'read_file',data: this.data.file })
|
|
||||||
|
this.isoterminal.worker['emulator.read_file'](this.data.file)
|
||||||
.then( this.isoterminal.convert.Uint8ArrayToString )
|
.then( this.isoterminal.convert.Uint8ArrayToString )
|
||||||
.then( (str) => {
|
.then( (str) => {
|
||||||
|
console.log("creating editor")
|
||||||
this.createEditor( str )
|
this.createEditor( str )
|
||||||
})
|
})
|
||||||
.catch( (e) => {
|
.catch( (e) => {
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
* ┌─────────┐ ┌────────────┐ ┌─────────────┐ exit-AR
|
* ┌─────────┐ ┌────────────┐ ┌─────────────┐ exit-AR
|
||||||
* ┌───────►│ com/dom ┼──►│ com/window ├─►│ domrenderer │◄────────── exit-VR ◄─┐
|
* ┌───────►│ com/dom ┼──►│ com/window ├─►│ domrenderer │◄────────── exit-VR ◄─┐
|
||||||
* │ └─────────┘ └────────────┘ └─────▲───────┘ │
|
* │ └─────────┘ └────────────┘ └─────▲───────┘ │
|
||||||
* │ │ ┌───────────────┐ │
|
* │ │ ┌───────────────┐ │ renderer=dom
|
||||||
* ┌──────────┴────────┐ ┌─────┴──────┐ │ xterm.js │ ┌─────────────────────────────┐
|
* ┌──────────┴────────┐ ┌─────┴──────┐ │ xterm.js │ ┌─────────────────────────────┐
|
||||||
* │ com/isoterminal ├────────────────────────────►│com/xterm.js│◄─┤ │ │com/html-as-texture-in-XR.js │
|
* │ com/isoterminal ├────────────────────────────►│com/xterm.js│◄─┤ │ │com/html-as-texture-in-XR.js │
|
||||||
* └────────┬─┬────────┘ └──┬──────┬▲─┘ │ xterm.css │ └─────────────────────────────┘
|
* └────────┬─┬────────┘ └──┬──────┬▲─┘ │ xterm.css │ └─────────────────────────────┘
|
||||||
* │ │ ┌────────┐ ┌─────────▼──┐ ││ └───────────────┘ │ ▲
|
* │ │ ┌────────┐ ┌─────────▼──┐ ││ └───────────────┘ │ ▲
|
||||||
* │ └───────►│ plane ├─────►text───┼►canvas │◄────────────────── enter-VR │ │
|
* │ └───────►│ plane ├─────►text───┼►canvas │◄────────────────── enter-VR │ │
|
||||||
* │ └────────┘ └────────────┘ ││ enter-AR ◄─┘ │
|
* │ └────────┘ └────────────┘ ││ renderer=canvas enter-AR ◄─┘ │
|
||||||
* │ ││ │
|
* │ ││ │
|
||||||
* │ ││ │
|
* │ ││ │
|
||||||
* │ ISOTerminal.js ││ │
|
* │ ISOTerminal.js ││ │
|
||||||
|
@ -40,13 +40,15 @@ if( typeof AFRAME != 'undefined '){
|
||||||
padding: { type: 'number',"default": 18 },
|
padding: { type: 'number',"default": 18 },
|
||||||
minimized: { type: 'boolean',"default":false},
|
minimized: { type: 'boolean',"default":false},
|
||||||
maximized: { type: 'boolean',"default":false},
|
maximized: { type: 'boolean',"default":false},
|
||||||
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":true }, // 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":300 } // in ms: time between canvas re-draws
|
canvasLatency: { type: 'number', "default":500 }, // in ms: time between canvas re-draws
|
||||||
|
renderer: { type: 'string', "default":"canvas" },// 'dom' or 'canvas' (=faster) for immersive mode
|
||||||
|
debug: { type: 'boolean', "default":false }
|
||||||
},
|
},
|
||||||
|
|
||||||
init: function(){
|
init: function(){
|
||||||
|
@ -65,21 +67,22 @@ if( typeof AFRAME != 'undefined '){
|
||||||
},
|
},
|
||||||
|
|
||||||
requires:{
|
requires:{
|
||||||
com: "com/dom.js",
|
com: "com/dom.js",
|
||||||
window: "com/window.js",
|
window: "com/window.js",
|
||||||
v86: "com/isoterminal/libv86.js",
|
v86: "com/isoterminal/libv86.js",
|
||||||
// allow xrsh to selfcontain scene + itself
|
// allow xrsh to selfcontain scene + itself
|
||||||
xhook: "https://jpillora.com/xhook/dist/xhook.min.js",
|
xhook: "https://jpillora.com/xhook/dist/xhook.min.js",
|
||||||
selfcontain: "com/selfcontainer.js",
|
selfcontain: "com/selfcontainer.js",
|
||||||
// html to texture
|
// html to texture
|
||||||
htmlinxr: "com/html-as-texture-in-xr.js",
|
htmlinxr: "com/html-as-texture-in-xr.js",
|
||||||
// isoterminal features
|
// isoterminal features
|
||||||
ISOTerminal: "com/isoterminal/ISOTerminal.js",
|
PromiseWorker: "com/isoterminal/PromiseWorker.js",
|
||||||
localforage: "https://cdn.rawgit.com/mozilla/localForage/master/dist/localforage.js"
|
ISOTerminal: "com/isoterminal/ISOTerminal.js",
|
||||||
|
localforage: "https://cdn.rawgit.com/mozilla/localForage/master/dist/localforage.js"
|
||||||
},
|
},
|
||||||
|
|
||||||
dom: {
|
dom: {
|
||||||
//scale: 0.5,
|
scale: 1.0,
|
||||||
events: ['click','keydown'],
|
events: ['click','keydown'],
|
||||||
html: (me) => `<div class="isoterminal">
|
html: (me) => `<div class="isoterminal">
|
||||||
</div>`,
|
</div>`,
|
||||||
|
@ -114,12 +117,16 @@ if( typeof AFRAME != 'undefined '){
|
||||||
}
|
}
|
||||||
|
|
||||||
.wb-body:has(> .isoterminal){
|
.wb-body:has(> .isoterminal){
|
||||||
background: #000C;
|
background: #000C;
|
||||||
overflow:hidden;
|
overflow:hidden;
|
||||||
border-radius:7px;
|
border-radius:7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.XR .wb-body:has(> .isoterminal){
|
.XR .wb-body:has(> .isoterminal){
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.XR .isoterminal{
|
||||||
background: #000;
|
background: #000;
|
||||||
}
|
}
|
||||||
.isoterminal *,
|
.isoterminal *,
|
||||||
|
@ -210,10 +217,12 @@ if( typeof AFRAME != 'undefined '){
|
||||||
}
|
}
|
||||||
|
|
||||||
// init isoterminal
|
// init isoterminal
|
||||||
this.isoterminal = new ISOTerminal(instance,this.data)
|
this.term = new ISOTerminal(instance,this.data)
|
||||||
|
|
||||||
instance.addEventListener('DOMready', () => {
|
instance.addEventListener('DOMready', () => {
|
||||||
//instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}`)
|
if( this.data.renderer == 'dom' ){
|
||||||
|
instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}`)
|
||||||
|
}
|
||||||
//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}`)
|
||||||
|
@ -221,17 +230,17 @@ 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}`)
|
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.isoterminal.send(e.detail,0) )
|
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]
|
||||||
this.isoterminal.start(opts)
|
this.term.start(opts)
|
||||||
})
|
})
|
||||||
|
|
||||||
instance.setAttribute("dom", "")
|
instance.setAttribute("dom", "")
|
||||||
|
|
||||||
this.isoterminal.addEventListener('postReady', (e)=>{
|
this.term.addEventListener('postReady', (e)=>{
|
||||||
// bugfix: send window dimensions to xterm (xterm.js does that from dom-sizechange to xterm via escape codes)
|
// bugfix: send window dimensions to xterm (xterm.js does that from dom-sizechange to xterm via escape codes)
|
||||||
let wb = instance.winbox
|
let wb = instance.winbox
|
||||||
if( this.data.maximized ){
|
if( this.data.maximized ){
|
||||||
|
@ -240,12 +249,13 @@ if( typeof AFRAME != 'undefined '){
|
||||||
}else wb.resize()
|
}else wb.resize()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.isoterminal.addEventListener('ready', (e)=>{
|
this.term.addEventListener('ready', (e) => {
|
||||||
instance.dom.classList.remove('blink')
|
instance.dom.classList.remove('blink')
|
||||||
this.isoterminal.emit('status',"running")
|
this.term.emit('status',"running")
|
||||||
|
if( this.data.debug ) this.runTests()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.isoterminal.addEventListener('status', function(e){
|
this.term.addEventListener('status', function(e){
|
||||||
let msg = e.detail
|
let msg = e.detail
|
||||||
const w = instance.winbox
|
const w = instance.winbox
|
||||||
if(!w) return
|
if(!w) return
|
||||||
|
@ -265,7 +275,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
if( this.el.components.xterm ){
|
if( this.el.components.xterm ){
|
||||||
this.el.components.xterm.term.focus()
|
this.el.components.xterm.term.focus()
|
||||||
}
|
}
|
||||||
if( this.el.components.window ){
|
if( this.el.components.window && this.data.renderer == 'canvas'){
|
||||||
this.el.components.window.show( showdom )
|
this.el.components.window.show( showdom )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,6 +297,14 @@ if( typeof AFRAME != 'undefined '){
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
runTests: async function(){
|
||||||
|
await AFRAME.utils.require({
|
||||||
|
"test_util": "tests/util.js",
|
||||||
|
"test_isoterminal":"tests/ISOTerminal.js"
|
||||||
|
})
|
||||||
|
console.test.run()
|
||||||
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
||||||
// combined AFRAME+DOM reactive events
|
// combined AFRAME+DOM reactive events
|
||||||
|
|
|
@ -22,7 +22,7 @@ ISOTerminal.prototype.emit = function(event,data,sender){
|
||||||
// this feels complex, but actually keeps event- and function-names more concise in codebase
|
// this feels complex, but actually keeps event- and function-names more concise in codebase
|
||||||
this.dispatchEvent( evObj )
|
this.dispatchEvent( evObj )
|
||||||
if( sender != "instance" && this.instance ) this.instance.dispatchEvent(evObj)
|
if( sender != "instance" && this.instance ) this.instance.dispatchEvent(evObj)
|
||||||
if( sender != "worker" && this.worker ) this.worker.postMessage({event,data}, this.getTransferable(data) )
|
if( sender != "worker" && this.worker ) this.worker.postMessage({event,data}, PromiseWorker.prototype.getTransferable(data) )
|
||||||
if( sender !== undefined && typeof this[event] == 'function' ) this[event].apply(this, data && data.push ? data : [data] )
|
if( sender !== undefined && typeof this[event] == 'function' ) this[event].apply(this, data && data.push ? data : [data] )
|
||||||
//})
|
//})
|
||||||
}
|
}
|
||||||
|
@ -128,46 +128,30 @@ ISOTerminal.prototype.start = function(opts){
|
||||||
//disable_jit: false,
|
//disable_jit: false,
|
||||||
filesystem: {},
|
filesystem: {},
|
||||||
autostart: true,
|
autostart: true,
|
||||||
|
debug: this.opts.debug ? true : false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this
|
||||||
|
.setupWorker(opts)
|
||||||
|
.startVM(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
ISOTerminal.prototype.setupWorker = function(opts){
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the WebWorker (which runs v86)
|
* the WebWorker (which runs v86)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
this.worker = new PromiseWorker( "com/isoterminal/worker.js", (cb,event,data) => {
|
||||||
this.worker = new Worker("com/isoterminal/worker.js");
|
if( !data.promiseId ) this.emit(event,data,"worker") // forward event to world
|
||||||
this.worker.onmessage = (e) => {
|
|
||||||
const {event,data} = e.data
|
|
||||||
const cb = (event,data) => () => {
|
|
||||||
if( data.promiseId ){
|
|
||||||
this.workerPromise.resolver(data) // forward to promise resolver
|
|
||||||
}else this.emit(event,data,"worker") // forward event to world
|
|
||||||
|
|
||||||
}
|
|
||||||
this.preventFrameDrop( cb(event,data) )
|
this.preventFrameDrop( cb(event,data) )
|
||||||
}
|
})
|
||||||
|
|
||||||
/*
|
|
||||||
* postMessage.promise basically performs this.worker.postMessage
|
|
||||||
* in a promise way (to easily retrieve async output)
|
|
||||||
*/
|
|
||||||
|
|
||||||
this.worker.postMessage.promise = function(data){
|
return this
|
||||||
if( typeof data != 'object' ) data = {data}
|
}
|
||||||
this.resolvers = this.resolvers || {}
|
|
||||||
this.id = this.id == undefined ? 0 : this.id
|
|
||||||
data.id = this.id++
|
|
||||||
// Send id and task to WebWorker
|
|
||||||
this.preventFrameDrop( () => this.worker.postMessage(data,getTransferable(data) ) )
|
|
||||||
return new Promise(resolve => this.resolvers[data.id] = resolve);
|
|
||||||
}.bind(this.worker.postMessage)
|
|
||||||
|
|
||||||
this.worker.postMessage.promise.resolver = function(data){
|
|
||||||
if( !data || !data.promiseId ) throw 'promiseId not given'
|
|
||||||
this.resolvers[ data.promiseId ](data);
|
|
||||||
delete this.resolvers[ data.promiseId ]; // Prevent memory leak
|
|
||||||
}.bind(this.worker.postMessage)
|
|
||||||
|
|
||||||
|
ISOTerminal.prototype.startVM = function(opts){
|
||||||
|
|
||||||
this.emit('runISO',{...opts, bufferLatency: this.opts.bufferLatency })
|
this.emit('runISO',{...opts, bufferLatency: this.opts.bufferLatency })
|
||||||
const loading = [
|
const loading = [
|
||||||
|
@ -228,10 +212,10 @@ ISOTerminal.prototype.start = function(opts){
|
||||||
\r[38;5;165m local-first, polyglot, unixy WebXR IDE & runtime
|
\r[38;5;165m local-first, polyglot, unixy WebXR IDE & runtime
|
||||||
\r
|
\r
|
||||||
\r credits: NLnet | @nlnet@nlnet.nl
|
\r credits: NLnet | @nlnet@nlnet.nl
|
||||||
\r MrDoob | THREE.js
|
|
||||||
\r Diego Marcos | AFRAME.js
|
|
||||||
\r Leon van Kammen | @lvk@mastodon.online
|
\r Leon van Kammen | @lvk@mastodon.online
|
||||||
\r Fabien Benetou | @utopiah@mastodon.pirateparty.be
|
\r Fabien Benetou | @utopiah@mastodon.pirateparty.be
|
||||||
|
\r Mr Doob | THREE.js
|
||||||
|
\r Diego Marcos | AFRAME.js
|
||||||
`
|
`
|
||||||
|
|
||||||
const text_color = "\r[38;5;129m"
|
const text_color = "\r[38;5;129m"
|
||||||
|
@ -303,16 +287,3 @@ ISOTerminal.prototype.preventFrameDrop = function(cb){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ISOTerminal.prototype.getTransferable = function(data){
|
|
||||||
function isTransferable(obj) {
|
|
||||||
return obj instanceof ArrayBuffer ||
|
|
||||||
obj instanceof MessagePort ||
|
|
||||||
obj instanceof ImageBitmap ||
|
|
||||||
(typeof OffscreenCanvas !== 'undefined' && obj instanceof OffscreenCanvas) ||
|
|
||||||
(typeof ReadableStream !== 'undefined' && obj instanceof ReadableStream) ||
|
|
||||||
(typeof WritableStream !== 'undefined' && obj instanceof WritableStream) ||
|
|
||||||
(typeof TransformStream !== 'undefined' && obj instanceof TransformStream);
|
|
||||||
}
|
|
||||||
if( isTransferable(data) ) console.log("Transferable!")
|
|
||||||
if( isTransferable(data) ) return isTransferable(data) ? [data] : undefined
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* This is basically a Javascript Proxy for 'new Worker()'
|
||||||
|
* which allows calling worker-functions via promises.
|
||||||
|
*
|
||||||
|
* It's basically comlink without the fat.
|
||||||
|
*
|
||||||
|
* const w = new PromiseWorker("worker.js", (cb,event_or_fn,data) => {
|
||||||
|
* cb(event_or_fn,data) // decorate/ratelimit/hooks here
|
||||||
|
* })
|
||||||
|
* w.foo().then( console.dir )
|
||||||
|
*
|
||||||
|
* in worker.js define a function: this.foo = () => return {foo:"Bar"}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function PromiseWorker(file, onmessage){
|
||||||
|
|
||||||
|
let proxy
|
||||||
|
const worker = new Worker(file)
|
||||||
|
|
||||||
|
worker.onmessage = (e) => { // handle messages originating from worker
|
||||||
|
const {event,data} = e.data // this is worker.onmessage(...)
|
||||||
|
const cb = (event,data) => () => { //
|
||||||
|
if( data.promiseId ){ //
|
||||||
|
proxy.resolver(data) // forward to promise resolver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onmessage(cb,event,data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxy = new Proxy(this,{
|
||||||
|
get(me,k){
|
||||||
|
if( k.match(/(postMessage|onmessage)/) ) return worker[k].bind(worker)
|
||||||
|
if( k.match(/(resolver|promise)/) ) return this[k].bind(this)
|
||||||
|
// promisify postMessage(...) call
|
||||||
|
return function(){
|
||||||
|
return this.promise(me,{event: k, data: [...arguments] })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
promise(me,msg){
|
||||||
|
if( typeof msg != 'object' || !msg.event || !msg.data ){
|
||||||
|
throw 'worker.promise({event:..,data:..}) did not receive correct msg : '+JSON.stringify(msg)
|
||||||
|
}
|
||||||
|
this.resolvers = this.resolvers || {last:1,pending:{}}
|
||||||
|
msg.data.promiseId = this.resolvers.last++
|
||||||
|
// Send id and task to WebWorker
|
||||||
|
worker.postMessage(msg, PromiseWorker.prototype.getTransferable(msg.data) )
|
||||||
|
return new Promise( resolve => this.resolvers.pending[ msg.data.promiseId ] = resolve );
|
||||||
|
},
|
||||||
|
|
||||||
|
resolver(data){
|
||||||
|
if( !data || !data.promiseId ) throw 'promiseId not given'
|
||||||
|
this.resolvers.pending[ data.promiseId ](data.result);
|
||||||
|
delete this.resolvers.pending[ data.promiseId ]; // Prevent memory leak
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PromiseWorker.prototype.getTransferable = function(data){
|
||||||
|
let objs = []
|
||||||
|
function isTransferable(obj) {
|
||||||
|
return obj instanceof ArrayBuffer ||
|
||||||
|
obj instanceof MessagePort ||
|
||||||
|
obj instanceof ImageBitmap ||
|
||||||
|
(typeof OffscreenCanvas !== 'undefined' && obj instanceof OffscreenCanvas) ||
|
||||||
|
(typeof ReadableStream !== 'undefined' && obj instanceof ReadableStream) ||
|
||||||
|
(typeof WritableStream !== 'undefined' && obj instanceof WritableStream) ||
|
||||||
|
(typeof TransformStream !== 'undefined' && obj instanceof TransformStream);
|
||||||
|
}
|
||||||
|
for( var i in data ){
|
||||||
|
if( isTransferable(data[i]) ) objs.push(data[i])
|
||||||
|
}
|
||||||
|
if( objs.length ) debugger
|
||||||
|
return objs.length ? objs : undefined
|
||||||
|
}
|
|
@ -12,15 +12,19 @@ emulator.fs9p.update_file = async function(file,data){
|
||||||
}
|
}
|
||||||
|
|
||||||
const inode = this.GetInode(p.id);
|
const inode = this.GetInode(p.id);
|
||||||
const buf = typeof data == 'string' ? convert.toUint8Array(data) : data
|
const buf = typeof data == 'string' ? convert.toUint8Array(data) : data || ""
|
||||||
await this.Write(p.id,0, buf.length, buf )
|
try{
|
||||||
// update inode
|
await this.Write(p.id,0, buf.length, buf )
|
||||||
inode.size = buf.length
|
// update inode
|
||||||
const now = Math.round(Date.now() / 1000);
|
inode.size = buf.length
|
||||||
inode.atime = inode.mtime = now;
|
const now = Math.round(Date.now() / 1000);
|
||||||
me.postMessage({event:'exec',data:[`touch /mnt/${file}`]}) // update inode
|
inode.atime = inode.mtime = now;
|
||||||
return new Promise( (resolve,reject) => resolve(buf) )
|
me.postMessage({event:'exec',data:[`touch /mnt/${file}`]}) // update inode
|
||||||
|
return new Promise( (resolve,reject) => resolve(buf) )
|
||||||
|
}catch(e){
|
||||||
|
console.error({file,data})
|
||||||
|
return new Promise( (resolve,reject) => reject(e) )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator.fs9p.append_file = async function(file,data){
|
emulator.fs9p.append_file = async function(file,data){
|
||||||
|
|
|
@ -9,7 +9,7 @@ if( typeof emulator != 'undefined' ){
|
||||||
this['emulator.save_state'] = async function(){
|
this['emulator.save_state'] = async function(){
|
||||||
console.log("saving session")
|
console.log("saving session")
|
||||||
let state = await emulator.save_state()
|
let state = await emulator.save_state()
|
||||||
this.postMessage({event:"state_saved",data:state})
|
this.postMessage({event:"state_saved",data:state},[state])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,31 @@
|
||||||
if( typeof emulator != 'undefined' ){
|
if( typeof emulator != 'undefined' ){
|
||||||
// inside worker-thread
|
// inside worker-thread
|
||||||
|
|
||||||
// unix to js device
|
this.listenIndexHTML = () => {
|
||||||
this.emulator.readFromPipe( 'root/index.html', async (data) => {
|
const file = "dev/browser/html"
|
||||||
const buf = await emulator.read_file("root/index.html")
|
emulator.readFromPipe( file, async (data) => {
|
||||||
const decoder = new TextDecoder('utf-8');
|
const buf = await emulator.read_file( file )
|
||||||
const html = decoder.decode(buf).replace(/^#!\/bin\/html/,'') // remove leftover shebangs if any
|
const decoder = new TextDecoder('utf-8');
|
||||||
try{
|
const html = decoder.decode(buf).replace(/^#!\/bin\/html/,'') // remove leftover shebangs if any
|
||||||
this.postMessage({event:'runHTML',data:[html]})
|
try{
|
||||||
}catch(e){
|
this.postMessage({event:'runHTML',data:[html]})
|
||||||
console.error(e)
|
}catch(e){
|
||||||
}
|
console.error(file)
|
||||||
})
|
console.error(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
// inside browser-thread
|
// inside browser-thread
|
||||||
|
|
||||||
|
ISOTerminal.addEventListener('emulator-started', function(){
|
||||||
|
this.addEventListener('ready', async () => {
|
||||||
|
this.worker.listenIndexHTML()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
ISOTerminal.prototype.runHTML = function(html){
|
ISOTerminal.prototype.runHTML = function(html){
|
||||||
let $scene = document.querySelector("a-scene")
|
let $scene = document.querySelector("a-scene")
|
||||||
let $root = document.querySelector("a-entity#root")
|
let $root = document.querySelector("a-entity#root")
|
||||||
|
@ -26,6 +35,7 @@ if( typeof emulator != 'undefined' ){
|
||||||
$root.id = "root"
|
$root.id = "root"
|
||||||
$scene.appendChild($root)
|
$scene.appendChild($root)
|
||||||
}
|
}
|
||||||
|
console.log(html)
|
||||||
$root.innerHTML = html
|
$root.innerHTML = html
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ if( typeof emulator != 'undefined' ){
|
||||||
// inside worker-thread
|
// inside worker-thread
|
||||||
|
|
||||||
// unix to js device
|
// unix to js device
|
||||||
this.emulator.readFromPipe( 'root/index.js', async (data) => {
|
this.emulator.readFromPipe( 'dev/browser/js', async (data) => {
|
||||||
const buf = await emulator.read_file("root/index.js")
|
const buf = await emulator.read_file("dev/browser/js")
|
||||||
const decoder = new TextDecoder('utf-8');
|
const decoder = new TextDecoder('utf-8');
|
||||||
const js = decoder.decode(buf).replace(/^#!\/bin\/js/,'') // remove leftover shebangs if any
|
const js = decoder.decode(buf).replace(/^#!\/bin\/js/,'') // remove leftover shebangs if any
|
||||||
try{
|
try{
|
||||||
|
|
|
@ -9,17 +9,17 @@ ISOTerminal.prototype.redirectConsole = function(handler){
|
||||||
log.apply(log, args);
|
log.apply(log, args);
|
||||||
};
|
};
|
||||||
console.error = (...args)=>{
|
console.error = (...args)=>{
|
||||||
const textArg = args[0].message?args[0].message:args[0];
|
const textArg = args[0]
|
||||||
handler( textArg+'\n', '\x1b[31merror\x1b[0m');
|
handler( textArg+'\n', '\x1b[31merror\x1b[0m');
|
||||||
err.apply(log, args);
|
err.apply(log, args);
|
||||||
};
|
};
|
||||||
console.dir = (...args)=>{
|
console.dir = (...args)=>{
|
||||||
const textArg = args[0].message?args[0].message:args[0];
|
const textArg = args[0]
|
||||||
handler( JSON.stringify(textArg,null,2)+'\n');
|
handler( JSON.stringify(textArg,null,2)+'\n');
|
||||||
dir.apply(log, args);
|
dir.apply(log, args);
|
||||||
};
|
};
|
||||||
console.warn = (...args)=>{
|
console.warn = (...args)=>{
|
||||||
const textArg = args[0].message?args[0].message:args[0];
|
const textArg = args[0]
|
||||||
handler(textArg+'\n','\x1b[38;5;208mwarn\x1b[0m');
|
handler(textArg+'\n','\x1b[38;5;208mwarn\x1b[0m');
|
||||||
err.apply(log, args);
|
err.apply(log, args);
|
||||||
};
|
};
|
||||||
|
@ -46,7 +46,6 @@ ISOTerminal.addEventListener('emulator-started', function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('unhandledrejection', function(event) {
|
window.addEventListener('unhandledrejection', function(event) {
|
||||||
console.error('Unhandled promise rejection:')
|
|
||||||
console.error(event);
|
console.error(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,14 @@ importScripts("libv86.js");
|
||||||
importScripts("ISOTerminal.js") // we don't instance it again here (just use its functions)
|
importScripts("ISOTerminal.js") // we don't instance it again here (just use its functions)
|
||||||
|
|
||||||
this.runISO = function(opts){
|
this.runISO = function(opts){
|
||||||
|
this.opts = opts
|
||||||
|
if( opts.debug ) console.dir(opts)
|
||||||
|
|
||||||
if( opts.cdrom && !opts.cdrom.url.match(/^http/) ) opts.cdrom.url = "../../"+opts.cdrom.url
|
if( opts.cdrom && !opts.cdrom.url.match(/^http/) ) opts.cdrom.url = "../../"+opts.cdrom.url
|
||||||
if( opts.bzimage && !opts.cdrom.url.match(/^http/) ) opts.bzimage.url = "../../"+opts.bzimage.url
|
if( opts.bzimage && !opts.cdrom.url.match(/^http/) ) opts.bzimage.url = "../../"+opts.bzimage.url
|
||||||
|
|
||||||
console.dir(opts)
|
|
||||||
let emulator = this.emulator = new V86(opts);
|
let emulator = this.emulator = new V86(opts);
|
||||||
console.log("worker:started emulator")
|
console.log("[worker.js] started emulator")
|
||||||
|
|
||||||
// event forwarding
|
// event forwarding
|
||||||
|
|
||||||
|
@ -40,8 +42,8 @@ this.runISO = function(opts){
|
||||||
/*
|
/*
|
||||||
* forward events/functions so non-worker world can reach them
|
* forward events/functions so non-worker world can reach them
|
||||||
*/
|
*/
|
||||||
this['emulator.create_file'] = function(){ emulator.create_file.apply(emulator, arguments[0]) }
|
this['emulator.create_file'] = async function(){ return emulator.create_file.apply(emulator, arguments[0]) }
|
||||||
this['emulator.read_file'] = function(){ emulator.read_file.apply(emulator, arguments[0]) }
|
this['emulator.read_file'] = async function(){ return emulator.read_file.apply(emulator, arguments[0]) }
|
||||||
|
|
||||||
// filename will be read from 9pfs: "/mnt/"+filename
|
// filename will be read from 9pfs: "/mnt/"+filename
|
||||||
emulator.readFromPipe = function(filename,cb){
|
emulator.readFromPipe = function(filename,cb){
|
||||||
|
@ -64,7 +66,19 @@ this['serial0-input'] = function(c){ this.emulator.bus.send( 'serial0-input', c)
|
||||||
this['serial1-input'] = function(c){ this.emulator.bus.send( 'serial1-input', c) } // to /dev/ttyS1
|
this['serial1-input'] = function(c){ this.emulator.bus.send( 'serial1-input', c) } // to /dev/ttyS1
|
||||||
this['serial2-input'] = function(c){ this.emulator.bus.send( 'serial2-input', c) } // to /dev/ttyS2
|
this['serial2-input'] = function(c){ this.emulator.bus.send( 'serial2-input', c) } // to /dev/ttyS2
|
||||||
|
|
||||||
this.onmessage = function(e){
|
this.onmessage = async function(e){
|
||||||
let {event,data} = e.data
|
let {event,data} = e.data
|
||||||
if( this[event] ) this[event](data)
|
if( this[event] ){
|
||||||
|
if( this.opts?.debug ) console.log(`[worker.js] this.${event}(${JSON.stringify(data).substr(0,60)})`)
|
||||||
|
try{
|
||||||
|
let result = await this[event](data)
|
||||||
|
if( data.promiseId ){ // auto-callback to ISOTerminal.worker.promise(...)
|
||||||
|
this.postMessage({event,data: {...data,result}})
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
if( data.promiseId ){ // auto-callback to ISOTerminal.worker.promise(...)
|
||||||
|
this.postMessage({event,data: {...data,error:e}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
48
com/xterm.js
48
com/xterm.js
|
@ -92,6 +92,7 @@ const TERMINAL_THEME = {
|
||||||
|
|
||||||
AFRAME.registerComponent('xterm', {
|
AFRAME.registerComponent('xterm', {
|
||||||
schema: Object.assign({
|
schema: Object.assign({
|
||||||
|
XRrenderer: { type: 'string', default: 'canvas', },
|
||||||
cols: { type: 'number', default: 110, },
|
cols: { type: 'number', default: 110, },
|
||||||
rows: { type: 'number', default: Math.floor( (window.innerHeight * 0.7 ) * 0.054 ) },
|
rows: { type: 'number', default: Math.floor( (window.innerHeight * 0.7 ) * 0.054 ) },
|
||||||
canvasLatency:{ type:'number', default: 200 }
|
canvasLatency:{ type:'number', default: 200 }
|
||||||
|
@ -108,21 +109,33 @@ AFRAME.registerComponent('xterm', {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// 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
|
this.el.terminalElement = terminalElement
|
||||||
|
|
||||||
// we switch between dom/canvas rendering because canvas looks pixely in nonimmersive mode
|
if( this.data.XRrenderer == 'canvas' ){
|
||||||
this.el.sceneEl.addEventListener('enter-vr', this.enterImmersive.bind(this) )
|
// setup slightly bigger black backdrop (this.el.getObject3D("mesh"))
|
||||||
this.el.sceneEl.addEventListener('enter-ar', this.enterImmersive.bind(this) )
|
// and terminal text (this.el.planeText.getObject("mesh"))
|
||||||
this.el.sceneEl.addEventListener('exit-vr', this.exitImmersive.bind(this) )
|
this.el.setAttribute("geometry",`primitive: box; width:2.07; height:${this.data.rows*5.3/this.data.cols}*2; depth: -0.12`)
|
||||||
this.el.sceneEl.addEventListener('exit-ar', this.exitImmersive.bind(this) )
|
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)
|
||||||
|
|
||||||
|
// we switch between dom/canvas rendering because canvas looks pixely in nonimmersive mode
|
||||||
|
this.el.sceneEl.addEventListener('enter-vr', this.enterImmersive.bind(this) )
|
||||||
|
this.el.sceneEl.addEventListener('enter-ar', this.enterImmersive.bind(this) )
|
||||||
|
this.el.sceneEl.addEventListener('exit-vr', this.exitImmersive.bind(this) )
|
||||||
|
this.el.sceneEl.addEventListener('exit-ar', this.exitImmersive.bind(this) )
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tick = AFRAME.utils.throttleLeadingAndTrailing( () => {
|
||||||
|
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.renderer._renderDebouncer._innerRefresh()
|
||||||
|
}
|
||||||
|
}, this.data.canvasLatency)
|
||||||
|
|
||||||
// Build up a theme object
|
// Build up a theme object
|
||||||
const theme = Object.keys(this.data).reduce((theme, key) => {
|
const theme = Object.keys(this.data).reduce((theme, key) => {
|
||||||
|
@ -149,15 +162,6 @@ AFRAME.registerComponent('xterm', {
|
||||||
rendererType: this.renderType // 'dom' // 'canvas'
|
rendererType: this.renderType // 'dom' // 'canvas'
|
||||||
})
|
})
|
||||||
|
|
||||||
this.tick = AFRAME.utils.throttleLeadingAndTrailing( () => {
|
|
||||||
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.renderer._renderDebouncer._innerRefresh()
|
|
||||||
}
|
|
||||||
}, this.data.canvasLatency)
|
|
||||||
|
|
||||||
this.term.open(terminalElement)
|
this.term.open(terminalElement)
|
||||||
this.term.focus()
|
this.term.focus()
|
||||||
this.setRenderType('dom')
|
this.setRenderType('dom')
|
||||||
|
|
Loading…
Reference in New Issue