reworked autorestore + added touchkeyboard compatibility
/ mirror_to_github (push) Successful in 30s Details
/ test (push) Successful in 5s Details

This commit is contained in:
Leon van Kammen 2024-10-23 16:50:07 +00:00
parent ce44449fc4
commit 4f83e1af0b
7 changed files with 262 additions and 140 deletions

View File

@ -36,7 +36,7 @@ AFRAME.registerComponent('codemirror', {
css: (me) => `.CodeMirror{ css: (me) => `.CodeMirror{
width: ${me.com.data.width}px !important; width: ${me.com.data.width}px !important;
height: ${me.com.data.height}px !important; height: ${me.com.data.height-30}px !important;
} }
.codemirror *{ .codemirror *{
font-size: 14px; font-size: 14px;
@ -45,7 +45,10 @@ AFRAME.registerComponent('codemirror', {
letter-spacing: 0 !important; letter-spacing: 0 !important;
text-shadow: 0px 0px 10px #F075; text-shadow: 0px 0px 10px #F075;
} }
#${me.dom.id} .wb-body { overflow:hidden; }
.wb-body:has(> .codemirror){
overflow:hidden;
}
.CodeMirror { .CodeMirror {
margin-top:18px; margin-top:18px;
@ -78,11 +81,30 @@ AFRAME.registerComponent('codemirror', {
this.editor.updateFile( this.data.file, instance.getValue() ) this.editor.updateFile( this.data.file, instance.getValue() )
}) })
this
.handleFocus()
setTimeout( () => { setTimeout( () => {
this.el.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}`) // only show aframe-html in xr this.el.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}`) // only show aframe-html in xr
},1500) },1500)
}, },
handleFocus: function(){
const focus = (showdom) => (e) => {
if( this.editor ){
this.editor.focus()
}
if( this.el.components.window && this.data.renderer == 'canvas'){
this.el.components.window.show( showdom )
}
}
this.el.addEventListener('obbcollisionstarted', focus(false) )
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) )
},
updateFile: async function(file,str){ updateFile: async function(file,str){
// 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

View File

@ -21,7 +21,7 @@ if( !AFRAME.components['html-as-texture-in-xr'] ){
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' )
if( this.data.faceuser ){ if( this.data.faceuser ){
this.el.setAttribute("position", AFRAME.utils.XD.getPositionInFrontOfCamera(0.5) ) this.el.setAttribute("position", AFRAME.utils.XD.getPositionInFrontOfCamera(0.8) )
} }
}, },

View File

@ -37,6 +37,7 @@ if( typeof AFRAME != 'undefined '){
overlayfs: { type:"string"}, overlayfs: { type:"string"},
width: { type: 'number',"default": -1 }, width: { type: 'number',"default": -1 },
height: { type: 'number',"default": -1 }, height: { type: 'number',"default": -1 },
depth: { type: 'number',"default": 0.03 },
lineHeight: { type: 'number',"default": 18 }, lineHeight: { type: 'number',"default": 18 },
padding: { type: 'number',"default": 18 }, padding: { type: 'number',"default": 18 },
minimized: { type: 'boolean',"default":false}, minimized: { type: 'boolean',"default":false},
@ -44,19 +45,18 @@ 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
memory: { type: 'number', "default":64 }, // VM memory (in MB) memory: { type: 'number', "default":40 }, // VM memory (in MB) [NOTE: quest or smartphone might crash > 40mb ]
bufferLatency: { type: 'number', "default":30 }, // in ms: bufferlatency from webworker to xterm (batch-update every char to texture) bufferLatency: { type: 'number', "default":1 }, // in ms: bufferlatency from webworker to xterm (batch-update every char to texture)
debug: { type: 'boolean', "default":false } debug: { type: 'boolean', "default":false }
}, },
init: function(){ init: function(){
this.el.object3D.visible = false this.el.object3D.visible = false
if( this.data.width == -1 ) this.data.width = document.body.offsetWidth
if( this.data.height == -1 ) this.data.height = document.body.offsetHeight
this.data.width -= this.data.padding*2
this.data.height -= this.data.padding*2
this.calculateDimension()
this.initHud() this.initHud()
this.setupBox()
fetch(this.data.iso,{method: 'HEAD'}) fetch(this.data.iso,{method: 'HEAD'})
.then( (res) => { .then( (res) => {
if( res.status != 200 ) throw 'not found' if( res.status != 200 ) throw 'not found'
@ -79,17 +79,17 @@ if( typeof AFRAME != 'undefined '){
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 global features
PromiseWorker: "com/isoterminal/PromiseWorker.js", PromiseWorker: "com/isoterminal/PromiseWorker.js",
ISOTerminal: "com/isoterminal/ISOTerminal.js", ISOTerminal: "com/isoterminal/ISOTerminal.js",
localforage: "https://cdn.rawgit.com/mozilla/localForage/master/dist/localforage.js" localforage: "com/isoterminal/localforage.js",
}, },
dom: { dom: {
scale: 1.0, scale: 0.66,
events: ['click','keydown'], events: ['click','keydown'],
html: (me) => `<div class="isoterminal"> html: (me) => `<div class="isoterminal">
<div id="vt100" tabindex="0"> <div id="term" tabindex="0">
<pre></pre> <pre></pre>
</div> </div>
</div>`, </div>`,
@ -105,7 +105,7 @@ if( typeof AFRAME != 'undefined '){
position:relative; position:relative;
line-height: ${me.com.data.lineHeight}px; line-height: ${me.com.data.lineHeight}px;
} }
#vt100 { #term {
outline: none !important; outline: none !important;
} }
@font-face { @font-face {
@ -127,10 +127,6 @@ if( typeof AFRAME != 'undefined '){
border:none; border:none;
padding:none; padding:none;
} }
span blink:last-of-type{
border-right: 8px solid #F07;
padding-right: 3px;
}
#overlay .winbox:has(> .isoterminal){ #overlay .winbox:has(> .isoterminal){
background:transparent; background:transparent;
@ -218,8 +214,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', () => {
instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}; faceuser: true`) this.setupVT100(instance)
setTimeout( () => this.setupVT100(instance),100) setTimeout( () => {
instance.setAttribute("html-as-texture-in-xr", `domid: #term; faceuser: true`)
},100)
//instance.winbox.resize(720,380) //instance.winbox.resize(720,380)
let size = `width: ${this.data.width}; height: ${this.data.height}` let size = `width: ${this.data.width}; height: ${this.data.height}`
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}`)
@ -231,6 +229,8 @@ if( typeof AFRAME != 'undefined '){
// 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]
opts.cols = this.cols
opts.rows = this.rows
this.term.start(opts) this.term.start(opts)
}) })
@ -262,6 +262,7 @@ if( typeof AFRAME != 'undefined '){
if( this.el.components.window && this.data.renderer == 'canvas'){ if( this.el.components.window && this.data.renderer == 'canvas'){
this.el.components.window.show( showdom ) this.el.components.window.show( showdom )
} }
this.el.emit('focus',e.detail)
} }
this.el.addEventListener('obbcollisionstarted', focus(false) ) this.el.addEventListener('obbcollisionstarted', focus(false) )
@ -290,17 +291,20 @@ if( typeof AFRAME != 'undefined '){
}, },
setupVT100: function(instance){ setupVT100: function(instance){
const el = this.el.dom.querySelector('#vt100') const el = this.el.dom.querySelector('#term')
this.vt100 = new VT100( const opts = {
Math.floor(this.data.width/this.data.lineHeight), cols: this.cols,
Math.floor(this.data.height*0.8/this.data.lineHeight), rows: this.rows,
el, el_or_id: el,
100 max_scroll_lines: 100,
) nodim: true
}
this.vt100 = new VT100( opts )
this.vt100.el = el
this.vt100.curs_set( 1, true) this.vt100.curs_set( 1, true)
el.focus() el.focus()
this.el.addEventListener('focus', () => el.focus())
this.vt100.getch( (ch,t) => { this.vt100.getch( (ch,t) => {
console.log(ch)
this.term.send( ch ) this.term.send( ch )
this.vt100.curs_set( 0, true) this.vt100.curs_set( 0, true)
}) })
@ -323,6 +327,27 @@ if( typeof AFRAME != 'undefined '){
//}) //})
}, },
setupBox: function(){
// setup slightly bigger black backdrop (this.el.getObject3D("mesh"))
const w = this.data.width/950;
const h = this.data.height/950;
this.el.box = document.createElement('a-entity')
this.el.box.setAttribute("geometry",`primitive: box; width:${w}; height:${h}; depth: -${this.data.depth}`)
this.el.box.setAttribute("material","shader:flat; color:black; opacity:0.9; transparent:true; ")
this.el.box.setAttribute("position",`0 0 ${(this.data.depth/2)-0.001}`)
this.el.appendChild(this.el.box)
},
calculateDimension: function(){
if( this.data.width == -1 ) this.data.width = document.body.offsetWidth
if( this.data.height == -1 ) this.data.height = document.body.offsetHeight
if( this.data.height > this.data.width ) this.data.height = this.data.width // mobile smartphone fix
this.data.width -= this.data.padding*2
this.data.height -= this.data.padding*2
this.cols = Math.floor(this.data.width/this.data.lineHeight*1.9)
this.rows = Math.floor(this.data.height*0.5/this.data.lineHeight*1.7) // keep extra height for mobile browser bottom-bar (android)
},
events:{ events:{
// combined AFRAME+DOM reactive events // combined AFRAME+DOM reactive events

View File

@ -61,11 +61,11 @@ ISOTerminal.prototype.convert = {
const bytes = new Uint8Array(buffer); const bytes = new Uint8Array(buffer);
const len = bytes.byteLength; const len = bytes.byteLength;
for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]); for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
return window.btoa(binary); return btoa(binary);
}, },
base64ToArrayBuffer: function(base64) { base64ToArrayBuffer: function(base64) {
const binaryString = window.atob(base64); const binaryString = atob(base64);
const len = binaryString.length; const len = binaryString.length;
const bytes = new Uint8Array(len); const bytes = new Uint8Array(len);
@ -211,11 +211,14 @@ ISOTerminal.prototype.startVM = function(opts){
\r https://xrsh.isvery.ninja ▬▬▬▬▬▬▬▬▬▬▬▬ \r https://xrsh.isvery.ninja ▬▬▬▬▬▬▬▬▬▬▬▬
\r local-first, polyglot, unixy WebXR IDE & runtime \r local-first, polyglot, unixy WebXR IDE & runtime
\r \r
\r credits: NLnet | @nlnet@nlnet.nl \r credits
\r Leon van Kammen | @lvk@mastodon.online \r -------
\r Fabien Benetou | @utopiah@mastodon.pirateparty.be \r @nlnet@nlnet.nl
\r Mr Doob | THREE.js \r @lvk@mastodon.online
\r Diego Marcos | AFRAME.js \r @utopiah@mastodon.pirateparty.be
\r https://www.w3.org/TR/webxr
\r https://three.org
\r https://aframe.org
` `
const text_color = "\r" const text_color = "\r"

View File

@ -1,8 +1,15 @@
// https://raw.githubusercontent.com/vsinitsyn/vt100/refs/heads/coffeescript/public/javascripts/VT100.js // https://raw.githubusercontent.com/vetupinitsyn/vt100/refs/heads/coffeescript/public/javascripts/VT100.js
// //
// VT100.js -- a text terminal emulator in JavaScript with a ncurses-like // VT100.js -- a text terminal emulator in JavaScript with a ncurses-like
// interface and a POSIX-like interface. (The POSIX-like calls are // interface and a POSIX-like interface. (The POSIX-like calls are
// implemented on top of the ncurses-like calls, not the other way round.) // implemented on top of the ncurses-like calls, not the other way round.)
//
// required markup:
//
// <div id="term" tabindex="0">
// <pre></pre>
// </div>
// //
// Released under the GNU LGPL v2.1, by Frank Bi <bi@zompower.tk> // Released under the GNU LGPL v2.1, by Frank Bi <bi@zompower.tk>
// //
@ -77,8 +84,10 @@
// interpreted and acted on. // interpreted and acted on.
// constructor // constructor
function VT100(wd, ht, el_or_id, max_scroll_lines, fg, bg) function VT100(opts)
{ {
this.opts = opts
let {cols, rows, el_or_id, max_scroll_lines, fg, bg, nodim} = opts
if (!max_scroll_lines) { if (!max_scroll_lines) {
max_scroll_lines = 1000; max_scroll_lines = 1000;
} }
@ -92,20 +101,20 @@ function VT100(wd, ht, el_or_id, max_scroll_lines, fg, bg)
var r; var r;
var c; var c;
var scr = typeof el_or_id == 'string' ? document.getElementById(el_or_id) : el_or_id var scr = typeof el_or_id == 'string' ? document.getElementById(el_or_id) : el_or_id
this.wd_ = wd; this.wd_ = cols;
this.ht_ = ht; this.ht_ = rows;
// Keep up to max_scroll_lines of scrollback history. // Keep up to max_scroll_lines of scrollback history.
this.max_ht_ = max_scroll_lines; this.max_ht_ = max_scroll_lines;
this._set_colors(fg, bg); this._set_colors(fg, bg);
this.text_ = new Array(ht); this.text_ = new Array(rows);
this.attr_ = new Array(ht); this.attr_ = new Array(rows);
this.redraw_ = new Array(ht); this.redraw_ = new Array(rows);
this.scroll_region_ = [0, ht-1]; this.scroll_region_ = [0, rows-1];
this.start_row_id = 0; this.start_row_id = 0;
this.num_rows_ = ht; this.num_rows_ = rows;
for (r = 0; r < ht; ++r) { for (r = 0; r < rows; ++r) {
this.text_[r] = new Array(wd); this.text_[r] = new Array(cols);
this.attr_[r] = new Array(wd); this.attr_[r] = new Array(cols);
this.redraw_[r] = 1; this.redraw_[r] = 1;
} }
this.scr_ = scr; this.scr_ = scr;
@ -116,10 +125,14 @@ function VT100(wd, ht, el_or_id, max_scroll_lines, fg, bg)
this.key_buf_ = []; this.key_buf_ = [];
this.echo_ = false; this.echo_ = false;
this.esc_state_ = 0; this.esc_state_ = 0;
this.log_level_ = VT100.DEBUG //WARN; this.log_level_ = VT100.WARN
this.clear_all(); this.clear_all();
this.refresh();
// rate limit this.refresh
this.refresh = this.throttleSmart( VT100.prototype.refresh.bind(this), 100)
this.setupTouchInputFallback() // smartphone
} }
// public constants -- colours and colour pairs // public constants -- colours and colour pairs
@ -172,6 +185,7 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
var vt = VT100.the_vt_, ch; var vt = VT100.the_vt_, ch;
if (vt === undefined) if (vt === undefined)
return true; return true;
//if ( event.keyCode != undefined || !event.charCode){ //if ( event.keyCode != undefined || !event.charCode){
// ch = event.keyCode; // ch = event.keyCode;
// if (ch == 13) // if (ch == 13)
@ -180,11 +194,11 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
// return true; // return true;
// ch = String.fromCharCode(ch); // ch = String.fromCharCode(ch);
//} else { //} else {
ch = event.charCode;
//dump("ch: " + ch + "\n"); //dump("ch: " + ch + "\n");
//dump("ctrl?: " + event.ctrlKey + "\n"); //dump("ctrl?: " + event.ctrlKey + "\n");
vt.debug("onkeypress:: keyCode: " + event.keyCode + ", ch: " + event.charCode); vt.debug("onkeypress:: ch: " + event.code + " ,key: "+event.key);
if (ch) { if (event.key.length == 1) {
ch = event.key.charCodeAt(0)
if (ch > 255) if (ch > 255)
return true; return true;
if (event.ctrlKey && event.shiftKey) { if (event.ctrlKey && event.shiftKey) {
@ -203,51 +217,52 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
} }
} else { } else {
switch (event.key) { switch (event.key) {
case "Backspace": case "Backspace":
ch = '\b'; ch = '\b';
break; break;
case "Tab": case "Tab":
ch = '\t'; ch = '\t';
break; break;
case event.DOM_VK_RETURN: case "Enter":
case event.DOM_VK_ENTER: ch = '\n';
ch = '\r'; break;
break; case "ArrowUp":
case event.DOM_VK_UP: if (this.cursor_key_mode_ == VT100.CK_CURSOR)
if (this.cursor_key_mode_ == VT100.CK_CURSOR)
ch = '\x1b[A'; ch = '\x1b[A';
else else
ch = '\x1bOA'; ch = '\x1bOA';
break; break;
case event.DOM_VK_DOWN: case "ArrowDown":
if (this.cursor_key_mode_ == VT100.CK_CURSOR) if (this.cursor_key_mode_ == VT100.CK_CURSOR)
ch = '\x1b[B'; ch = '\x1b[B';
else else
ch = '\x1bOB'; ch = '\x1bOB';
break; break;
case event.DOM_VK_RIGHT: case "ArrowRight":
if (this.cursor_key_mode_ == VT100.CK_CURSOR) if (this.cursor_key_mode_ == VT100.CK_CURSOR)
ch = '\x1b[C'; ch = '\x1b[C';
else else
ch = '\x1bOC'; ch = '\x1bOC';
break; break;
case event.DOM_VK_LEFT: case "ArrowLeft":
if (this.cursor_key_mode_ == VT100.CK_CURSOR) if (this.cursor_key_mode_ == VT100.CK_CURSOR)
ch = '\x1b[D'; ch = '\x1b[D';
else else
ch = '\x1bOD'; ch = '\x1bOD';
break; break;
case event.DOM_VK_DELETE: case "Delete":
ch = '\x1b[3~'; ch = '\x1b[3~';
break; break;
case event.DOM_VK_HOME: case "Home":
ch = '\x1b[H'; ch = '\x1b[H';
break; break;
case event.DOM_VK_ESCAPE: case "Escape":
ch = '\x1b'; ch = '\x1b';
break; case "Control":
default: break;
return true; default:
return true
break;
} }
} }
// Stop the event from doing anything else. // Stop the event from doing anything else.
@ -311,39 +326,39 @@ VT100.prototype.html_colours_ = function VT100_html_colours_(attr)
var fg, bg, co0, co1; var fg, bg, co0, co1;
fg = attr.fg; fg = attr.fg;
bg = attr.bg; bg = attr.bg;
switch (attr.mode & (VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD)) { switch (attr.mode & (VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD)) {
case 0: case 0:
case VT100.A_DIM | VT100.A_BOLD: case VT100.A_DIM | VT100.A_BOLD:
co0 = '00'; co0 = '00';
if (bg == VT100.COLOR_WHITE) if (bg == VT100.COLOR_WHITE)
co1 = 'ff'; co1 = 'ff';
else else
co1 = 'c0'; co1 = 'c0';
break; break;
case VT100.A_BOLD: case VT100.A_BOLD:
co0 = '00'; co1 = 'ff'; co0 = '00'; co1 = 'ff';
break; break;
case VT100.A_DIM: case VT100.A_DIM:
if (fg == VT100.COLOR_BLACK) if (fg == VT100.COLOR_BLACK)
co0 = '40'; co0 = '40';
else else
co0 = '00'; co0 = '00';
co1 = '40'; co1 = '40';
break; break;
case VT100.A_REVERSE: case VT100.A_REVERSE:
case VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD: case VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD:
co0 = 'c0'; co1 = '40'; co0 = 'c0'; co1 = 'ff';
break; break;
case VT100.A_REVERSE | VT100.A_BOLD: case VT100.A_REVERSE | VT100.A_BOLD:
co0 = 'c0'; co1 = '00'; co0 = 'c0'; co1 = '00';
break; break;
default: default:
if (fg == VT100.COLOR_BLACK) if (fg == VT100.COLOR_BLACK)
co0 = '80'; co0 = '80';
else else
co0 = 'c0'; co0 = 'c0';
co1 = 'c0'; co1 = 'c0';
} }
return { return {
f: '#' + (fg & 4 ? co1 : co0) + f: '#' + (fg & 4 ? co1 : co0) +
(fg & 2 ? co1 : co0) + (fg & 2 ? co1 : co0) +
@ -1283,6 +1298,52 @@ VT100.prototype.warn = function VT100_warn(message) {
} }
} }
VT100.prototype.throttleSmart = function throttleSmart(fn, wait) {
let timeout, lastArgs;
return (...args) => {
lastArgs = lastArgs || []
if (!timeout) {
fn(...args); timeout = setTimeout(() => { fn(...lastArgs); timeout = null; }, wait);
} else lastArgs = args;
};
}
VT100.prototype.setupTouchInputFallback = function(){
this.scr_.addEventListener('touchend', () => {
if( !this.input ){
this.form = document.createElement("form")
this.form.addEventListener("submit", (e) => {
e.preventDefault()
this.key_buf_.push('\n')
setTimeout(VT100.go_getch_, 0);
})
this.input = document.createElement("input")
this.input.setAttribute("cols", this.opts.cols )
this.input.setAttribute("rows", this.opts.rows )
this.input.style.opacity = '0'
this.input.style.position = 'absolute'
this.input.style.left = '-9999px'
this.form.appendChild(this.input)
this.scr_.parentElement.appendChild(this.form)
this.input.handler = () => {
let ch = this.input.value
// detect backspace
//if( e.inputType == 'deleteContentBackward' ) ch = '\b'
this.input.value = ''
if( !ch ) return
this.key_buf_.push(ch);
setTimeout(VT100.go_getch_, 0);
this.input.valueLast = this.input.value
}
this.input.addEventListener('input', this.input.handler )
}
setTimeout( () => this.input.focus(), 10 )
})
}
function dump(x) { function dump(x) {
// Do nothing // Do nothing
console.log(x)
} }

View File

@ -1,15 +1,31 @@
if( typeof emulator != 'undefined' ){ if( typeof emulator != 'undefined' ){
// inside worker-thread // inside worker-thread
importScripts("localforage.js") // we don't instance it again here (just use its functions)
this['emulator.restore_state'] = async function(data){ this['emulator.restore_state'] = async function(data){
await emulator.restore_state(data) return new Promise( (resolve,reject) => {
console.log("restored state") localforage.getItem("state", async (err,stateBase64) => {
this.postMessage({event:"state_restored",data:false}) if( stateBase64 && !err ){
state = ISOTerminal.prototype.convert.base64ToArrayBuffer( stateBase64 )
await emulator.restore_state(state)
console.log("restored state")
}else return reject("worker.js: emulator.restore_state (could not get state from localforage)")
resolve()
})
})
} }
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},[state]) localforage.setDriver([
localforage.INDEXEDDB,
localforage.WEBSQL,
localforage.LOCALSTORAGE
]).then( () => {
localforage.setItem("state", ISOTerminal.prototype.convert.arrayBufferToBase64(state) )
console.log("state saved")
})
console.dir(state)
} }
@ -30,30 +46,21 @@ if( typeof emulator != 'undefined' ){
localforage.getItem("state", async (err,stateBase64) => { localforage.getItem("state", async (err,stateBase64) => {
if( stateBase64 && !err && confirm('continue last session?') ){ if( stateBase64 && !err && confirm('continue last session?') ){
this.noboot = true // see feat/boot.js this.noboot = true // see feat/boot.js
state = this.convert.base64ToArrayBuffer( stateBase64 ) try{
await this.worker['emulator.restore_state']()
this.addEventListener('state_restored', function(){
// simulate / fastforward boot events // simulate / fastforward boot events
this.postBoot( () => { this.postBoot( () => {
this.send("l\n") this.send("l\n")
this.send("hook wakeup\n") this.send("hook wakeup\n")
}) })
}) }catch(e){ console.error(e) }
this.worker.postMessage({event:'emulator.restore_state',data:state})
} }
}) })
this.save = async () => { this.save = async () => {
const state = await this.worker.postMessage({event:"emulator.save_state",data:false}) await this.worker['emulator.save_state']()
} }
this.addEventListener('state_saved', function(e){
const state = e.detail
localforage.setItem("state", this.convert.arrayBufferToBase64(state) )
console.log("state saved")
})
window.addEventListener("beforeunload", function (e) { window.addEventListener("beforeunload", function (e) {
var confirmationMessage = "Sure you want to leave?\nTIP: enter 'save' to continue this session later"; var confirmationMessage = "Sure you want to leave?\nTIP: enter 'save' to continue this session later";
(e || window.event).returnValue = confirmationMessage; //Gecko + IE (e || window.event).returnValue = confirmationMessage; //Gecko + IE

View File

@ -4,7 +4,11 @@ ISOTerminal.addEventListener('ready', function(e){
ISOTerminal.prototype.boot = async function(e){ ISOTerminal.prototype.boot = async function(e){
// set environment // set environment
let env = ['export BROWSER=1'] let env = [
`export LINES=${this.opts.rows}`,
`export COLUMNS=${this.opts.cols}`,
'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]+'"') )