"use strict"; // Top-level state and UI manager globalThis.App = class App { // Produce a new class instance static async create() { let ret = new App(); await ret.init(); return ret; } // Perform initial tasks async init() { // Initialization tasks this.initEnv(); this.initMenus(); // WebAssembly core module this.core = new Worker(Bundle.get("app/Emulator.js").toDataURL()); await new Promise((resolve,reject)=>{ this.core.onmessage = e=>{ this.core.onmessage = e=>this.onmessage(e.data); resolve(); }; this.core.postMessage({ command: "Init", wasm : Bundle.get("core.wasm").data.buffer }); }); // Desktop pane this.desktop = this.gui.newPanel({ layout: "desktop" }); this.desktop.setRole("group"); this.desktop.element.setAttribute("desktop", ""); this.gui.add(this.desktop); // Debugging windows this.debuggers = [ new Debugger(this, 0), new Debugger(this, 1) ]; } // Initialize environment settings initEnv() { // Configure themes Bundle.get("app/theme/kiosk.css").style(); this.themes = { dark : Bundle.get("app/theme/dark.css" ).style(false), light : Bundle.get("app/theme/light.css" ).style(true ), virtual: Bundle.get("app/theme/virtual.css").style(false) }; this.theme = this.themes["light"]; // Produce toolkit instance this.gui = new Toolkit.Application({ layout: "grid", rows : "max-content auto" }); document.body.appendChild(this.gui.element); window.addEventListener("resize", ()=>{ this.gui.setSize(window.innerWidth+"px", window.innerHeight+"px"); }); window.dispatchEvent(new Event("resize")); // Configure locales this.gui.addLocale(Bundle.get("app/locale/en-US.js").toString()); this.gui.setLocale(navigator.language); } // Initialize main menu bar initMenus() { // Menu bar this.mainMenu = this.gui.newMenuBar({ name: "{menu._}" }); this.gui.add(this.mainMenu); this.gui.addPropagationListener(e=>this.mainMenu.restoreFocus()); // File menu let menu = this.mainMenu.newMenu({ text: "{menu.file._}"}); let item = menu.newMenuItem({ text: "{menu.file.loadROM}"}); item.addClickListener(()=>this.loadROM()); // Debug menu menu = this.mainMenu.newMenu({ text: "{menu.debug._}" }); item = menu.newMenuItem({ text: "{memory._}" }); item.addClickListener( ()=>this.debuggers[0].memory.setVisible(true, true)); item = menu.newMenuItem({ text: "{cpu._}" }); item.addClickListener( ()=>this.debuggers[0].cpu.setVisible(true, true)); // Theme menu menu = this.mainMenu.newMenu({ text: "{menu.theme._}" }); item = menu.newMenuItem({ text: "{menu.theme.light}" }); item.addClickListener(()=>this.setTheme("light")); item = menu.newMenuItem({ text: "{menu.theme.dark}" }); item.addClickListener(()=>this.setTheme("dark")); item = menu.newMenuItem({ text: "{menu.theme.virtual}" }); item.addClickListener(()=>this.setTheme("virtual")); } ///////////////////////////// Message Methods ///////////////////////////// // Message received onmessage(msg) { if ("dbgwnd" in msg) { this.debuggers[msg.sim].message(msg); return; } switch (msg.command) { case "SetROM": this.setROM(msg); break; } } // ROM buffer has been configured setROM(msg) { let dbg = this.debuggers[msg.sim]; dbg.refresh(); } ///////////////////////////// Private Methods ///////////////////////////// // Prompt the user to select a ROM file loadROM() { let file = document.createElement("input"); file.type = "file"; file.addEventListener("input", ()=>this.selectROM(file.files[0])); file.click(); } // Specify a ROM file async selectROM(file) { // No file is specified (perhaps the user canceled) if (file == null) return; // Check the file's size if ( file.size < 1024 || file.size > 0x1000000 || (file.size - 1 & file.size) != 0 ) { alert(this.gui.translate("{app.romNotVB}")); return; } // Load the file data into a byte buffer let filename = file.name; try { file = await file.arrayBuffer(); } catch { alert(this.gui.translate("{app.readFileError}")); return; } // Send the ROM to the WebAssembly core module this.core.postMessage({ command: "SetROM", reset : true, rom : file, sim : 0 }, file); } // Specify the current color theme setTheme(key) { let theme = this.themes[key]; if (theme == this.theme) return; let old = this.theme; this.theme = theme; theme.setEnabled(true); old.setEnabled(false); } };