huge refactor to webworker..tears and happiness..
/ test (push) Successful in 4s
Details
/ test (push) Successful in 4s
Details
This commit is contained in:
parent
35ff5dc710
commit
6b9cd42569
|
@ -1,32 +1,33 @@
|
||||||
function ISOTerminal(instance,opts){
|
/*
|
||||||
// create a neutral isoterminal object which can be decorated
|
*
|
||||||
// with prototype functions and has addListener() and dispatchEvent()
|
* css/html template
|
||||||
let obj = new EventTarget()
|
*
|
||||||
obj.instance = instance
|
* ┌─────────┐ ┌────────────┐ ┌─────────────┐ exit-AR
|
||||||
obj.opts = opts
|
* ┌───────►│ com/dom ┼──►│ com/window ├─►│ domrenderer │◄────────── exit-VR ◄─┐
|
||||||
// register default event listeners (enable file based features like isoterminal/jsconsole.js e.g.)
|
* │ └─────────┘ └────────────┘ └─────▲───────┘ │
|
||||||
for( let event in ISOTerminal.listener )
|
* │ │ ┌───────────────┐ │
|
||||||
for( let cb in ISOTerminal.listener[event] )
|
* ┌──────────┴────────┐ ┌─────┴──────┐ │ xterm.js │ ┌─────────────────────────────┐
|
||||||
obj.addEventListener( event, ISOTerminal.listener[event][cb] )
|
* │ com/isoterminal ├────────────────────────────►│com/xterm.js│◄─┤ │ │com/html-as-texture-in-XR.js │
|
||||||
// compose object with functions
|
* └────────┬─┬────────┘ └──┬──────┬▲─┘ │ xterm.css │ └─────────────────────────────┘
|
||||||
for( let i in ISOTerminal.prototype ) obj[i] = ISOTerminal.prototype[i]
|
* │ │ ┌────────┐ ┌─────────▼──┐ ││ └───────────────┘ │ ▲
|
||||||
obj.emit('init')
|
* │ └───────►│ plane ├─────►text───┼►canvas │◄────────────────── enter-VR │ │
|
||||||
return obj
|
* │ └────────┘ └────────────┘ ││ enter-AR ◄─┘ │
|
||||||
}
|
* │ ││ │
|
||||||
|
* │ ││ │
|
||||||
ISOTerminal.prototype.emit = function(event,data){
|
* │ ISOTerminal.js ││ │
|
||||||
data = data || false
|
* │ ┌───────────────────────────┐◄────┘│ │
|
||||||
this.dispatchEvent( new CustomEvent(event, {detail: data} ) )
|
* │ │ com/isoterminal/worker.js ├──────┘ │
|
||||||
}
|
* │ └──────────────┌────────────┤ │
|
||||||
|
* │ │ v86.js │ │
|
||||||
ISOTerminal.addEventListener = (event,cb) => {
|
* │ │ feat/*.js │ │
|
||||||
ISOTerminal.listener = ISOTerminal.listener || {}
|
* │ │ libv86.js │ │
|
||||||
ISOTerminal.listener[event] = ISOTerminal.listener[event] || []
|
* │ └────────────┘ │
|
||||||
ISOTerminal.listener[event].push(cb)
|
* │ │
|
||||||
}
|
* └────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
*
|
||||||
// ISOTerminal has defacto support for AFRAME
|
* NOTE: For convenience reasons, events are forwarded between com/isoterminal.js, worker.js and ISOTerminal
|
||||||
// but can be decorated to work without it as well
|
* Instead of a melting pot of different functionnames, events are flowing through everything (ISOTerminal.emit())
|
||||||
|
*/
|
||||||
|
|
||||||
if( typeof AFRAME != 'undefined '){
|
if( typeof AFRAME != 'undefined '){
|
||||||
|
|
||||||
|
@ -68,14 +69,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
// 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
|
||||||
core: "com/isoterminal/core.js",
|
ISOTerminal: "com/isoterminal/ISOTerminal.js",
|
||||||
utils_9p: "com/isoterminal/feat/9pfs_utils.js",
|
|
||||||
boot: "com/isoterminal/feat/boot.js",
|
|
||||||
jsconsole: "com/isoterminal/feat/jsconsole.js",
|
|
||||||
javascript: "com/isoterminal/feat/javascript.js",
|
|
||||||
indexhtml: "com/isoterminal/feat/index.html.js",
|
|
||||||
indexjs: "com/isoterminal/feat/index.js.js",
|
|
||||||
autorestore: "com/isoterminal/feat/autorestore.js",
|
|
||||||
localforage: "https://cdn.rawgit.com/mozilla/localForage/master/dist/localforage.js"
|
localforage: "https://cdn.rawgit.com/mozilla/localForage/master/dist/localforage.js"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -83,9 +77,6 @@ if( typeof AFRAME != 'undefined '){
|
||||||
scale: 0.66,
|
scale: 0.66,
|
||||||
events: ['click','keydown'],
|
events: ['click','keydown'],
|
||||||
html: (me) => `<div class="isoterminal">
|
html: (me) => `<div class="isoterminal">
|
||||||
<div id="screen" style="white-space: pre; font: 14px monospace; "></div>
|
|
||||||
<canvas style="display: none"></canvas>
|
|
||||||
<div id="serial"></div>
|
|
||||||
</div>`,
|
</div>`,
|
||||||
|
|
||||||
css: (me) => `.isoterminal{
|
css: (me) => `.isoterminal{
|
||||||
|
@ -112,7 +103,8 @@ if( typeof AFRAME != 'undefined '){
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.isoterminal *,
|
.isoterminal *,
|
||||||
.xterm-dom-renderer-owner-1 .xterm-rows {
|
.isotemrinal .xterm-dom-renderer-owner-1 .xterm-rows {
|
||||||
|
background:transparent !important;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: "Cousine",Liberation Mono,DejaVu Sans Mono,Courier New,monospace;
|
font-family: "Cousine",Liberation Mono,DejaVu Sans Mono,Courier New,monospace;
|
||||||
font-weight:500 !important;
|
font-weight:500 !important;
|
||||||
|
@ -122,6 +114,11 @@ if( typeof AFRAME != 'undefined '){
|
||||||
|
|
||||||
.isoterminal style{ display:none }
|
.isoterminal style{ display:none }
|
||||||
|
|
||||||
|
#overlay .winbox:has(> .isoterminal){
|
||||||
|
background:transparent;
|
||||||
|
box-shadow:none;
|
||||||
|
}
|
||||||
|
|
||||||
.wb-body:has(> .isoterminal){
|
.wb-body:has(> .isoterminal){
|
||||||
background: #000C;
|
background: #000C;
|
||||||
overflow:hidden;
|
overflow:hidden;
|
||||||
|
@ -135,6 +132,11 @@ if( typeof AFRAME != 'undefined '){
|
||||||
.isoterminal div{ display:block; }
|
.isoterminal div{ display:block; }
|
||||||
.isoterminal span{ display: inline }
|
.isoterminal span{ display: inline }
|
||||||
|
|
||||||
|
.isoterminal .xterm-helpers {
|
||||||
|
position:absolute;
|
||||||
|
opacity:0;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes fade {
|
@keyframes fade {
|
||||||
from { opacity: 1.0; }
|
from { opacity: 1.0; }
|
||||||
50% { opacity: 0.5; }
|
50% { opacity: 0.5; }
|
||||||
|
@ -158,13 +160,27 @@ if( typeof AFRAME != 'undefined '){
|
||||||
initTerminal: async function(singleton){
|
initTerminal: async function(singleton){
|
||||||
|
|
||||||
if( this.data.xterm ){
|
if( this.data.xterm ){
|
||||||
this.requires.xtermjs = "https://unpkg.com/@xterm/xterm@5.5.0/lib/xterm.js"
|
// why 3.12?
|
||||||
this.requires.xtermcss = "https://unpkg.com/@xterm/xterm@5.5.0/css/xterm.css"
|
// first versions used 1.5.4, a typescript rewrite which:
|
||||||
this.requires.xterm = "com/isoterminal/feat/xterm.js"
|
// * does not use canvas anymore [which would be ideal for THREE.js texture]
|
||||||
// xterm relies on window.requestAnimationFrame which is not called in XR (xrSession.requestAnimationFrame is)
|
// * does not allow switching between dom/canvas
|
||||||
|
// * only allows a standalone WebGL addon (conflicts with THREE)
|
||||||
|
// * heavily dependent on requestAnimationFrame (conflicts with THREE)
|
||||||
|
// * typescript-rewrite results in ~300k lib (instead of 96k)
|
||||||
|
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.xtermcss = "com/xterm.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
let s = await AFRAME.utils.require(this.requires)
|
await AFRAME.utils.require(this.requires)
|
||||||
|
await AFRAME.utils.require({ // ISOTerminal plugins
|
||||||
|
boot: "com/isoterminal/feat/boot.js",
|
||||||
|
javascript: "com/isoterminal/feat/javascript.js",
|
||||||
|
jsconsole: "com/isoterminal/feat/jsconsole.js",
|
||||||
|
//indexhtml: "com/isoterminal/feat/index.html.js",
|
||||||
|
//indexjs: "com/isoterminal/feat/index.js.js",
|
||||||
|
//autorestore: "com/isoterminal/feat/autorestore.js",
|
||||||
|
})
|
||||||
|
|
||||||
this.el.setAttribute("selfcontainer","")
|
this.el.setAttribute("selfcontainer","")
|
||||||
|
|
||||||
|
@ -194,10 +210,12 @@ 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","")
|
||||||
|
instance.addEventListener("xterm-input", (e) => this.isoterminal.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.runISO(opts)
|
this.isoterminal.start(opts)
|
||||||
})
|
})
|
||||||
|
|
||||||
instance.setAttribute("dom", "")
|
instance.setAttribute("dom", "")
|
||||||
|
@ -214,9 +232,6 @@ if( typeof AFRAME != 'undefined '){
|
||||||
this.isoterminal.addEventListener('ready', (e)=>{
|
this.isoterminal.addEventListener('ready', (e)=>{
|
||||||
instance.dom.classList.remove('blink')
|
instance.dom.classList.remove('blink')
|
||||||
this.isoterminal.emit('status',"running")
|
this.isoterminal.emit('status',"running")
|
||||||
setTimeout( () => { // important: after window maximize animation to get true size
|
|
||||||
instance.setAttribute("html-as-texture-in-xr", `domid: #${instance.uid}`) // only show aframe-html in xr
|
|
||||||
},1500)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.isoterminal.addEventListener('status', function(e){
|
this.isoterminal.addEventListener('status', function(e){
|
||||||
|
@ -236,8 +251,8 @@ if( typeof AFRAME != 'undefined '){
|
||||||
instance.addEventListener('window.onmaximize', resize )
|
instance.addEventListener('window.onmaximize', resize )
|
||||||
|
|
||||||
const focus = (e) => {
|
const focus = (e) => {
|
||||||
if( this.isoterminal?.emulator?.serial_adapter?.term ){
|
if( this.el.components.xterm ){
|
||||||
this.isoterminal.emulator.serial_adapter.term.focus()
|
this.el.components.xterm.term.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instance.addEventListener('obbcollisionstarted', focus )
|
instance.addEventListener('obbcollisionstarted', focus )
|
||||||
|
@ -257,10 +272,6 @@ if( typeof AFRAME != 'undefined '){
|
||||||
// reactive events for this.data updates
|
// reactive events for this.data updates
|
||||||
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
|
myvalue: function(e){ this.el.dom.querySelector('b').innerText = this.data.myvalue },
|
||||||
|
|
||||||
ready: function( ){
|
|
||||||
this.el.dom.style.display = 'none'
|
|
||||||
},
|
|
||||||
|
|
||||||
launcher: async function(){
|
launcher: async function(){
|
||||||
this.initTerminal()
|
this.initTerminal()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
ISOTerminal.addEventListener('emulator-started', function(){
|
|
||||||
let emulator = this.emulator
|
let emulator = this.emulator
|
||||||
let isoterminal = this
|
|
||||||
|
|
||||||
emulator.fs9p.update_file = async function(file,data){
|
emulator.fs9p.update_file = async function(file,data){
|
||||||
|
const convert = ISOTerminal.prototype.convert
|
||||||
|
|
||||||
const p = this.SearchPath(file);
|
const p = this.SearchPath(file);
|
||||||
|
|
||||||
|
@ -12,18 +11,19 @@ ISOTerminal.addEventListener('emulator-started', function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
const inode = this.GetInode(p.id);
|
const inode = this.GetInode(p.id);
|
||||||
const buf = typeof data == 'string' ? isoterminal.convert.toUint8Array(data) : data
|
const buf = typeof data == 'string' ? convert.toUint8Array(data) : data
|
||||||
await this.Write(p.id,0, buf.length, buf )
|
await this.Write(p.id,0, buf.length, buf )
|
||||||
// update inode
|
// update inode
|
||||||
inode.size = buf.length
|
inode.size = buf.length
|
||||||
const now = Math.round(Date.now() / 1000);
|
const now = Math.round(Date.now() / 1000);
|
||||||
inode.atime = inode.mtime = now;
|
inode.atime = inode.mtime = now;
|
||||||
isoterminal.exec(`touch ${file}`) // update inode
|
this.postMessage({event:'exec',data:[`touch ${file}`]}) // update inode
|
||||||
return new Promise( (resolve,reject) => resolve(buf) )
|
return new Promise( (resolve,reject) => resolve(buf) )
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator.fs9p.append_file = async function(file,data){
|
emulator.fs9p.append_file = async function(file,data){
|
||||||
|
const convert = ISOTerminal.prototype.convert
|
||||||
|
|
||||||
const p = this.SearchPath(file);
|
const p = this.SearchPath(file);
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ ISOTerminal.addEventListener('emulator-started', function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
const inode = this.GetInode(p.id);
|
const inode = this.GetInode(p.id);
|
||||||
const buf = typeof data == 'string' ? isoterminal.convert.toUint8Array(data) : data
|
const buf = typeof data == 'string' ? convert.toUint8Array(data) : data
|
||||||
await this.Write(p.id, inode.size, buf.length, buf )
|
await this.Write(p.id, inode.size, buf.length, buf )
|
||||||
// update inode
|
// update inode
|
||||||
inode.size = inode.size + buf.length
|
inode.size = inode.size + buf.length
|
||||||
|
@ -43,4 +43,5 @@ ISOTerminal.addEventListener('emulator-started', function(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
this['fs9p.append_file'] = function(){ emulator.fs9p.append_file.apply(emulator.fs9p, arguments[0]) }
|
||||||
|
this['fs9p.update_file'] = function(){ emulator.fs9p.update_file.apply(emulator.fs9p, arguments[0]) }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
ISOTerminal.addEventListener('emulator-started', function(e){
|
ISOTerminal.addEventListener('emulator-started', function(e){
|
||||||
|
return console.log("TODO: autorestore.js")
|
||||||
this.autorestore(e)
|
this.autorestore(e)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,11 @@ ISOTerminal.prototype.boot = async function(e){
|
||||||
// set environment
|
// set environment
|
||||||
let env = ['export BROWSER=1']
|
let env = ['export BROWSER=1']
|
||||||
for ( let i in document.location ){
|
for ( let i in document.location ){
|
||||||
if( typeof document.location[i] == 'string' )
|
if( typeof document.location[i] == 'string' ){
|
||||||
env.push( 'export '+String(i).toUpperCase()+'="'+decodeURIComponent( document.location[i]+'"') )
|
env.push( 'export '+String(i).toUpperCase()+'="'+decodeURIComponent( document.location[i]+'"') )
|
||||||
}
|
}
|
||||||
await this.emulator.create_file("profile.browser", this.convert.toUint8Array( env.join('\n') ) )
|
}
|
||||||
|
await this.emit("emulator.create_file", ["profile.browser", this.convert.toUint8Array( env.join('\n') ) ] )
|
||||||
|
|
||||||
if( this.serial_input == 0 ){
|
if( this.serial_input == 0 ){
|
||||||
if( !this.noboot ){
|
if( !this.noboot ){
|
||||||
|
@ -18,10 +19,5 @@ ISOTerminal.prototype.boot = async function(e){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( this.emulator.serial_adapter ) this.emulator.serial_adapter.term.focus()
|
|
||||||
else{
|
|
||||||
let els = [...document.querySelectorAll("div#screen")]
|
|
||||||
els.map( (el) => el.focus() )
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,32 @@
|
||||||
ISOTerminal.addEventListener('init', function(){
|
if( typeof emulator != 'undefined' ){
|
||||||
|
// inside worker-thread
|
||||||
this.addEventListener('emulator-started', function(e){
|
|
||||||
|
|
||||||
const emulator = this.emulator
|
|
||||||
|
|
||||||
// unix to js device
|
// unix to js device
|
||||||
this.readFromPipe( '/mnt/js', async (data) => {
|
emulator.readFromPipe( 'dev/browser/js', async (data) => {
|
||||||
const buf = await emulator.read_file("js")
|
const convert = ISOTerminal.prototype.convert
|
||||||
const script = this.convert.Uint8ArrayToString(buf)
|
const buf = await this.emulator.read_file("dev/browser/js")
|
||||||
|
const script = convert.Uint8ArrayToString(buf)
|
||||||
let PID="?"
|
let PID="?"
|
||||||
try{
|
try{
|
||||||
if( script.match(/^PID/) ){
|
if( script.match(/^PID/) ){
|
||||||
PID = script.match(/^PID=([0-9]+);/)[1]
|
PID = script.match(/^PID=([0-9]+);/)[1]
|
||||||
}
|
}
|
||||||
let res = (new Function(`${script}`))()
|
this.postMessage({event:'javascript-eval',data:{script,PID}})
|
||||||
if( res && typeof res != 'string' ) res = JSON.stringify(res,null,2)
|
|
||||||
// write output to 9p with PID as filename
|
|
||||||
// *FIXME* not flexible / robust
|
|
||||||
emulator.create_file(PID, this.convert.toUint8Array(res) )
|
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
}else{
|
||||||
|
// inside browser-thread
|
||||||
|
|
||||||
|
ISOTerminal.addEventListener('javascript-eval', function(e){
|
||||||
|
const {script,PID} = e.detail
|
||||||
|
let res = (new Function(`${script}`))()
|
||||||
|
if( res && typeof res != 'string' ) res = JSON.stringify(res,null,2)
|
||||||
|
// write output to 9p with PID as filename
|
||||||
|
// *FIXME* not flexible / robust
|
||||||
|
this.emit('emulator.create_file', [PID, this.convert.toUint8Array(res)] )
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ ISOTerminal.prototype.redirectConsole = function(handler){
|
||||||
}
|
}
|
||||||
|
|
||||||
ISOTerminal.addEventListener('emulator-started', function(){
|
ISOTerminal.addEventListener('emulator-started', function(){
|
||||||
let emulator = this.emulator
|
|
||||||
|
|
||||||
this.redirectConsole( (str,prefix) => {
|
this.redirectConsole( (str,prefix) => {
|
||||||
let finalStr = ""
|
let finalStr = ""
|
||||||
|
@ -35,7 +34,7 @@ ISOTerminal.addEventListener('emulator-started', function(){
|
||||||
str.trim().split("\n").map( (line) => {
|
str.trim().split("\n").map( (line) => {
|
||||||
finalStr += '\x1b[38;5;165m/dev/browser: \x1b[0m'+prefix+line+'\n'
|
finalStr += '\x1b[38;5;165m/dev/browser: \x1b[0m'+prefix+line+'\n'
|
||||||
})
|
})
|
||||||
emulator.fs9p.append_file( "console", finalStr )
|
this.emit('fs9p.append_file', ["/dev/browser/console",finalStr])
|
||||||
})
|
})
|
||||||
|
|
||||||
window.addEventListener('error', function(event) {
|
window.addEventListener('error', function(event) {
|
||||||
|
@ -51,9 +50,4 @@ ISOTerminal.addEventListener('emulator-started', function(){
|
||||||
console.error(event);
|
console.error(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
// enable/disable logging file (echo 1 > mnt/console.tty)
|
|
||||||
this.readFromPipe( '/mnt/console.tty', (data) => {
|
|
||||||
emulator.log_to_tty = ( String(data).trim() == '1')
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -27,7 +27,6 @@ ISOTerminal.prototype.xtermInit = function(){
|
||||||
})
|
})
|
||||||
|
|
||||||
term.onRender( () => {
|
term.onRender( () => {
|
||||||
|
|
||||||
// xterm relies on requestAnimationFrame (which does not called in immersive mode)
|
// xterm relies on requestAnimationFrame (which does not called in immersive mode)
|
||||||
let _window = term._core._coreBrowserService._window
|
let _window = term._core._coreBrowserService._window
|
||||||
if( !_window._XRSH_proxied ){ // patch the planet!
|
if( !_window._XRSH_proxied ){ // patch the planet!
|
||||||
|
@ -40,8 +39,10 @@ ISOTerminal.prototype.xtermInit = function(){
|
||||||
// term.tid = null
|
// term.tid = null
|
||||||
// },100)
|
// },100)
|
||||||
//}
|
//}
|
||||||
|
this.i = 0
|
||||||
const requestAnimationFrameAFRAME = AFRAME.utils.throttleLeadingAndTrailing(
|
const requestAnimationFrameAFRAME = AFRAME.utils.throttleLeadingAndTrailing(
|
||||||
function(cb){ cb() },150
|
function(cb){ cb() }
|
||||||
|
,150
|
||||||
)
|
)
|
||||||
|
|
||||||
// we proxy the _window object of xterm, and reroute
|
// we proxy the _window object of xterm, and reroute
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
importScripts("libv86.js");
|
||||||
|
importScripts("ISOTerminal.js") // we don't instance it again here (just use its functions)
|
||||||
|
|
||||||
|
//var emulator = new V86({
|
||||||
|
// wasm_path: "../build/v86.wasm",
|
||||||
|
// memory_size: 32 * 1024 * 1024,
|
||||||
|
// vga_memory_size: 2 * 1024 * 1024,
|
||||||
|
// bios: {
|
||||||
|
// url: "../bios/seabios.bin",
|
||||||
|
// },
|
||||||
|
// vga_bios: {
|
||||||
|
// url: "../bios/vgabios.bin",
|
||||||
|
// },
|
||||||
|
// cdrom: {
|
||||||
|
// url: "../images/linux4.iso",
|
||||||
|
// },
|
||||||
|
// autostart: true,
|
||||||
|
//});
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//emulator.add_listener("serial0-output-byte", function(byte)
|
||||||
|
//{
|
||||||
|
// var chr = String.fromCharCode(byte);
|
||||||
|
// this.postMessage(chr);
|
||||||
|
//}.bind(this));
|
||||||
|
//
|
||||||
|
//this.onmessage = function(e)
|
||||||
|
//{
|
||||||
|
// emulator.serial0_send(e.data);
|
||||||
|
//};
|
||||||
|
|
||||||
|
this.runISO = function(opts){
|
||||||
|
if( opts.cdrom ) opts.cdrom.url = "../../"+opts.cdrom.url
|
||||||
|
if( opts.bzimage ) opts.bzimage.url = "../../"+opts.bzimage.url
|
||||||
|
|
||||||
|
let emulator = this.emulator = new V86(opts);
|
||||||
|
console.log("worker:started emulator")
|
||||||
|
|
||||||
|
// event forwarding
|
||||||
|
emulator.add_listener("serial0-output-byte", function(byte){
|
||||||
|
this.postMessage({event:"serial0-output-byte",data:byte});
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
emulator.add_listener("emulator-started", function(){
|
||||||
|
importScripts("feat/9pfs_utils.js")
|
||||||
|
this.postMessage({event:"emulator-started",data:false});
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* forward events/functions so non-worker world can reach them
|
||||||
|
*/
|
||||||
|
this['emulator.create_file'] = function(){ emulator.create_file.apply(emulator, arguments[0]) }
|
||||||
|
this['emulator.read_file'] = function(){ emulator.read_file.apply(emulator, arguments[0]) }
|
||||||
|
|
||||||
|
|
||||||
|
// filename will be read from 9pfs: "/mnt/"+filename
|
||||||
|
emulator.readFromPipe = function(filename,cb){
|
||||||
|
emulator.add_listener("9p-write-end", async (opts) => {
|
||||||
|
if ( opts[0] == filename.replace(/.*\//,'') ){
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
importScripts("feat/javascript.js")
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* forward events/functions so non-worker world can reach them
|
||||||
|
*/
|
||||||
|
|
||||||
|
this['serial0-input'] = function(c){
|
||||||
|
this.emulator.bus.send( 'serial0-input', c)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onmessage = function(e){
|
||||||
|
let {event,data} = e.data
|
||||||
|
if( this[event] ) this[event](data)
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
let terminalInstance = 0
|
||||||
|
|
||||||
|
const TERMINAL_THEME = {
|
||||||
|
theme_foreground: {
|
||||||
|
// 'default': '#ffffff'
|
||||||
|
},
|
||||||
|
theme_background: {
|
||||||
|
// 'default': '#000'
|
||||||
|
},
|
||||||
|
theme_cursor: {
|
||||||
|
// 'default': '#ffffff'
|
||||||
|
},
|
||||||
|
theme_selection: {
|
||||||
|
// 'default': 'rgba(255, 255, 255, 0.3)'
|
||||||
|
},
|
||||||
|
theme_black: {
|
||||||
|
// 'default': '#000000'
|
||||||
|
},
|
||||||
|
theme_red: {
|
||||||
|
// 'default': '#e06c75'
|
||||||
|
},
|
||||||
|
theme_brightRed: {
|
||||||
|
// 'default': '#e06c75'
|
||||||
|
},
|
||||||
|
theme_green: {
|
||||||
|
// 'default': '#A4EFA1'
|
||||||
|
},
|
||||||
|
theme_brightGreen: {
|
||||||
|
// 'default': '#A4EFA1'
|
||||||
|
},
|
||||||
|
theme_brightYellow: {
|
||||||
|
// 'default': '#EDDC96'
|
||||||
|
},
|
||||||
|
theme_yellow: {
|
||||||
|
// 'default': '#EDDC96'
|
||||||
|
},
|
||||||
|
theme_magenta: {
|
||||||
|
// 'default': '#e39ef7'
|
||||||
|
},
|
||||||
|
theme_brightMagenta: {
|
||||||
|
// 'default': '#e39ef7'
|
||||||
|
},
|
||||||
|
theme_cyan: {
|
||||||
|
// 'default': '#5fcbd8'
|
||||||
|
},
|
||||||
|
theme_brightBlue: {
|
||||||
|
// 'default': '#5fcbd8'
|
||||||
|
},
|
||||||
|
theme_brightCyan: {
|
||||||
|
// 'default': '#5fcbd8'
|
||||||
|
},
|
||||||
|
theme_blue: {
|
||||||
|
// 'default': '#5fcbd8'
|
||||||
|
},
|
||||||
|
theme_white: {
|
||||||
|
// 'default': '#d0d0d0'
|
||||||
|
},
|
||||||
|
theme_brightBlack: {
|
||||||
|
// 'default': '#808080'
|
||||||
|
},
|
||||||
|
theme_brightWhite: {
|
||||||
|
// 'default': '#ffffff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AFRAME.registerComponent('xterm', {
|
||||||
|
schema: Object.assign({
|
||||||
|
cols: {
|
||||||
|
type: 'number',
|
||||||
|
default: 110,
|
||||||
|
},
|
||||||
|
rows: {
|
||||||
|
type: 'number',
|
||||||
|
default: Math.floor( (window.innerHeight * 0.7 ) * 0.054 )
|
||||||
|
},
|
||||||
|
}, TERMINAL_THEME),
|
||||||
|
|
||||||
|
write: function(message) {
|
||||||
|
this.term.write(message)
|
||||||
|
},
|
||||||
|
init: function () {
|
||||||
|
const terminalElement = document.createElement('div')
|
||||||
|
terminalElement.setAttribute('style', `
|
||||||
|
width: ${Math.floor( window.innerWidth * 0.7 )}px;
|
||||||
|
height: ${Math.floor( window.innerHeight * 0.7 )}px;
|
||||||
|
overflow: hidden;
|
||||||
|
`)
|
||||||
|
|
||||||
|
this.el.dom.appendChild(terminalElement)
|
||||||
|
//document.body.appendChild(terminalElement)
|
||||||
|
this.el.terminalElement = terminalElement
|
||||||
|
|
||||||
|
// Build up a theme object
|
||||||
|
const theme = Object.keys(this.data).reduce((theme, key) => {
|
||||||
|
if (!key.startsWith('theme_')) return theme
|
||||||
|
const data = this.data[key]
|
||||||
|
if(!data) return theme
|
||||||
|
theme[key.slice('theme_'.length)] = data
|
||||||
|
return theme
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
this.renderType = 'dom'
|
||||||
|
|
||||||
|
const term = new Terminal({
|
||||||
|
theme: theme,
|
||||||
|
allowTransparency: true,
|
||||||
|
cursorBlink: true,
|
||||||
|
disableStdin: false,
|
||||||
|
rows: this.data.rows,
|
||||||
|
cols: this.data.cols,
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: 1.15,
|
||||||
|
rendererType: this.renderType
|
||||||
|
})
|
||||||
|
|
||||||
|
this.term = term
|
||||||
|
|
||||||
|
term.open(terminalElement)
|
||||||
|
term.focus()
|
||||||
|
|
||||||
|
terminalElement.querySelector('.xterm-viewport').style.background = 'transparent'
|
||||||
|
|
||||||
|
////// now we can scale canvases to the parent element
|
||||||
|
const $screen = terminalElement.querySelector('.xterm-screen')
|
||||||
|
$screen.style.width = '100%'
|
||||||
|
|
||||||
|
term.on('refresh', () => {
|
||||||
|
if( this.renderType == 'canvas' ){
|
||||||
|
const material = this.el.getObject3D('mesh').material
|
||||||
|
if (!material.map) return
|
||||||
|
this.canvasContext.drawImage(this.cursorCanvas, 0,0)
|
||||||
|
material.map.needsUpdate = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
term.on('data', (data) => {
|
||||||
|
this.el.emit('xterm-input', data)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.el.addEventListener('click', () => {
|
||||||
|
term.focus()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.el.addEventListener('serial-output-byte', (e) => {
|
||||||
|
const byte = e.detail
|
||||||
|
var chr = String.fromCharCode(byte);
|
||||||
|
this.term.write(chr)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.el.addEventListener('serial-output-string', (e) => this.term.write(e.detail) )
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
setRenderType: function(type){
|
||||||
|
|
||||||
|
if( type.match(/(dom|canvas)/) ){
|
||||||
|
|
||||||
|
if( type == 'dom'){
|
||||||
|
this.el.removeAttribute('material')
|
||||||
|
}
|
||||||
|
|
||||||
|
term.setOption('rendererType',type )
|
||||||
|
this.renderType = type
|
||||||
|
|
||||||
|
if( type == 'canvas'){
|
||||||
|
this.canvas = terminalElement.querySelector('.xterm-text-layer')
|
||||||
|
this.canvasContext = this.canvas.getContext('2d')
|
||||||
|
this.cursorCanvas = terminalElement.querySelector('.xterm-cursor-layer')
|
||||||
|
this.el.setAttribute('material', 'transparent', true)
|
||||||
|
this.el.setAttribute('material', 'src', '#' + this.canvas.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
})
|
Loading…
Reference in New Issue