xterm repaint improvements (still not great)
/ mirror_to_github (push) Successful in 19s Details
/ test (push) Successful in 5s Details

This commit is contained in:
Leon van Kammen 2024-09-23 20:31:28 +00:00
parent 44f79ac02a
commit 9d69ef9c57
5 changed files with 28 additions and 142 deletions

View File

@ -59,7 +59,6 @@ if( !AFRAME.components.dom ){
.assignUniqueID() .assignUniqueID()
.scaleDOMvsXR() .scaleDOMvsXR()
.triggerKeyboardForInputs() .triggerKeyboardForInputs()
.stubRequestAnimationFrame()
document.querySelector('#overlay').appendChild(this.el.dom) document.querySelector('#overlay').appendChild(this.el.dom)
this.el.emit('DOMready',{el: this.el.dom}) this.el.emit('DOMready',{el: this.el.dom})
@ -131,10 +130,5 @@ if( !AFRAME.components.dom ){
return this return this
}, },
stubRequestAnimationFrame: async function(){
let s = await AFRAME.utils.require(this.requires)
this.el.setAttribute("requestAnimationFrameXR","")
}
}) })
} }

View File

@ -38,13 +38,13 @@ if( typeof AFRAME != 'undefined '){
rows: { type: 'number',"default": 30 }, rows: { type: 'number',"default": 30 },
padding: { type: 'number',"default": 18 }, padding: { type: 'number',"default": 18 },
minimized: { type: 'boolean',"default":false}, minimized: { type: 'boolean',"default":false},
maximized: { type: 'boolean',"default":true}, maximized: { type: 'boolean',"default":false},
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":48 } // VM memory (in MB) memory: { type: 'number', "default":48 } // VM memory (in MB)
}, },
init: async function(){ init: function(){
this.el.object3D.visible = false this.el.object3D.visible = false
fetch(this.data.iso,{method: 'HEAD'}) fetch(this.data.iso,{method: 'HEAD'})
.then( (res) => { .then( (res) => {
@ -125,6 +125,7 @@ if( typeof AFRAME != 'undefined '){
.wb-body:has(> .isoterminal){ .wb-body:has(> .isoterminal){
background: #000C; background: #000C;
overflow:hidden; overflow:hidden;
border-radius:7px;
} }
.XR .wb-body:has(> .isoterminal){ .XR .wb-body:has(> .isoterminal){
@ -159,6 +160,7 @@ if( typeof AFRAME != 'undefined '){
this.requires.xtermjs = "https://unpkg.com/@xterm/xterm@5.5.0/lib/xterm.js" this.requires.xtermjs = "https://unpkg.com/@xterm/xterm@5.5.0/lib/xterm.js"
this.requires.xtermcss = "https://unpkg.com/@xterm/xterm@5.5.0/css/xterm.css" this.requires.xtermcss = "https://unpkg.com/@xterm/xterm@5.5.0/css/xterm.css"
this.requires.xterm = "com/isoterminal/feat/xterm.js" this.requires.xterm = "com/isoterminal/feat/xterm.js"
// xterm relies on window.requestAnimationFrame which is not called in XR (xrSession.requestAnimationFrame is)
} }
let s = await AFRAME.utils.require(this.requires) let s = await AFRAME.utils.require(this.requires)
@ -199,7 +201,6 @@ if( typeof AFRAME != 'undefined '){
instance.setAttribute("dom", "") instance.setAttribute("dom", "")
this.isoterminal.addEventListener('postReady', (e)=>{ this.isoterminal.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
@ -246,7 +247,6 @@ if( typeof AFRAME != 'undefined '){
instance.object3D.quaternion.copy( AFRAME.scenes[0].camera.quaternion ) // face towards camera instance.object3D.quaternion.copy( AFRAME.scenes[0].camera.quaternion ) // face towards camera
}, },
events:{ events:{
// combined AFRAME+DOM reactive events // combined AFRAME+DOM reactive events

View File

@ -25,9 +25,30 @@ ISOTerminal.prototype.xtermInit = function(){
term.select(0, 0, 0) term.select(0, 0, 0)
isoterm.emit('status','copied to clipboard') isoterm.emit('status','copied to clipboard')
}) })
term.onRender( () => {
console.log("render")
// xterm relies on requestAnimationFrame (which does not called in immersive mode)
const _window = term._core._coreBrowserService._window
const requestAnimationFrame = _window.requestAnimationFrame
// luckily xterm allows a swappable window object
let newWindow = function(){}.bind(window)
for( var i in window ) newWindow[i] = window[i]
newWindow.requestAnimationFrame = (cb) => {
if( term.tid != null ) return
setTimeout( () => {
cb()
term.tid = null
},200)
}
term._core._coreBrowserService._window = newWindow
})
return term return term
} }
this.addEventListener('emulator-started', function(){ this.addEventListener('emulator-started', function(){
this.emulator.serial_adapter.term.element.querySelector('.xterm-viewport').style.background = 'transparent' this.emulator.serial_adapter.term.element.querySelector('.xterm-viewport').style.background = 'transparent'
// toggle immersive with ESCAPE // toggle immersive with ESCAPE
@ -36,7 +57,9 @@ ISOTerminal.prototype.xtermInit = function(){
const resize = (w,h) => { const resize = (w,h) => {
setTimeout( () => { setTimeout( () => {
if( isoterm?.emulator?.serial_adapter?.term ){
isoterm.xtermAutoResize(isoterm.emulator.serial_adapter.term, isoterm.instance,-3) isoterm.xtermAutoResize(isoterm.emulator.serial_adapter.term, isoterm.instance,-3)
}
},800) // wait for resize anim },800) // wait for resize anim
} }
isoterm.instance.addEventListener('window.onresize', resize ) isoterm.instance.addEventListener('window.onresize', resize )

View File

@ -1,50 +0,0 @@
/*
* ## requestAnimationFrameXR
*
* reroutes requestAnimationFrame-calls to xrSession.requestAnimationFrame
* reason: in immersive mode this function behaves differently
* (causing HTML apps like xterm.js not getting updated due to relying
* on window.requestAnimationFrame)
*
* ```html
* <a-entity requestAnimationFrameXR dom/>
* ```
*/
if( !AFRAME.systems.requestAnimationFrameXR ){
AFRAME.registerSystem('requestAnimationFrameXR',{
init: function init(){
if( document.location.hostname.match(/localhost/) ) return // allow webxr polyfill during development (they hang in XR)
AFRAME.systems.requestAnimationFrameXR.q = []
this.sceneEl.addEventListener('enter-vr', this.enable )
this.sceneEl.addEventListener('enter-ar', this.enable )
this.sceneEl.addEventListener('exit-vr', this.disable )
this.sceneEl.addEventListener('exit-ar', this.disable )
},
enable: function enable(){
this.requestAnimationFrame = window.requestAnimationFrame
// NOTE: we don't call xrSession.requestAnimationFrame directly like this:
//
// window.requestAnimationFrame = AFRAME.utils.throttleTick( (cb) => this.sceneEl.xrSession.requestAnimationFrame(cb), 50 )
//
// as that breaks webxr polyfill (for in-browser testing)
// instead we defer calls to tick() (which is called both in XR and non-XR)
//
window.requestAnimationFrame = (cb) => AFRAME.systems.requestAnimationFrameXR.q.push(cb)
const q = AFRAME.systems.requestAnimationFrameXR.q
this.tick = AFRAME.utils.throttleTick( () => {
while( q.length != 0 ) (q.pop())()
},50)
},
disable: function disable(){
delete this.tick
window.requestAnimationFrame = this.requestAnimationFrame
}
})
}

View File

@ -1,81 +0,0 @@
AFRAME.registerComponent('xrfragments', {
schema: {
url: { type:"string"}
},
init: function () {
},
events:{
launcher: async function(){
let url = prompt('enter URL to glb/fbx/json/obj/usdz asset', 'https://xrfragment.org/index.glb')
if( !url ) return
await AFRAME.utils.require({
xrfragments: "https://xrfragment.org/dist/xrfragment.aframe.js",
})
// remove objects which are marked to be removed from scene (with noxrf)
let els = [...document.querySelectorAll('[noxrf]') ]
els.map( (el) => el.remove() )
if( !this.el.getAttribute("xrf") ){
this.el.setAttribute("xrf", url )
let ARbutton = document.querySelector('.a-enter-ar-button')
if( ARbutton ){
ARbutton.addEventListener('click', () => {
AFRAME.XRF.reset()
})
}
}else AFRAME.XRF.navigator.to(url)
}
},
manifest: { // HTML5 manifest to identify app to xrsh
"short_name": "XRF",
"name": "XR Fragment URL",
"icons": [ ],
"id": "/?source=pwa",
"start_url": "/?source=pwa",
"background_color": "#3367D6",
"display": "standalone",
"scope": "/",
"theme_color": "#3367D6",
"shortcuts": [
{
"name": "What is the latest news?",
"cli":{
"usage": "helloworld <type> [options]",
"example": "helloworld news",
"args":{
"--latest": {type:"string"}
}
},
"short_name": "Today",
"description": "View weather information for today",
"url": "/today?source=pwa",
"icons": [{ "src": "/images/today.png", "sizes": "192x192" }]
}
],
"description": "Hello world information",
"screenshots": [
{
"src": "/images/screenshot1.png",
"type": "image/png",
"sizes": "540x720",
"form_factor": "narrow"
}
],
"help":`
Helloworld application
This is a help file which describes the application.
It will be rendered thru troika text, and will contain
headers based on non-punctualized lines separated by linebreaks,
in above's case "\nHelloworld application\n" will qualify as header.
`
}
});