modified vt100 to capture keys correctly
/ test (push) Successful in 8s
Details
/ test (push) Successful in 8s
Details
This commit is contained in:
parent
10a3ff6688
commit
ce44449fc4
|
@ -35,24 +35,27 @@ if( typeof AFRAME != 'undefined '){
|
||||||
schema: {
|
schema: {
|
||||||
iso: { type:"string", "default":"https://forgejo.isvery.ninja/assets/xrsh-buildroot/main/xrsh.iso" },
|
iso: { type:"string", "default":"https://forgejo.isvery.ninja/assets/xrsh-buildroot/main/xrsh.iso" },
|
||||||
overlayfs: { type:"string"},
|
overlayfs: { type:"string"},
|
||||||
cols: { type: 'number',"default": 80 },
|
width: { type: 'number',"default": -1 },
|
||||||
rows: { type: 'number',"default": 20 },
|
height: { type: 'number',"default": -1 },
|
||||||
|
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},
|
||||||
maximized: { type: 'boolean',"default":false},
|
maximized: { type: 'boolean',"default":true},
|
||||||
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":false }, // use xterm.js? (=slower)
|
|
||||||
memory: { type: 'number', "default":64 }, // VM memory (in MB)
|
memory: { type: 'number', "default":64 }, // VM memory (in MB)
|
||||||
bufferLatency: { type: 'number', "default":300 }, // in ms: bufferlatency from webworker to xterm (batch-update every char to texture)
|
bufferLatency: { type: 'number', "default":30 }, // in ms: bufferlatency from webworker to xterm (batch-update every char to texture)
|
||||||
canvasLatency: { type: 'number', "default":500 }, // in ms: time between canvas re-draws
|
|
||||||
renderer: { type: 'string', "default":"canvas" },// 'dom' or 'canvas' (=faster) for immersive mode
|
|
||||||
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.initHud()
|
this.initHud()
|
||||||
fetch(this.data.iso,{method: 'HEAD'})
|
fetch(this.data.iso,{method: 'HEAD'})
|
||||||
.then( (res) => {
|
.then( (res) => {
|
||||||
|
@ -70,6 +73,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
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",
|
||||||
|
vt100: "com/isoterminal/VT100.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",
|
||||||
|
@ -85,8 +89,9 @@ if( typeof AFRAME != 'undefined '){
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
events: ['click','keydown'],
|
events: ['click','keydown'],
|
||||||
html: (me) => `<div class="isoterminal">
|
html: (me) => `<div class="isoterminal">
|
||||||
<input type="text" style="opacity:0; position:absolute; width:200px; height:200px;"/>
|
<div id="vt100" tabindex="0">
|
||||||
<pre></pre>
|
<pre></pre>
|
||||||
|
</div>
|
||||||
</div>`,
|
</div>`,
|
||||||
|
|
||||||
css: (me) => `.isoterminal{
|
css: (me) => `.isoterminal{
|
||||||
|
@ -98,7 +103,10 @@ if( typeof AFRAME != 'undefined '){
|
||||||
.isoterminal div{
|
.isoterminal div{
|
||||||
display:block;
|
display:block;
|
||||||
position:relative;
|
position:relative;
|
||||||
line-height:18px;
|
line-height: ${me.com.data.lineHeight}px;
|
||||||
|
}
|
||||||
|
#vt100 {
|
||||||
|
outline: none !important;
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Cousine';
|
font-family: 'Cousine';
|
||||||
|
@ -115,6 +123,15 @@ if( typeof AFRAME != 'undefined '){
|
||||||
|
|
||||||
.isoterminal style{ display:none }
|
.isoterminal style{ display:none }
|
||||||
|
|
||||||
|
blink{
|
||||||
|
border: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;
|
||||||
box-shadow:none;
|
box-shadow:none;
|
||||||
|
@ -132,29 +149,12 @@ if( typeof AFRAME != 'undefined '){
|
||||||
.XR .isoterminal{
|
.XR .isoterminal{
|
||||||
background: #000;
|
background: #000;
|
||||||
}
|
}
|
||||||
.isoterminal *,
|
.isoterminal *{
|
||||||
.isoterminal .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;
|
||||||
text-shadow: 0px 0px 10px #F075;
|
text-shadow: 0px 0px 10px #F075;
|
||||||
}
|
}
|
||||||
.isoterminal .xterm-rows.xterm-focus .xterm-cursor.xterm-cursor-block {
|
|
||||||
background-color:#a5F !important;
|
|
||||||
}
|
|
||||||
.isoterminal .xterm-rows div{
|
|
||||||
height:8px;
|
|
||||||
height:18px;
|
|
||||||
}
|
|
||||||
.isoterminal .xterm-rows span{
|
|
||||||
width:8px;
|
|
||||||
}
|
|
||||||
.isoterminal .xterm-helpers {
|
|
||||||
position:absolute;
|
|
||||||
opacity:0;
|
|
||||||
top: -2000px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fade {
|
@keyframes fade {
|
||||||
from { opacity: 1.0; }
|
from { opacity: 1.0; }
|
||||||
|
@ -168,7 +168,6 @@ if( typeof AFRAME != 'undefined '){
|
||||||
to { opacity: 1.0; }
|
to { opacity: 1.0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.isoterminal .xterm-rows.xterm-focus .xterm-cursor.xterm-cursor-block,
|
|
||||||
.blink{
|
.blink{
|
||||||
animation:fade 1000ms infinite;
|
animation:fade 1000ms infinite;
|
||||||
-webkit-animation:fade 1000ms infinite;
|
-webkit-animation:fade 1000ms infinite;
|
||||||
|
@ -179,8 +178,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
|
|
||||||
initTerminal: async function(singleton){
|
initTerminal: async function(singleton){
|
||||||
|
|
||||||
if( this.data.xterm ){
|
// why not latest xterm or v3.12 with builtin-canvas support?
|
||||||
// why 3.12?
|
|
||||||
// first versions used 1.5.4, a typescript rewrite which:
|
// first versions used 1.5.4, a typescript rewrite which:
|
||||||
// * acts weird with oculus browser keyboard (does not repaint properly after typing)
|
// * acts weird with oculus browser keyboard (does not repaint properly after typing)
|
||||||
// * does not use canvas anymore [which would be ideal for THREE.js texture]
|
// * does not use canvas anymore [which would be ideal for THREE.js texture]
|
||||||
|
@ -188,12 +186,7 @@ if( typeof AFRAME != 'undefined '){
|
||||||
// * only allows a standalone WebGL addon (conflicts with THREE)
|
// * only allows a standalone WebGL addon (conflicts with THREE)
|
||||||
// * heavily dependent on requestAnimationFrame (conflicts with THREE)
|
// * heavily dependent on requestAnimationFrame (conflicts with THREE)
|
||||||
// * typescript-rewrite results in ~300k lib (instead of 96k)
|
// * typescript-rewrite results in ~300k lib (instead of 96k)
|
||||||
this.requires.xtermcss = "//unpkg.com/xterm@3.12.0/dist/xterm.css",
|
// * v3.12 had slightly better performance but still very heavy
|
||||||
this.requires.xtermjs = "//unpkg.com/xterm@3.12.0/dist/xterm.js",
|
|
||||||
this.requires.xtermcss = "com/xterm.js"
|
|
||||||
}else{
|
|
||||||
this.requires.vt100 = "com/isoterminal/VT100.js"
|
|
||||||
}
|
|
||||||
|
|
||||||
await AFRAME.utils.require(this.requires)
|
await AFRAME.utils.require(this.requires)
|
||||||
await AFRAME.utils.require({ // ISOTerminal plugins
|
await AFRAME.utils.require({ // ISOTerminal plugins
|
||||||
|
@ -225,18 +218,15 @@ if( typeof AFRAME != 'undefined '){
|
||||||
this.term = new ISOTerminal(instance,this.data)
|
this.term = new ISOTerminal(instance,this.data)
|
||||||
|
|
||||||
instance.addEventListener('DOMready', () => {
|
instance.addEventListener('DOMready', () => {
|
||||||
if( this.data.renderer == 'dom' || !this.data.xterm ){
|
|
||||||
instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}; faceuser: true`)
|
instance.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}; faceuser: true`)
|
||||||
}
|
setTimeout( () => this.setupVT100(instance),100)
|
||||||
if( !this.data.xterm ) this.setupVT100(instance)
|
|
||||||
//instance.winbox.resize(720,380)
|
//instance.winbox.resize(720,380)
|
||||||
let size = `width: ${Math.floor(this.data.cols*8.65)}; height: ${Math.floor(this.data.rows*21.1)}`
|
let size = `width: ${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}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
instance.addEventListener('window.oncreate', (e) => {
|
instance.addEventListener('window.oncreate', (e) => {
|
||||||
instance.dom.classList.add('blink')
|
instance.dom.classList.add('blink')
|
||||||
if( this.data.xterm ) this.setupXterm(instance)
|
|
||||||
|
|
||||||
// run iso
|
// run iso
|
||||||
let opts = {dom:instance.dom}
|
let opts = {dom:instance.dom}
|
||||||
|
@ -246,15 +236,6 @@ if( typeof AFRAME != 'undefined '){
|
||||||
|
|
||||||
instance.setAttribute("dom", "")
|
instance.setAttribute("dom", "")
|
||||||
|
|
||||||
this.term.addEventListener('postReady', (e)=>{
|
|
||||||
// bugfix: send window dimensions to xterm (xterm.js does that from dom-sizechange to xterm via escape codes)
|
|
||||||
let wb = instance.winbox
|
|
||||||
if( this.data.maximized ){
|
|
||||||
wb.restore()
|
|
||||||
wb.maximize()
|
|
||||||
}else wb.resize()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.term.addEventListener('ready', (e) => {
|
this.term.addEventListener('ready', (e) => {
|
||||||
instance.dom.classList.remove('blink')
|
instance.dom.classList.remove('blink')
|
||||||
this.term.emit('status',"running")
|
this.term.emit('status',"running")
|
||||||
|
@ -278,9 +259,6 @@ if( typeof AFRAME != 'undefined '){
|
||||||
instance.addEventListener('window.onmaximize', resize )
|
instance.addEventListener('window.onmaximize', resize )
|
||||||
|
|
||||||
const focus = (showdom) => (e) => {
|
const focus = (showdom) => (e) => {
|
||||||
if( this.el.components.xterm ){
|
|
||||||
this.el.components.xterm.term.focus()
|
|
||||||
}
|
|
||||||
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 )
|
||||||
}
|
}
|
||||||
|
@ -311,14 +289,22 @@ if( typeof AFRAME != 'undefined '){
|
||||||
console.test.run()
|
console.test.run()
|
||||||
},
|
},
|
||||||
|
|
||||||
setupXterm: function(){
|
|
||||||
instance.setAttribute("xterm",`cols: ${this.data.cols}; rows: ${this.data.rows}; canvasLatency: ${this.data.canvasLatency}; XRrenderer: ${this.data.renderer}`)
|
|
||||||
instance.addEventListener("xterm-input", (e) => this.term.send(e.detail,0) )
|
|
||||||
},
|
|
||||||
|
|
||||||
setupVT100: function(instance){
|
setupVT100: function(instance){
|
||||||
this.vt100 = new VT100(100,50, this.el.dom, 100 )
|
const el = this.el.dom.querySelector('#vt100')
|
||||||
|
this.vt100 = new VT100(
|
||||||
|
Math.floor(this.data.width/this.data.lineHeight),
|
||||||
|
Math.floor(this.data.height*0.8/this.data.lineHeight),
|
||||||
|
el,
|
||||||
|
100
|
||||||
|
)
|
||||||
this.vt100.curs_set( 1, true)
|
this.vt100.curs_set( 1, true)
|
||||||
|
el.focus()
|
||||||
|
this.vt100.getch( (ch,t) => {
|
||||||
|
console.log(ch)
|
||||||
|
this.term.send( ch )
|
||||||
|
this.vt100.curs_set( 0, true)
|
||||||
|
})
|
||||||
|
|
||||||
this.el.addEventListener('serial-output-byte', (e) => {
|
this.el.addEventListener('serial-output-byte', (e) => {
|
||||||
const byte = e.detail
|
const byte = e.detail
|
||||||
var chr = String.fromCharCode(byte);
|
var chr = String.fromCharCode(byte);
|
||||||
|
@ -327,6 +313,14 @@ if( typeof AFRAME != 'undefined '){
|
||||||
this.el.addEventListener('serial-output-string', (e) => {
|
this.el.addEventListener('serial-output-string', (e) => {
|
||||||
this.vt100.write(e.detail)
|
this.vt100.write(e.detail)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
//this.el.dom.querySelector('input').addEventListener('keyup', (e) => {
|
||||||
|
// VT100.handle_onkeypress_( {charCode : e.charCode || e.keyCode, keyCode: e.keyCode}, (chars) => {
|
||||||
|
// debugger
|
||||||
|
// chars.map( (c) => this.term.send(str) )
|
||||||
|
// })
|
||||||
|
//})
|
||||||
},
|
},
|
||||||
|
|
||||||
events:{
|
events:{
|
||||||
|
|
|
@ -86,7 +86,7 @@ function VT100(wd, ht, el_or_id, max_scroll_lines, fg, bg)
|
||||||
fg = VT100.COLOR_WHITE;
|
fg = VT100.COLOR_WHITE;
|
||||||
}
|
}
|
||||||
if (typeof(bg) == 'undefined') {
|
if (typeof(bg) == 'undefined') {
|
||||||
bg = VT100.COLOR_BLACK;
|
bg = VT100.COLOR_TRANSPARENT //COLOR_BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
var r;
|
var r;
|
||||||
|
@ -116,13 +116,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.WARN;
|
this.log_level_ = VT100.DEBUG //WARN;
|
||||||
|
|
||||||
this.clear_all();
|
this.clear_all();
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
// public constants -- colours and colour pairs
|
// public constants -- colours and colour pairs
|
||||||
|
VT100.COLOR_TRANSPARENT = -1;
|
||||||
VT100.COLOR_BLACK = 0;
|
VT100.COLOR_BLACK = 0;
|
||||||
VT100.COLOR_BLUE = 1;
|
VT100.COLOR_BLUE = 1;
|
||||||
VT100.COLOR_GREEN = 2;
|
VT100.COLOR_GREEN = 2;
|
||||||
|
@ -164,21 +165,21 @@ VT100.the_vt_ = undefined;
|
||||||
// class methods
|
// class methods
|
||||||
|
|
||||||
// this is actually an event handler
|
// this is actually an event handler
|
||||||
VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event)
|
VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event,cb)
|
||||||
{
|
{
|
||||||
//dump("event target: " + event.target.id + "\n");
|
//dump("event target: " + event.target.id + "\n");
|
||||||
//dump("event originalTarget: " + event.originalTarget.id + "\n");
|
//dump("event originalTarget: " + event.originalTarget.id + "\n");
|
||||||
var vt = VT100.the_vt_, ch;
|
var vt = VT100.the_vt_, ch;
|
||||||
if (vt === undefined)
|
if (vt === undefined)
|
||||||
return true;
|
return true;
|
||||||
if (VT100.browser_ie_ || VT100.browser_opera_) {
|
//if ( event.keyCode != undefined || !event.charCode){
|
||||||
ch = event.keyCode;
|
// ch = event.keyCode;
|
||||||
if (ch == 13)
|
// if (ch == 13)
|
||||||
ch = 10;
|
// ch = 10;
|
||||||
else if (ch > 255 || (ch < 32 && ch != 8))
|
// else if (ch > 255 || (ch < 32 && ch != 8))
|
||||||
return true;
|
// return true;
|
||||||
ch = String.fromCharCode(ch);
|
// ch = String.fromCharCode(ch);
|
||||||
} else {
|
//} else {
|
||||||
ch = event.charCode;
|
ch = event.charCode;
|
||||||
//dump("ch: " + ch + "\n");
|
//dump("ch: " + ch + "\n");
|
||||||
//dump("ctrl?: " + event.ctrlKey + "\n");
|
//dump("ctrl?: " + event.ctrlKey + "\n");
|
||||||
|
@ -201,11 +202,11 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event)
|
||||||
ch = '\n';
|
ch = '\n';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (event.keyCode) {
|
switch (event.key) {
|
||||||
case event.DOM_VK_BACK_SPACE:
|
case "Backspace":
|
||||||
ch = '\b';
|
ch = '\b';
|
||||||
break;
|
break;
|
||||||
case event.DOM_VK_TAB:
|
case "Tab":
|
||||||
ch = '\t';
|
ch = '\t';
|
||||||
break;
|
break;
|
||||||
case event.DOM_VK_RETURN:
|
case event.DOM_VK_RETURN:
|
||||||
|
@ -251,9 +252,11 @@ VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event)
|
||||||
}
|
}
|
||||||
// Stop the event from doing anything else.
|
// Stop the event from doing anything else.
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
|
||||||
vt.key_buf_.push(ch);
|
vt.key_buf_.push(ch);
|
||||||
setTimeout(VT100.go_getch_, 0);
|
if( cb ){
|
||||||
|
cb(vt.key_buf_)
|
||||||
|
vt.key_buf_ = []
|
||||||
|
}else setTimeout(VT100.go_getch_, 0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +348,7 @@ VT100.prototype.html_colours_ = function VT100_html_colours_(attr)
|
||||||
f: '#' + (fg & 4 ? co1 : co0) +
|
f: '#' + (fg & 4 ? co1 : co0) +
|
||||||
(fg & 2 ? co1 : co0) +
|
(fg & 2 ? co1 : co0) +
|
||||||
(fg & 1 ? co1 : co0),
|
(fg & 1 ? co1 : co0),
|
||||||
b: '#' + (bg & 4 ? co1 : co0) +
|
b: attr.bg == VT100.COLOR_TRANSPARENT ? 'transparent' : '#' + (bg & 4 ? co1 : co0) +
|
||||||
(bg & 2 ? co1 : co0) +
|
(bg & 2 ? co1 : co0) +
|
||||||
(bg & 1 ? co1 : co0)
|
(bg & 1 ? co1 : co0)
|
||||||
};
|
};
|
||||||
|
@ -545,12 +548,10 @@ VT100.prototype.curs_set = function(vis, grab, eventist)
|
||||||
this.grab_events_ = true;
|
this.grab_events_ = true;
|
||||||
VT100.the_vt_ = this;
|
VT100.the_vt_ = this;
|
||||||
eventist.addEventListener("keypress", VT100.handle_onkeypress_, false);
|
eventist.addEventListener("keypress", VT100.handle_onkeypress_, false);
|
||||||
if (VT100.browser_ie_)
|
eventist.addEventListener("keydown", VT100.handle_onkeypress_, false);
|
||||||
document.onkeydown = VT100.handle_onkeydown_;
|
|
||||||
} else {
|
} else {
|
||||||
eventist.removeEventListener("keypress", VT100.handle_onkeypress_, false);
|
eventist.removeEventListener("keypress", VT100.handle_onkeypress_, false);
|
||||||
if (VT100.browser_ie_)
|
eventist.removeEventListener("keydown", VT100.handle_onkeypress_, false);
|
||||||
document.onkeydown = VT100.handle_onkeydown_;
|
|
||||||
this.grab_events_ = false;
|
this.grab_events_ = false;
|
||||||
VT100.the_vt_ = undefined;
|
VT100.the_vt_ = undefined;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue