diff --git a/com/codemirror.js b/com/codemirror.js new file mode 100644 index 0000000..0d36726 --- /dev/null +++ b/com/codemirror.js @@ -0,0 +1,120 @@ +AFRAME.registerComponent('codemirror', { + schema: { + foo: { type:"string"} + }, + + init: function () { + this.el.object3D.visible = false + //this.el.innerHTML = ` ` + this.requireAll() + }, + + requireAll: async function(){ + let s = await AFRAME.utils.require(this.requires) + setTimeout( () => this.el.setAttribute("dom",""), 300 ) + }, + + requires:{ + window: "com/window.js", + htmltexture: "com/html-as-texture-in-xr.js", + codemirror: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/codemirror.js", + codemirrorcss: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.35.0/codemirror.css", + cmtheme: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.35.0/theme/shadowfox.css" + }, + + dom: { + scale: 0.5, + events: ['click','keydown'], + html: (me) => `
+
`, + + css: (me) => `.codemirror{ + width:100%; + } + .wb-body + .codemirror{ overflow:hidden; } + .CodeMirror { + margin-top:18px; + } + .cm-s-shadowfox.CodeMirror { + background:transparent !important; + } + ` + }, + + events:{ + + // component events + DOMready: function(e){ + console.log(`title: codemirror; uid: ${this.el.dom.id}; attach: #overlay; dom: #${this.el.dom.id};`) + this.el.setAttribute("window", `title: codemirror; uid: ${this.el.dom.id}; attach: #overlay; dom: #${this.el.dom.id};`) + this.el.setAttribute("html-as-texture-in-xr", `domid: #${this.el.dom.id}`) // only show aframe-html in xr + this.editor = CodeMirror( this.el.dom, { + value: "function myScript(){return 100;}\n", + mode: "javascript", + lineNumbers: true, + styleActiveLine: true, + matchBrackets: true, + Tab: "indentMore", + defaultTab: function(cm) { + if (cm.somethingSelected()) cm.indentSelection("add"); + else cm.replaceSelection(" ", "end"); + } + }) + this.editor.setOption("theme", "shadowfox") + }, + }, + + + manifest: { // HTML5 manifest to identify app to xrsh + "short_name": "Paste", + "name": "Paste", + "icons": [ + { + "src": "https://css.gg/clipboard.svg", + "type": "image/svg+xml", + "sizes": "512x512" + } + ], + "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 [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": "Paste the clipboard", + "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. + ` + } + +}); + diff --git a/com/isoterminal.js b/com/isoterminal.js index c943522..77dd088 100644 --- a/com/isoterminal.js +++ b/com/isoterminal.js @@ -1,7 +1,9 @@ -function ISOTerminal(){ +function ISOTerminal(instance,opts){ // create a neutral isoterminal object which can be decorated // with prototype functions and has addListener() and dispatchEvent() - let obj = new EventTarget() + let obj = new EventTarget() + obj.instance = instance + obj.opts = opts // 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] ) @@ -30,12 +32,16 @@ if( typeof AFRAME != 'undefined '){ AFRAME.registerComponent('isoterminal', { schema: { - iso: { type:"string", "default":"com/isoterminal/images/buildroot-bzimage.bin" }, - cols: { type: 'number',"default": 120 }, - rows: { type: 'number',"default": 30 }, - padding:{ type: 'number',"default": 18 }, - transparent: { type:'boolean', "default":false } // need good gpu - }, + iso: { type:"string", "default":"com/isoterminal/images/buildroot-bzimage.bin" }, + overlayfs: { type:"string"}, + cols: { type: 'number',"default": 120 }, + rows: { type: 'number',"default": 30 }, + padding: { type: 'number',"default": 18 }, + maximized: { type: 'boolean',"default":true}, + transparent: { type:'boolean', "default":false }, // need good gpu + xterm: { type: 'boolean', "default":true }, // use xterm.js (slower) + memory: { type: 'number', "default":32 } // VM memory (in MB) + }, init: async function(){ this.el.object3D.visible = false @@ -45,8 +51,6 @@ if( typeof AFRAME != 'undefined '){ requires:{ com: "com/dom.js", window: "com/window.js", - xtermjs: "https://unpkg.com/@xterm/xterm@5.5.0/lib/xterm.js", - xtermcss: "https://unpkg.com/@xterm/xterm@5.5.0/css/xterm.css", v86: "com/isoterminal/libv86.js", // allow xrsh to selfcontain scene + itself xhook: "https://jpillora.com/xhook/dist/xhook.min.js", @@ -57,16 +61,20 @@ if( typeof AFRAME != 'undefined '){ core: "com/isoterminal/core.js", utils_9p: "com/isoterminal/feat/9pfs_utils.js", boot: "com/isoterminal/feat/boot.js", - xterm: "com/isoterminal/feat/xterm.js", jsconsole: "com/isoterminal/feat/jsconsole.js", javascript: "com/isoterminal/feat/javascript.js", - index: "com/isoterminal/feat/index.html.js", + indexhtml: "com/isoterminal/feat/index.html.js", + indexjs: "com/isoterminal/feat/index.js.js", }, dom: { scale: 0.5, events: ['click','keydown'], - html: (me) => `
`, + html: (me) => `
+
+ +
+
`, css: (me) => `.isoterminal{ padding: ${me.com.data.padding}px; @@ -77,7 +85,9 @@ if( typeof AFRAME != 'undefined '){ white-space: pre; font-size: 14px; font-family: Liberation Mono,DejaVu Sans Mono,Courier New,monospace; - font-weight:700; + font-weight:900 !important; + letter-spacing: 0 !important; + line-height:16px; display:inline; overflow: hidden; } @@ -85,7 +95,7 @@ if( typeof AFRAME != 'undefined '){ .isoterminal style{ display:none } .wb-body:has(> .isoterminal){ - background: #000F; + background: #000C; overflow:hidden; } @@ -113,6 +123,12 @@ if( typeof AFRAME != 'undefined '){ initTerminal: async function(singleton){ + if( this.data.xterm ){ + 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.xterm = "com/isoterminal/feat/xterm.js" + } + let s = await AFRAME.utils.require(this.requires) this.el.setAttribute("selfcontainer","") @@ -132,10 +148,13 @@ if( typeof AFRAME != 'undefined '){ } // init isoterminal - this.isoterminal = new ISOTerminal() + this.isoterminal = new ISOTerminal(instance,this.data) instance.addEventListener('DOMready', () => { - instance.setAttribute("window", `title: xrsh [booting linux iso..]; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}`) + //instance.winbox.resize(720,380) + let size = this.data.xterm ? 'width: 1024px; height:600px' + : 'width: 720px; height:455px' + instance.setAttribute("window", `title: xrsh.iso; uid: ${instance.uid}; attach: #overlay; dom: #${instance.dom.id}; ${size}`) }) instance.addEventListener('window.oncreate', (e) => { @@ -146,9 +165,18 @@ if( typeof AFRAME != 'undefined '){ this.isoterminal.runISO(opts) }) - this.isoterminal.addEventListener('ready', function(e){ + instance.setAttribute("dom", "") + + + this.isoterminal.addEventListener('postReady', (e)=>{ + // bugfix: send window dimensions to xterm (xterm.js does that from dom-sizechange to xterm via escape codes) + if( this.data.maximized ) instance.winbox.maximize() + else instance.winbox.resize() + }) + + this.isoterminal.addEventListener('ready', (e)=>{ instance.dom.classList.remove('blink') - instance.winbox.maximize() + 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) @@ -159,34 +187,26 @@ if( typeof AFRAME != 'undefined '){ const w = instance.winbox if(!w) return w.titleBak = w.titleBak || w.title - instance.winbox.setTitle( `${w.titleBak} [${msg}]` ) + w.setTitle( `${w.titleBak} [${msg}]` ) }) instance.addEventListener('window.onclose', (e) => { if( !confirm('do you want to kill this virtual machine and all its processes?') ) e.halt = true }) - const resize = (w,h) => { - if( this.isoterminal.emulator && this.isoterminal.emulator.serial_adapter ){ - setTimeout( () => { - this.isoterminal.xtermAutoResize(this.isoterminal.emulator.serial_adapter.term,instance,-5) - },800) // wait for resize anim - } - } + const resize = (w,h) => { } instance.addEventListener('window.onresize', resize ) instance.addEventListener('window.onmaximize', resize ) - instance.setAttribute("dom", "") - - const focus = () => { + const focus = (e) => { if( this.isoterminal?.emulator?.serial_adapter?.focus ){ this.isoterminal.emulator.serial_adapter.term.focus() } } - instance.addEventListener('obbcollisionstarted', focus ) + //instance.addEventListener('obbcollisionstarted', focus ) this.el.sceneEl.addEventListener('enter-vr', focus ) this.el.sceneEl.addEventListener('enter-ar', focus ) - + instance.object3D.quaternion.copy( AFRAME.scenes[0].camera.quaternion ) // face towards camera }, diff --git a/com/isoterminal/core.js b/com/isoterminal/core.js index 1b3e97e..790be71 100644 --- a/com/isoterminal/core.js +++ b/com/isoterminal/core.js @@ -2,8 +2,23 @@ // // exec(['lua'] "print \"hello\") ---> cat /dev/browser/js/stdin | lua > /dev/browser/js/stdout //} -ISOTerminal.prototype.send = function(ttyNr, str){ - this.toUint8Array( str ).map( (c) => this.emulator.bus.send(`serial${ttyNr}-input`, c ) ) +ISOTerminal.prototype.serial_input = undefined; // can be set to 0,1,2,3 to define stdinput tty (xterm plugin) + +ISOTerminal.prototype.exec = function(shellscript){ + //let ts = String(Date.now())+".job" + //this.emulator.create_file(ts, this.toUint8Array(shellscript) ) + this.send(shellscript+"\n",1) +} + +ISOTerminal.prototype.send = function(str, ttyNr){ + if( !ttyNr ) ttyNr = this.serial_input + if( !ttyNr ){ + if( this.emulator.serial_adapter ){ + this.emulator.serial_adapter.term.paste(str) + }else this.emulator.keyboard_send_text(str) // vga screen + }else{ + this.toUint8Array( str ).map( (c) => this.emulator.bus.send(`serial${ttyNr}-input`, c ) ) + } } ISOTerminal.prototype.toUint8Array = function(str) { @@ -19,28 +34,31 @@ ISOTerminal.prototype.toUint8Array = function(str) { }, ISOTerminal.prototype.runISO = function(opts){ - this.opts = opts + + let me = this + this.opts = {...this.opts, ...opts} let image = {} if( opts.iso.match(/\.iso$/) ) image.cdrom = { url: opts.iso } if( opts.iso.match(/\.bin$/) ) image.bzimage = { url: opts.iso } - let emulator = this.emulator = new V86({ ...image, + opts = { ...image, uart1:true, // /dev/ttyS1 uart2:true, // /dev/ttyS2 uart3:true, // /dev/ttyS3 wasm_path: "com/isoterminal/v86.wasm", - memory_size: 32 * 1024 * 1024, - vga_memory_size: 2 * 1024 * 1024, - serial_container_xtermjs: opts.dom, - //screen_container: dom, //this.canvas.parentElement, + memory_size: opts.memory * 1024 * 1024, + vga_memory_size: 1024, //2 * 1024 * 1024, + screen_container: opts.dom, + //serial_container: opts.dom, bios: { url: "com/isoterminal/bios/seabios.bin", }, vga_bios: { url: "com/isoterminal/bios/vgabios.bin", + //urg|: "com/isoterminal/bios/VGABIOS-lgpl-latest.bin", }, network_relay_url: "wss://relay.widgetry.org/", - cmdline: "rw root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose modules=virtio_pci tsc=reliable init_on_free=on", + cmdline: "rw root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose modules=virtio_pci tsc=reliable init_on_freg|=on vga=ask", //vga=0x122", //bzimage_initrd_from_filesystem: true, //filesystem: { // baseurl: "com/isoterminal/v86/images/alpine-rootfs-flat", @@ -50,8 +68,9 @@ ISOTerminal.prototype.runISO = function(opts){ //disable_jit: false, filesystem: {}, autostart: true, - }); - + }; + this.emit('runISO',opts) + let emulator = this.emulator = new V86(opts) const loading = [ 'loading quantum bits and bytes', @@ -77,51 +96,39 @@ ISOTerminal.prototype.runISO = function(opts){ 'Transcending earthly limits' ] - let loadmsg = loading[ Math.floor(Math.random()*1000) % loading.length-1 ] + let loadmsg = loading[ Math.floor(Math.random()*1000) % loading.length ] this.emit('status',loadmsg) // replace welcome message https://github.com/copy/v86/blob/3c77b98bc4bc7a5d51a2056ea73d7666ca50fc9d/src/browser/serial.js#L231 let welcome = "This is the serial console. Whatever you type or paste here will be sent to COM1" - let motd = "\r" let msg = `${loadmsg}, please wait..` while( msg.length < welcome.length ) msg += " " msg += "\n" - motd += msg+"\033[0m" - - const files = [ - "com/isoterminal/mnt/js", - "com/isoterminal/mnt/jsh", - "com/isoterminal/mnt/xrsh", - "com/isoterminal/mnt/profile", - "com/isoterminal/mnt/profile.sh", - "com/isoterminal/mnt/profile.xrsh", - "com/isoterminal/mnt/profile.js", - "com/isoterminal/mnt/motd", - "com/isoterminal/mnt/v86pipe" - ] + motd += msg+"\033[0m" emulator.bus.register("emulator-started", async (e) => { this.emit('emulator-started',e) - emulator.serial_adapter.term.clear() - emulator.serial_adapter.term.write(motd) - let p = files.map( (f) => fetch(f) ) - Promise.all(p) - .then( (files) => { - files.map( (f) => { + if( emulator.serial_adapter ){ + emulator.serial_adapter.term.clear() + emulator.serial_adapter.term.write(motd) + } + + + if( me.opts.overlayfs ){ + fetch(me.opts.overlayfs) + .then( (f) => { f.arrayBuffer().then( (buf) => { - emulator.create_file( f.url.replace(/.*mnt\//,''), new Uint8Array(buf) ) + emulator.create_file('overlayfs.zip', new Uint8Array(buf) ) }) }) - }) + } - //emulator.serial0_send('chmod +x /mnt/js') - //emulator.serial0_send() let line = '' let ready = false - emulator.add_listener("serial0-output-byte", async (byte) => { - this.emit('serial0-output-byte',byte) + emulator.add_listener(`serial0-output-byte`, async (byte) => { + this.emit('${this.serial}-output-byte',byte) var chr = String.fromCharCode(byte); if(chr < " " && chr !== "\n" && chr !== "\t" || chr > "~") { @@ -137,12 +144,10 @@ ISOTerminal.prototype.runISO = function(opts){ { line += chr; } - - if( !ready && line.match(/^(\/ #|~%)/) ){ - this.emit('ready') + if( !ready && line.match(/^(\/ #|~%|\[.*\]>)/) ){ + this.emit('postReady') + setTimeout( () => this.emit('ready'), 500 ) ready = true - //emulator.serial0_send("root\n") - //emulator.serial0_send("mv /mnt/js . && chmod +x js\n") } }); }); diff --git a/com/isoterminal/feat/boot.js b/com/isoterminal/feat/boot.js index d0c0ad5..357c1af 100644 --- a/com/isoterminal/feat/boot.js +++ b/com/isoterminal/feat/boot.js @@ -10,12 +10,15 @@ ISOTerminal.prototype.boot = async function(){ env.push( 'export '+String(i).toUpperCase()+'="'+document.location[i]+'"') } await this.emulator.create_file("profile.browser", this.toUint8Array( env.join('\n') ) ) - let boot = `clear ; echo 'preparing xrsh env..'; source /mnt/profile` - // exec hash as extra boot cmd - if( document.location.hash.length > 1 ){ - boot += ` ; cmd='${decodeURI(document.location.hash.substr(1))}' && $cmd` + if( this.serial_input == 0 ){ + let boot = "source /etc/profile\n" + this.send(boot+"\n") + } + + if( this.emulator.serial_adapter ) this.emulator.serial_adapter.term.focus() + else{ + let els = [...document.querySelectorAll("div#screen")] + els.map( (el) => el.focus() ) } - this.emulator.serial0_send(boot+"\n") - this.emulator.serial_adapter.term.focus() } diff --git a/com/isoterminal/feat/index.html.js b/com/isoterminal/feat/index.html.js index c6765aa..b36220c 100644 --- a/com/isoterminal/feat/index.html.js +++ b/com/isoterminal/feat/index.html.js @@ -1,10 +1,29 @@ -ISOTerminal.addEventListener('ready', function(){ - this.addEventListener('file-read', (e) => { - const data = e.detail - if( data.file == 'index.html'){ - data.promise = new Promise( (resolve,reject) => { - resolve( this.toUint8Array(document.documentElement.outerHTML) ) - }) - } - }) +ISOTerminal.addEventListener('init', function(){ + + this.addEventListener('emulator-started', function(e){ + + const emulator = this.emulator + + // unix to js device + this.readFromPipe( '/mnt/index.html', async (data) => { + const buf = await emulator.read_file("index.html") + const decoder = new TextDecoder('utf-8'); + const html = decoder.decode(buf) + try{ + let $scene = document.querySelector("a-scene") + let $root = document.querySelector("a-entity#root") + if( !$root ){ + $root = document.createElement("a-entity") + $root.id = "root" + $scene.appendChild($root) + } + $root.innerHTML = html + }catch(e){ + console.error(e) + } + }) + + }) + }) + diff --git a/com/isoterminal/feat/index.js.js b/com/isoterminal/feat/index.js.js new file mode 100644 index 0000000..4482d0e --- /dev/null +++ b/com/isoterminal/feat/index.js.js @@ -0,0 +1,28 @@ +ISOTerminal.addEventListener('init', function(){ + + this.addEventListener('emulator-started', function(e){ + + const emulator = this.emulator + + // unix to js device + this.readFromPipe( '/mnt/index.js', async (data) => { + const buf = await emulator.read_file("index.js") + const decoder = new TextDecoder('utf-8'); + const js = decoder.decode(buf) + try{ + let $root = document.querySelector("script#root") + if( !$root ){ + $root = document.createElement("script") + $root.id = "root" + document.body.appendChild($root) + } + $root.innerHTML = js + }catch(e){ + console.error(e) + } + }) + + }) + +}) + diff --git a/com/isoterminal/feat/javascript.js b/com/isoterminal/feat/javascript.js index 0b4ad67..960918f 100644 --- a/com/isoterminal/feat/javascript.js +++ b/com/isoterminal/feat/javascript.js @@ -6,7 +6,7 @@ ISOTerminal.addEventListener('init', function(){ // unix to js device this.readFromPipe( '/mnt/js', async (data) => { - const buf = await emulator.read_file("dev/browser/js") + const buf = await emulator.read_file("js") const decoder = new TextDecoder('utf-8'); const script = decoder.decode(buf) let PID="?" diff --git a/com/isoterminal/feat/jsconsole.js b/com/isoterminal/feat/jsconsole.js index 1986339..84c6dfa 100644 --- a/com/isoterminal/feat/jsconsole.js +++ b/com/isoterminal/feat/jsconsole.js @@ -30,14 +30,12 @@ ISOTerminal.addEventListener('emulator-started', function(){ let emulator = this.emulator this.redirectConsole( (str,prefix) => { - if( emulator.log_to_tty ){ - prefix = prefix ? prefix+' ' : ' ' - str.trim().split("\n").map( (line) => { - emulator.serial_adapter.term.write( '\r\x1b[38;5;165m/dev/browser: \x1b[0m'+prefix+line+'\n' ) - }) - emulator.serial_adapter.term.write( '\r' ) - } - emulator.fs9p.append_file( "console", str ) + let finalStr = "" + prefix = prefix ? prefix+' ' : ' ' + str.trim().split("\n").map( (line) => { + finalStr += '\x1b[38;5;165m/dev/browser: \x1b[0m'+prefix+line+'\n' + }) + emulator.fs9p.append_file( "console", finalStr ) }) window.addEventListener('error', function(event) { diff --git a/com/isoterminal/feat/screenbuttons.js b/com/isoterminal/feat/screenbuttons.js new file mode 100644 index 0000000..595b28e --- /dev/null +++ b/com/isoterminal/feat/screenbuttons.js @@ -0,0 +1,13 @@ +ISOTerminal.addEventListener('ready', function(){ + this.screenButtonsCreate() +}) + +ISOTerminal.prototype.screenButtonsCreate = function(){ + let el = document.createElement("a-plane") + el.setAttribute("height","1") + el.setAttribute("width","1") + el.setAttribute("scale","0.1 0.07 1") + el.setAttribute("position", "-0.326 -0.270 0") + this.instance.appendChild(el) +} + diff --git a/com/isoterminal/feat/xterm.js b/com/isoterminal/feat/xterm.js index 9df0d3f..6342887 100644 --- a/com/isoterminal/feat/xterm.js +++ b/com/isoterminal/feat/xterm.js @@ -2,7 +2,14 @@ ISOTerminal.addEventListener('init', function(){ if( typeof Terminal != 'undefined' ) this.xtermInit() }) +ISOTerminal.addEventListener('runISO', function(e){ + let opts = e.detail + opts.serial_container_xtermjs = opts.screen_container + delete opts.screen_container +}) + ISOTerminal.prototype.xtermInit = function(){ + this.serial_input = 0 // set input to serial line 0 let isoterm = this // monkeypatch Xterm (which V86 initializes) so we can add our own constructor args window._Terminal = window.Terminal @@ -11,7 +18,8 @@ ISOTerminal.prototype.xtermInit = function(){ cursorBlink:true, onSelectionChange: function(e){ debugger - } + }, + letterSpacing: 0 }) term.onSelectionChange( () => { @@ -27,6 +35,14 @@ ISOTerminal.prototype.xtermInit = function(){ // toggle immersive with ESCAPE //document.body.addEventListener('keydown', (e) => e.key == 'Escape' && this.emulator.serial_adapter.term.blur() ) }) + + const resize = (w,h) => { + setTimeout( () => { + isoterm.xtermAutoResize(isoterm.emulator.serial_adapter.term, isoterm.instance,-2) + },800) // wait for resize anim + } + isoterm.instance.addEventListener('window.onresize', resize ) + isoterm.instance.addEventListener('window.onmaximize', resize ) } ISOTerminal.prototype.xtermAutoResize = function(term,instance,rowoffset){ diff --git a/com/isoterminal/mnt/xrsh b/com/isoterminal/mnt/xrsh deleted file mode 100755 index f3867e8..0000000 --- a/com/isoterminal/mnt/xrsh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/sh -# -# a minimalistic terminal muxer - -# Save the original stdout and stderr file descriptors for later restoration -exec 3>&1 4>&2 - -# Function to check if a session is already running on the given VT -is_session_running() { - vt_number=$1 - - # Check if any process is running on /dev/tty - fuser /dev/tty"$vt_number" >/dev/null 2>&1 - return $? -} - -# Function to mute the output of a session -mute_session() { - vt_number=$1 - if is_session_running "$vt_number"; then - # Redirect stdout and stderr of the session to /dev/null - exec > /dev/null 2>&1 - fi -} - -# Function to unmute the current session (restore stdout and stderr) -unmute_session() { - exec 1>&3 2>&4 # Restore stdout and stderr from file descriptors 3 and 4 -} - -# Function to start a new session if not already running -start_or_switch_session() { - vt_number=$1 - - # Mute all other sessions except the one we're switching to - for vt in $(seq 1 12); do # Assuming you have up to 12 VTs, adjust as needed - if [ "$vt" != "$vt_number" ]; then - mute_session "$vt" - fi - done - - if is_session_running "$vt_number"; then - echo "Switching to existing session on VT$vt_number" - unmute_session # Unmute the session we're switching to - chvt "$vt_number" - else - echo "Starting a new session on VT$vt_number" - openvt -c "$vt_number" -- /bin/sh & - sleep 1 # Give the session a moment to start - unmute_session # Unmute the new session - chvt "$vt_number" - fi -} - -# Ensure a session number is provided -if [ "$#" -ne 1 ]; then - echo "Usage: $0 " - exit 1 -fi - -# Start or switch to the session -start_or_switch_session $1 diff --git a/com/window.js b/com/window.js index 846d8d6..f6d6d1d 100644 --- a/com/window.js +++ b/com/window.js @@ -1,13 +1,14 @@ AFRAME.registerComponent('window', { schema:{ - title: {type:'string',"default":"title"}, - width: {type:'string'}, // wrap - height: {type:'string',"default":'260px'}, - uid: {type:'string'}, - attach: {type:'selector'}, - dom: {type:'selector'}, - x: {type:'string',"default":"center"}, - y: {type:'string',"default":"center"} + title: {type:'string',"default":"title"}, + width: {type:'string'}, // wrap + height: {type:'string',"default":'260px'}, + uid: {type:'string'}, + attach: {type:'selector'}, + dom: {type:'selector'}, + max: {type:'boolean',"default":false}, + x: {type:'string',"default":"center"}, + y: {type:'string',"default":"center"} }, dependencies:{ @@ -33,13 +34,14 @@ AFRAME.registerComponent('window', { id: this.data.uid || String(Math.random()).substr(4), // important hint for html-mesh root: this.data.attach || document.body, mount: this.data.dom, + max: this.data.max, onresize: () => this.el.emit('window.onresize',{}), onmaximize: () => this.el.emit('window.onmaximize',{}), - oncreate: () => { + oncreate: (e) => { this.el.emit('window.oncreate',{}) // resize after the dom content has been rendered & updated setTimeout( () => { - winbox.resize( this.el.dom.offsetWidth+'px', this.el.dom.offsetHeight+'px' ) + if( !this.data.max ) winbox.resize( this.el.dom.offsetWidth+'px', this.el.dom.offsetHeight+'px' ) // hint grabbable's obb-collider to track the window-object this.el.components['obb-collider'].data.trackedObject3D = 'components.html.el.object3D.children.0' this.el.components['obb-collider'].update()