2024-10-02 19:03:04 +00:00
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 ( )
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 ] )
obj . addEventListener ( event , ISOTerminal . listener [ event ] [ cb ] )
// compose object with functions
for ( let i in ISOTerminal . prototype ) obj [ i ] = ISOTerminal . prototype [ i ]
obj . emit ( 'init' )
2025-01-24 17:21:26 +01:00
instance . sceneEl . emit ( "isoterminal_init" , { } )
2024-10-02 19:03:04 +00:00
return obj
}
ISOTerminal . prototype . emit = function ( event , data , sender ) {
data = data || false
const evObj = new CustomEvent ( event , { detail : data } )
2024-11-15 09:23:06 +00:00
this . preventFrameDrop ( ( ) => {
2024-10-04 15:49:15 +00:00
// forward event to worker/instance/AFRAME element or component-function
// this feels complex, but actually keeps event- and function-names more concise in codebase
this . dispatchEvent ( evObj )
if ( sender != "instance" && this . instance ) this . instance . dispatchEvent ( evObj )
2024-10-15 10:32:16 +00:00
if ( sender != "worker" && this . worker ) this . worker . postMessage ( { event , data } , PromiseWorker . prototype . getTransferable ( data ) )
2024-10-04 15:49:15 +00:00
if ( sender !== undefined && typeof this [ event ] == 'function' ) this [ event ] . apply ( this , data && data . push ? data : [ data ] )
2024-11-15 09:23:06 +00:00
} )
2024-10-02 19:03:04 +00:00
}
2024-09-05 17:09:40 +00:00
2024-10-02 19:03:04 +00:00
ISOTerminal . addEventListener = ( event , cb ) => {
ISOTerminal . listener = ISOTerminal . listener || { }
ISOTerminal . listener [ event ] = ISOTerminal . listener [ event ] || [ ]
ISOTerminal . listener [ event ] . push ( cb )
}
2024-09-16 11:28:28 +00:00
2025-04-09 20:48:10 +02:00
ISOTerminal . prototype . exec = function ( opts ) {
const shellscript = opts [ 0 ] ;
const cb = opts [ 1 ] ;
2025-04-18 15:22:23 +02:00
let cmd = ` printf " \n \r "; { sh<<EOF \n ${ shellscript } ; \n EOF \n } &> /mnt/exec; \n `
2025-06-20 17:19:48 +02:00
console . log ( cmd )
2025-04-09 20:48:10 +02:00
if ( cb ) {
window . cb = cb
2025-04-18 15:22:23 +02:00
cmd += ` js 'document.querySelector("[isoterminal]").emit("read_file", ["exec", window.cb ])'; \n `
2025-04-09 20:48:10 +02:00
}
2025-04-18 15:22:23 +02:00
this . send ( cmd , 1 )
2024-09-16 11:28:28 +00:00
}
2024-11-15 09:23:06 +00:00
ISOTerminal . prototype . hook = function ( hookname , args ) {
2025-01-15 18:02:30 +01:00
let cmd = ` { type hook || source /etc/profile.sh; }; hook ${ hookname } " ${ args . join ( '" "' ) } " `
2025-06-20 17:19:48 +02:00
this . exec ( [ cmd ] )
2024-11-15 09:23:06 +00:00
}
2024-10-02 19:03:04 +00:00
ISOTerminal . prototype . serial _input = 0 ; // can be set to 0,1,2,3 to define stdinput tty (xterm plugin)
2024-09-16 11:28:28 +00:00
ISOTerminal . prototype . send = function ( str , ttyNr ) {
2024-10-02 19:03:04 +00:00
if ( ttyNr == undefined ) ttyNr = this . serial _input
2025-01-28 11:30:31 +01:00
if ( ( this . emulator || this . worker ) && this . ready ) {
if ( ttyNr == undefined ) {
if ( this . emulator . serial _adapter ) {
this . emulator . serial _adapter . term . paste ( str )
} else this . emulator . keyboard _send _text ( str ) // vga screen
} else {
this . convert . toUint8Array ( str ) . map ( ( c ) => {
this . preventFrameDrop (
( ) => {
this . worker . postMessage ( { event : ` serial ${ ttyNr } -input ` , data : c } )
}
)
} )
}
2024-09-16 11:28:28 +00:00
} else {
2025-01-28 11:30:31 +01:00
this . emit ( 'serial-output-string' , str )
2024-09-16 11:28:28 +00:00
}
2024-09-05 17:09:40 +00:00
}
2024-09-23 14:59:29 +00:00
ISOTerminal . prototype . convert = {
arrayBufferToBase64 : function ( buffer ) {
let binary = '' ;
const bytes = new Uint8Array ( buffer ) ;
const len = bytes . byteLength ;
for ( let i = 0 ; i < len ; i ++ ) binary += String . fromCharCode ( bytes [ i ] ) ;
2024-10-23 16:50:07 +00:00
return btoa ( binary ) ;
2024-09-23 14:59:29 +00:00
} ,
base64ToArrayBuffer : function ( base64 ) {
2024-10-23 16:50:07 +00:00
const binaryString = atob ( base64 ) ;
2024-09-23 14:59:29 +00:00
const len = binaryString . length ;
const bytes = new Uint8Array ( len ) ;
for ( let i = 0 ; i < len ; i ++ ) {
bytes [ i ] = binaryString . charCodeAt ( i ) ;
}
return bytes . buffer ;
} ,
toUint8Array : function ( str ) {
str = String ( str ) || String ( "" )
// Create a new Uint8Array with the same length as the input string
const uint8Array = new Uint8Array ( str . length ) ;
// Iterate over the string and populate the Uint8Array
for ( let i = 0 ; i < str . length ; i ++ ) {
uint8Array [ i ] = str . charCodeAt ( i ) ;
}
return uint8Array ;
} ,
Uint8ArrayToString : function ( arr ) {
const decoder = new TextDecoder ( 'utf-8' ) ; // Specify encoding
return decoder . decode ( arr ) ;
2024-09-03 16:33:35 +00:00
}
2024-09-23 14:59:29 +00:00
}
2024-09-03 16:33:35 +00:00
2024-10-02 19:03:04 +00:00
ISOTerminal . prototype . start = function ( opts ) {
2024-09-16 11:28:28 +00:00
let me = this
this . opts = { ... this . opts , ... opts }
2024-09-03 16:33:35 +00:00
let image = { }
if ( opts . iso . match ( /\.iso$/ ) ) image . cdrom = { url : opts . iso }
if ( opts . iso . match ( /\.bin$/ ) ) image . bzimage = { url : opts . iso }
2024-09-16 11:28:28 +00:00
opts = { ... image ,
2024-09-05 17:09:40 +00:00
uart1 : true , // /dev/ttyS1
uart2 : true , // /dev/ttyS2
uart3 : true , // /dev/ttyS3
2024-10-02 19:03:04 +00:00
wasm _path : "v86.wasm" ,
2024-09-16 11:28:28 +00:00
memory _size : opts . memory * 1024 * 1024 ,
2024-09-17 16:59:38 +00:00
vga _memory _size : 2 * 1024 * 1024 ,
2024-10-02 19:03:04 +00:00
//screen_container: opts.dom,
2024-09-16 11:28:28 +00:00
//serial_container: opts.dom,
2024-09-03 16:33:35 +00:00
bios : {
2024-10-02 19:03:04 +00:00
url : "bios/seabios.bin" ,
2024-09-03 16:33:35 +00:00
} ,
vga _bios : {
2024-10-02 19:03:04 +00:00
url : "bios/vgabios.bin" ,
2024-09-16 11:28:28 +00:00
//urg|: "com/isoterminal/bios/VGABIOS-lgpl-latest.bin",
2024-09-03 16:33:35 +00:00
} ,
2024-09-16 11:28:28 +00:00
cmdline : "rw root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose modules=virtio_pci tsc=reliable init_on_freg|=on vga=ask" , //vga=0x122",
2024-11-28 06:25:05 +00:00
net _device : {
2025-05-23 18:02:33 +02:00
//type:"ne2k",
2025-05-23 18:05:12 +02:00
relay _url : "fetch"
//relay_url:"wss://relay.widgetry.org/"
2025-05-23 18:02:33 +02:00
//local_http: true,
//type:"virtio",
//relay_url:"fetch",
//cors_proxy: "https://corsproxy.io/"
2024-11-28 06:25:05 +00:00
} ,
2024-09-03 16:33:35 +00:00
//bzimage_initrd_from_filesystem: true,
//filesystem: {
// baseurl: "com/isoterminal/v86/images/alpine-rootfs-flat",
// basefs: "com/isoterminal/v86/images/alpine-fs.json",
// },
//screen_dummy: true,
//disable_jit: false,
2025-02-18 15:24:38 +01:00
overlayfs : this . opts . overlayfs ,
2024-09-03 16:33:35 +00:00
filesystem : { } ,
autostart : true ,
2025-01-15 22:36:54 +01:00
prompt : this . opts . prompt ,
2024-10-15 10:32:16 +00:00
debug : this . opts . debug ? true : false
2024-09-16 11:28:28 +00:00
} ;
2024-09-03 16:33:35 +00:00
2024-10-15 10:32:16 +00:00
this
. setupWorker ( opts )
. startVM ( opts )
}
ISOTerminal . prototype . setupWorker = function ( opts ) {
2025-01-27 16:03:37 +01:00
if ( typeof window . PromiseWorker == 'undefined' ) return this
2024-10-04 09:08:39 +00:00
/ *
* the WebWorker ( which runs v86 )
*
* /
2024-10-15 10:32:16 +00:00
this . worker = new PromiseWorker ( "com/isoterminal/worker.js" , ( cb , event , data ) => {
if ( ! data . promiseId ) this . emit ( event , data , "worker" ) // forward event to world
2024-10-04 15:49:15 +00:00
this . preventFrameDrop ( cb ( event , data ) )
2024-10-15 10:32:16 +00:00
} )
2024-10-04 09:08:39 +00:00
2024-10-15 10:32:16 +00:00
return this
}
2024-10-04 09:08:39 +00:00
2025-01-15 22:36:54 +01:00
ISOTerminal . prototype . getLoaderMsg = function ( ) {
2024-10-04 09:08:39 +00:00
2024-09-03 16:33:35 +00:00
const loading = [
'loading quantum bits and bytes' ,
'preparing quantum flux capacitors' ,
'crunching peanuts and chakras' ,
'preparing parallel universe' ,
'loading quantum state fluctuations' ,
'preparing godmode' ,
'loading cat pawns and cuteness' ,
'beaming up scotty' ,
'still faster than Windows update' ,
'loading a microlinux' ,
'figuring out meaning of life' ,
'Aligning your chakras now' ,
'Breathing in good vibes' ,
'Finding inner peace soon' ,
'Centering your Zen energy' ,
'Awakening third eye powers' ,
'Tuning into the universe' ,
'Balancing your cosmic karma' ,
'Stretching time and space' ,
'Recharging your soul battery' ,
'Transcending earthly limits'
]
2024-10-04 15:49:15 +00:00
const empower = [
"FOSS gives users control over their software, offering freedom to modify and share" ,
2025-01-15 18:02:30 +01:00
"Feeling powerless? FOSS escapes a mindset known as learned helplessness" ,
2024-10-04 15:49:15 +00:00
"FOSS breaks this cycle by showing that anyone can learn and contribute" ,
"Proprietary software can make users dependent, but FOSS offers real choices" ,
"FOSS communities provide support and encourage users to develop new skills" ,
"FOSS empowers users to customize and improve their tools" ,
"Engaging with FOSS helps build confidence and self-reliance in tech" ,
"FOSS shows that anyone can shape the digital world with curiosity and effort" ,
2025-01-15 18:02:30 +01:00
"Linux can revive old computers, extending their life and reduces e-waste" ,
2024-10-04 15:49:15 +00:00
"Many lightweight Linux distributions run smoothly on older hardware" ,
"Installing Linux on aging devices keeps them functional instead of sending them to the landfill" ,
"Linux uses fewer resources, making it ideal for reusing older machines" ,
"By using Linux, you can avoid buying new hardware, cutting down on tech waste" ,
"Instead of discarding slow devices, Linux can bring them back to life" ,
"Linux supports a wide range of devices, helping to prevent e-waste" ,
"Open-source drivers in Linux enable compatibility with old peripherals, reducing the need for replacements" ,
"Free Linux software helps users avoid planned obsolescence in commercial products" ,
"Switching to Linux promotes sustainability by reducing demand for new gadgets and lowering e-waste"
]
2025-01-15 22:36:54 +01:00
let motd = `
2025-02-14 12:08:14 +01:00
\ r [ 38 ; 5 ; 57 m . . _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ . . _ . . _ . . .
\ r [ 38 ; 5 ; 93 m . . . \ \ \ \ / /\\______ \\/ _ _ _ _ _ // | \\. .
\ r [ 38 ; 5 ; 93 m . . . \ \ / | _ / \ \ _ _ _ _ _ \ \ / ~ \ \ .
\ r [ 38 ; 5 ; 129 m . . . / \ \ | | \ \ / \ \ Y / .
\ r [ 38 ; 5 ; 165 m . . . / _ _ _ / \ \ \ \ | _ _ _ _ | _ / _ _ _ _ _ _ _ / \ \ _ _ _ | _ / . .
\ r [ 38 ; 5 ; 201 m . . . . . . \ \ _ / . . . . \ \ / . . . . \ \ / . . _ \ \ / . .
\ r [ 38 ; 5 ; 165 m ▬ ▬ ▬ ▬ ▬ ▬ ▬ ▬ [ 38 ; 5 ; 51 mhttps : //xrsh.isvery.ninja [38;5;165m ▬▬▬▬▬▬▬▬▬▬▬▬
\ r [ 38 ; 5 ; 165 mlocal - first , polyglot , unixy WebXR IDE & runtime
2025-01-15 18:02:30 +01:00
\ r [ 38 ; 5 ; 57 m
2025-02-14 12:08:14 +01:00
\ rcredits
\ r -- -- -- -
\ r [ 38 ; 5 ; 51 mhttps : //www.w3.org/TR/webxr
\ rhttps : //xrfragment.org
\ rhttps : //threejs.org
\ rhttps : //aframe.org
\ rhttps : //busybox.net
\ rhttps : //buildroot.org
\ rfediverse : @ lvk @ mastodon . online @ utopiah @ mastodon . pirateparty . be @ nlnet @ nlnet . nl
2025-01-15 22:36:54 +01:00
\ r `
2024-10-04 15:49:15 +00:00
2024-10-02 19:03:04 +00:00
const text _color = "\r [38;5;129m"
const text _reset = "\033[0m"
2025-01-15 22:36:54 +01:00
const loadmsg = "\n\r" + loading [ Math . floor ( Math . random ( ) * 1000 ) % loading . length ] + "..please wait \n\n\r"
const empowermsg = "\n\r" + text _reset + '"' + empower [ Math . floor ( Math . random ( ) * 1000 ) % empower . length ] + '"\n\r'
return { motd , text _color , text _reset , loadmsg , empowermsg }
}
ISOTerminal . prototype . startVM = function ( opts ) {
this . v86opts = opts
2024-09-03 16:33:35 +00:00
2024-10-02 19:03:04 +00:00
this . addEventListener ( 'emulator-started' , async ( e ) => {
2025-05-22 16:24:53 +02:00
if ( this . boot . fromImage ) return this . emit ( 'serial-output-string' , "\r[!] downloading session...please wait\n\r[!] this could take a while depending on your connection..\n\r" )
2024-09-16 11:28:28 +00:00
2024-09-03 16:33:35 +00:00
let line = ''
2024-10-04 09:08:39 +00:00
this . ready = false
this . addEventListener ( ` serial0-output-string ` , async ( e ) => {
const str = e . detail
// lets scan for a prompt so we can send a 'ready' event to the world
2024-11-28 06:25:05 +00:00
if ( ! this . ready && str . match ( /\n(\/ #|~ #|~%|\[.*\]>)/ ) ) this . postBoot ( )
2024-10-04 15:49:15 +00:00
if ( this . ready || ! this . opts . muteUntilPrompt ) this . emit ( 'serial-output-string' , e . detail )
2024-10-04 09:08:39 +00:00
} )
2024-09-03 16:33:35 +00:00
} ) ;
2025-01-15 22:36:54 +01:00
let msglib = this . getLoaderMsg ( )
let msg = msglib . motd
this . emit ( 'serial-output-string' , msg )
2025-02-21 15:59:29 +01:00
this . emit ( 'bootMenu' , { bootMenu : this . opts . bootMenu , bootMenuURL : this . opts . bootMenuURL } )
2025-01-15 22:36:54 +01:00
}
ISOTerminal . prototype . bootISO = function ( ) {
2025-05-22 16:24:53 +02:00
const getImage = ( str ) => decodeURIComponent (
str . match ( /\&img=/ ) ? str . replace ( /.*img=/ , '' ) . replace ( /\&.*/ , '' ) : ''
)
2025-01-15 22:36:54 +01:00
let msglib = this . getLoaderMsg ( )
2025-02-28 17:05:43 +01:00
this . emit ( 'status' , msglib . loadmsg )
2025-01-15 22:36:54 +01:00
let msg = "\n\r" + msglib . empowermsg + msglib . text _color + msglib . loadmsg + msglib . text _reset
this . emit ( 'serial-output-string' , msg )
2025-05-22 16:24:53 +02:00
if ( getImage ( this . boot . hash ) ) {
this . boot . fromImage = true
}
this . emit ( 'runISO' , { ... this . v86opts , bufferLatency : this . opts . bufferLatency , img : getImage ( this . boot . hash ) } )
2024-09-03 16:33:35 +00:00
}
2024-10-03 10:41:53 +00:00
2024-10-04 15:49:15 +00:00
ISOTerminal . prototype . postBoot = function ( cb ) {
this . emit ( 'postReady' , { } )
this . ready = true
setTimeout ( ( ) => {
this . emit ( 'ready' , { } )
if ( cb ) cb ( )
} , 500 )
}
// this is allows (unsophisticated) outputbuffering
2024-10-04 09:08:39 +00:00
ISOTerminal . prototype . bufferOutput = function ( byte , cb , latency ) {
const resetBuffer = ( ) => ( { str : "" } )
this . buffer = this . buffer || resetBuffer ( )
this . buffer . str += String . fromCharCode ( byte )
if ( ! this . buffer . id ) {
cb ( this . buffer . str ) // send out leading call
this . buffer = resetBuffer ( )
this . buffer . id = setTimeout ( ( ) => { // accumulate succesive calls
if ( this . buffer . str ) cb ( this . buffer . str )
this . buffer = resetBuffer ( )
} , this . latency || 250 )
2024-10-03 10:41:53 +00:00
}
}
2024-10-04 09:08:39 +00:00
2025-01-03 12:44:26 +01:00
//ISOTerminal.prototype.bufferOutput = function(byte, cb, latency, buffer) {
// const str = String.fromCharCode(byte);
// //if (str === '\r' || str === '\n' || str.charCodeAt(0) < 32 || str.charCodeAt(0) === 127) {
// // cb(str);
// //} else if (str === '\x1b') { // ESC
// // buffer.esc = true;
// //} else if (buffer.esc) {
// // cb('\x1b' + str);
// // buffer.esc = false;
// //} else {
// buffer.str = (buffer.str || '') + str;
// if (Date.now() - (buffer.timestamp || 0) >= latency) {
// console.log(buffer.str)
// cb(buffer.str);
// buffer.str = '';
// buffer.timestamp = Date.now();
// }
// //}
//}
2024-10-04 15:49:15 +00:00
ISOTerminal . prototype . preventFrameDrop = function ( cb ) {
// don't let workers cause framerate dropping
const xr = this . instance . sceneEl . renderer . xr
if ( xr . isPresenting ) {
xr . getSession ( ) . requestAnimationFrame ( cb )
} else {
window . requestAnimationFrame ( cb )
}
}