refactor frontpage loading of profiles

This commit is contained in:
Fabian 2024-09-20 17:09:15 -06:00
parent 0bc1008c93
commit 49865ebbc6
3 changed files with 326 additions and 436 deletions

View file

@ -127,9 +127,9 @@
<table>
<tr>
<td width="350"><label for="cd_image">CD image</label></td>
<td width="350"><label for="cdrom_image">CD image</label></td>
<td>
<input type="file" id="cd_image">
<input type="file" id="cdrom_image">
</td>
</tr>

View file

@ -70,9 +70,9 @@
<h4>Setup</h4>
<table>
<tr>
<td width="350"><label for="cd_image">CD image</label></td>
<td width="350"><label for="cdrom_image">CD image</label></td>
<td>
<input type="file" id="cd_image">
<input type="file" id="cdrom_image">
</td>
</tr>

View file

@ -2,25 +2,7 @@
(function()
{
/** @const */
var ON_LOCALHOST = !location.hostname.endsWith("copy.sh");
/**
* @return {Object.<string, string>}
*/
function get_query_arguments()
{
var query = location.search.substr(1).split("&");
var parameters = {};
for(var i = 0; i < query.length; i++)
{
var param = query[i].split("=");
parameters[param[0]] = decodeURIComponent(param.slice(1).join("="));
}
return parameters;
}
const ON_LOCALHOST = !location.hostname.endsWith("copy.sh");
function set_title(text)
{
@ -47,11 +29,11 @@
}
}
var progress_ticks = 0;
let progress_ticks = 0;
function show_progress(e)
{
var el = $("loading");
const el = $("loading");
el.style.display = "block";
if(e.file_name.endsWith(".wasm"))
@ -68,7 +50,7 @@
return;
}
var line = "Downloading images ";
let line = "Downloading images ";
if(typeof e.file_index === "number" && e.file_count)
{
@ -99,7 +81,7 @@
return document.getElementById(id);
}
// these values are stored in localStorage
// These values were previously stored in localStorage
const elements_to_restore = [
"memory_size",
"video_memory_size",
@ -108,6 +90,14 @@
"enable_acpi",
"boot_order",
];
for(const item of elements_to_restore)
{
try
{
window.localStorage.removeItem(item);
}
catch(e) {}
}
function onload()
{
@ -117,84 +107,11 @@
return;
}
var settings = {};
$("start_emulation").onclick = function()
$("start_emulation").onclick = function(e)
{
// Save entered options into the local storage
for(const id of elements_to_restore)
{
const element = $(id);
if(element)
{
if(element.tagName === "SELECT" || element.type !== "checkbox")
{
window.localStorage.setItem(id, element.value);
}
else
{
window.localStorage.setItem(id, element.checked);
}
}
}
$("boot_options").style.display = "none";
set_profile("custom");
var images = [];
var last_file;
var bios = $("bios").files[0];
if(bios)
{
last_file = bios;
settings.bios = { buffer: bios };
}
var floppy_file = $("floppy_image").files[0];
if(floppy_file)
{
last_file = floppy_file;
settings.fda = { buffer: floppy_file };
}
var cd_file = $("cd_image").files[0];
if(cd_file)
{
last_file = cd_file;
settings.cdrom = { buffer: cd_file };
}
var hda_file = $("hda_image").files[0];
if(hda_file)
{
last_file = hda_file;
settings.hda = { buffer: hda_file };
}
var hdb_file = $("hdb_image") && $("hdb_image").files[0];
if(hdb_file)
{
last_file = hdb_file;
settings.hdb = { buffer: hdb_file };
}
if($("multiboot_image"))
{
var multiboot_file = $("multiboot_image").files[0];
if(multiboot_file)
{
last_file = multiboot_file;
settings.multiboot = { buffer: multiboot_file };
}
}
if(last_file)
{
set_title(last_file.name);
}
start_emulation(settings);
start_emulation(null, null);
$("start_emulation").blur();
e.preventDefault();
};
if(DEBUG)
@ -202,11 +119,17 @@
debug_onload();
}
const query_args = get_query_arguments();
const host = query_args["cdn"] || (ON_LOCALHOST ? "images/" : "//i.copy.sh/");
if(DEBUG && ON_LOCALHOST)
{
// don't use online relay in debug mode
$("networking_proxy").value = "ws://localhost:8080/";
}
const query_args = new URLSearchParams(location.search);
const host = query_args.get("cdn") || (ON_LOCALHOST ? "images/" : "//i.copy.sh/");
// Abandonware OS images are from https://winworldpc.com/library/operating-systems
var oses = [
const oses = [
{
id: "archlinux",
name: "Arch Linux",
@ -1328,7 +1251,7 @@
if(DEBUG)
{
// see tests/kvm-unit-tests/x86/
var tests = [
const tests = [
"realmode",
// All tests below require an APIC
"cmpxchg8b",
@ -1361,7 +1284,7 @@
}
}
var profile = query_args["profile"];
const profile = query_args.get("profile");
if(!profile && !DEBUG)
{
@ -1376,92 +1299,38 @@
link.href = "build/xterm.js";
document.head.appendChild(link);
if(query_args["disable_jit"])
for(const os of oses)
{
settings.disable_jit = true;
}
if(query_args["use_bochs_bios"])
{
settings.use_bochs_bios = true;
}
const m = parseInt(query_args["m"], 10);
if(m > 0)
{
settings.memory_size = Math.max(16, m) * 1024 * 1024;
}
const vram = parseInt(query_args["vram"], 10);
if(vram > 0)
{
settings.vga_memory_size = vram * 1024 * 1024;
}
settings.networking_proxy = query_args["networking_proxy"];
settings.audio = query_args["audio"] !== "0";
settings.acpi = query_args["acpi"];
for(var i = 0; i < oses.length; i++)
{
var infos = oses[i];
if(profile === infos.id)
if(profile === os.id)
{
start_profile(infos);
start_emulation(os, query_args);
return;
}
var element = $("start_" + infos.id);
const element = $("start_" + os.id);
if(element)
{
element.onclick = function(infos, element, e)
element.onclick = e =>
{
e.preventDefault();
set_profile(infos.id);
element.blur();
start_profile(infos);
}.bind(this, infos, element);
start_emulation(os, null);
};
}
}
if(profile === "custom")
{
if(query_args["hda.url"])
// TODO: if one of the file form fields has a value (firefox), start here?
if(query_args.has("hda.url") || query_args.has("cdrom.url") || query_args.has("fda.url"))
{
settings.hda = {
size: parseInt(query_args["hda.size"], 10) || undefined,
url: query_args["hda.url"],
async: true,
};
start_emulation(null, query_args);
}
if(query_args["cdrom.url"])
else
{
settings.cdrom = {
size: parseInt(query_args["cdrom.size"], 10) || undefined,
url: query_args["cdrom.url"],
async: true,
};
}
if(query_args["fda.url"])
{
settings.fda = {
size: parseInt(query_args["fda.size"], 10) || undefined,
url: query_args["fda.url"],
async: false,
};
}
if(settings.fda || settings.cdrom || settings.hda)
{
$("boot_options").style.display = "none";
start_emulation(settings, done);
return;
// TODO: fill input fields with query arg values?
}
}
else if(/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_]+$/g.test(profile))
@ -1479,7 +1348,7 @@
return o && { url: base + "/" + o["url"], async: o["async"], size: o["size"] };
}
start_profile({
const profile = {
id: p["id"],
name: p["name"],
memory_size: p["memory_size"],
@ -1492,124 +1361,10 @@
multiboot: handle_image(p["multiboot"]),
bzimage: handle_image(p["bzimage"]),
initrd: handle_image(p["initrd"]),
});
};
start_emulation(profile, query_args);
});
return;
}
for(const id of elements_to_restore)
{
const saved_value = window.localStorage.getItem(id);
if(saved_value)
{
const element = $(id);
if(element)
{
if(element.type === "checkbox")
{
element.checked = saved_value === "true" ? true : false;
}
else
{
element.value = saved_value;
}
}
}
}
if(DEBUG && ON_LOCALHOST)
{
// don't use online relay in debug mode
$("networking_proxy").value = "ws://localhost:8080/";
}
function start_profile(infos)
{
$("boot_options").style.display = "none";
set_title(infos.name);
settings.filesystem = infos.filesystem;
if(infos.state)
{
$("reset").style.display = "none";
settings.initial_state = infos.state;
}
settings.fda = infos.fda;
settings.cdrom = infos.cdrom;
settings.hda = infos.hda;
settings.multiboot = infos.multiboot;
settings.bzimage = infos.bzimage;
settings.initrd = infos.initrd;
settings.cmdline = infos.cmdline;
settings.bzimage_initrd_from_filesystem = infos.bzimage_initrd_from_filesystem;
settings.mac_address_translation = infos.mac_address_translation;
settings.cpuid_level = infos.cpuid_level;
settings.acpi = (!infos.state && settings.acpi !== undefined) ? settings.acpi : infos.acpi;
settings.memory_size = (!infos.state && settings.memory_size) ? settings.memory_size : infos.memory_size;
settings.vga_memory_size = (!infos.state && settings.vga_memory_size) ? settings.vga_memory_size : infos.vga_memory_size;
settings.disable_vga_display = infos.disable_vga_display;
settings.id = infos.id;
if(infos.boot_order !== undefined)
{
settings.boot_order = infos.boot_order;
}
let chunk_size = parseInt(query_args["chunk_size"], 10);
if(chunk_size >= 0)
{
if(chunk_size)
{
chunk_size = Math.min(4 * 1024 * 1024, Math.max(512, chunk_size));
chunk_size = 1 << Math.ceil(Math.log2(chunk_size));
}
else
{
chunk_size = undefined;
}
if(settings.hda)
{
settings.hda.fixed_chunk_size = chunk_size;
}
if(settings.cdrom)
{
settings.cdrom.fixed_chunk_size = chunk_size;
}
}
if(!DEBUG && infos.homepage)
{
$("description").style.display = "block";
const link = document.createElement("a");
link.href = infos.homepage;
link.textContent = infos.name;
link.target = "_blank";
$("description").appendChild(document.createTextNode("Running "));
$("description").appendChild(link);
}
start_emulation(settings, done);
}
function done(emulator)
{
if(query_args["c"])
{
setTimeout(function()
{
//emulator.serial0_send(query_args["c"] + "\n");
emulator.keyboard_send_text(query_args["c"] + "\n");
}, 25);
}
}
}
@ -1617,58 +1372,59 @@
{
// called on window.onload, in debug mode
var log_levels = $("log_levels");
const log_levels = $("log_levels");
if(log_levels)
if(!log_levels)
{
for(var i = 0; i < LOG_NAMES.length; i++)
return;
}
for(let i = 0; i < LOG_NAMES.length; i++)
{
const mask = LOG_NAMES[i][0];
if(mask === 1)
continue;
const name = LOG_NAMES[i][1].toLowerCase();
const input = document.createElement("input");
const label = document.createElement("label");
input.type = "checkbox";
label.htmlFor = input.id = "log_" + name;
if(LOG_LEVEL & mask)
{
var mask = LOG_NAMES[i][0];
input.checked = true;
}
input.mask = mask;
if(mask === 1)
continue;
label.append(input, v86util.pads(name, 4) + " ");
log_levels.appendChild(label);
var name = LOG_NAMES[i][1].toLowerCase(),
input = document.createElement("input"),
label = document.createElement("label");
if(i === Math.floor(LOG_NAMES.length / 2))
{
log_levels.append("\n");
}
}
input.type = "checkbox";
log_levels.onchange = function(e)
{
const target = e.target;
const mask = target.mask;
label.htmlFor = input.id = "log_" + name;
if(LOG_LEVEL & mask)
{
input.checked = true;
}
input.mask = mask;
label.appendChild(input);
label.appendChild(document.createTextNode(v86util.pads(name, 4) + " "));
log_levels.appendChild(label);
if(i === Math.floor(LOG_NAMES.length / 2))
{
log_levels.appendChild(document.createTextNode("\n"));
}
if(target.checked)
{
LOG_LEVEL |= mask;
}
else
{
LOG_LEVEL &= ~mask;
}
log_levels.onchange = function(e)
{
var target = e.target,
mask = target.mask;
if(target.checked)
{
LOG_LEVEL |= mask;
}
else
{
LOG_LEVEL &= ~mask;
}
target.blur();
};
}
target.blur();
};
}
window.addEventListener("load", onload, false);
@ -1689,145 +1445,269 @@
onload();
}
/** @param {?=} done */
function start_emulation(settings, done)
// we can get here in various ways:
// - the user clicked on the "start emulation"
// - the user clicked on a profile
// - the ?profile= query parameter specified a valid profile
// - the ?profile= query parameter was set to "custom" and at least one disk image was given
function start_emulation(profile, query_args)
{
/** @const */
var MB = 1024 * 1024;
$("boot_options").style.display = "none";
var memory_size = settings.memory_size;
if(!memory_size)
if(!query_args)
{
memory_size = parseInt($("memory_size").value, 10) * MB;
set_profile(profile?.id || "custom");
}
if(!memory_size)
const settings = {};
if(profile)
{
if(profile.state)
{
alert("Invalid memory size - reset to 128MB");
memory_size = 128 * MB;
$("reset").style.display = "none";
}
set_title(profile.name);
settings.initial_state = profile.state;
settings.filesystem = profile.filesystem;
settings.fda = profile.fda;
settings.cdrom = profile.cdrom;
settings.hda = profile.hda;
settings.multiboot = profile.multiboot;
settings.bzimage = profile.bzimage;
settings.initrd = profile.initrd;
settings.cmdline = profile.cmdline;
settings.bzimage_initrd_from_filesystem = profile.bzimage_initrd_from_filesystem;
settings.mac_address_translation = profile.mac_address_translation;
settings.cpuid_level = profile.cpuid_level;
settings.acpi = profile.acpi;
settings.memory_size = profile.memory_size;
settings.vga_memory_size = profile.vga_memory_size;
settings.boot_order = profile.boot_order;
if(!DEBUG && profile.homepage)
{
$("description").style.display = "block";
const link = document.createElement("a");
link.href = profile.homepage;
link.textContent = profile.name;
link.target = "_blank";
$("description").append(document.createTextNode("Running "), link);
}
}
var vga_memory_size = settings.vga_memory_size;
if(!vga_memory_size)
if(query_args)
{
vga_memory_size = parseInt($("video_memory_size").value, 10) * MB;
if(!vga_memory_size)
// ignore certain settings when using a state image
if(!settings.initial_state)
{
alert("Invalid video memory size - reset to 8MB");
vga_memory_size = 8 * MB;
let chunk_size = parseInt(query_args.get("chunk_size"), 10);
if(chunk_size >= 0)
{
chunk_size = Math.min(4 * 1024 * 1024, Math.max(512, chunk_size));
chunk_size = v86util.round_up_to_next_power_of_2(chunk_size);
}
else
{
chunk_size = 256 * 1024;
}
if(query_args.has("hda.url"))
{
settings.hda = {
size: parseInt(query_args.get("hda.size"), 10) || undefined,
// TODO: synchronous if small?
url: query_args.get("hda.url"),
fixed_chunk_size: chunk_size,
async: true,
};
}
if(query_args.has("cdrom.url"))
{
settings.cdrom = {
size: parseInt(query_args.get("cdrom.size"), 10) || undefined,
url: query_args.get("cdrom.url"),
fixed_chunk_size: chunk_size,
async: true,
};
}
if(query_args.has("fda.url"))
{
settings.fda = {
size: parseInt(query_args.get("fda.size"), 10) || undefined,
url: query_args.get("fda.url"),
async: false,
};
}
const m = parseInt(query_args.get("m"), 10);
if(m > 0)
{
settings.memory_size = Math.max(16, m) * 1024 * 1024;
}
const vram = parseInt(query_args.get("vram"), 10);
if(vram > 0)
{
settings.vga_memory_size = vram * 1024 * 1024;
}
settings.acpi = query_args.has("acpi") ? !!query_args.get("acpi") : undefined;
settings.use_bochs_bios = !!query_args.get("use_bochs_bios");
}
settings.disable_jit = !!query_args.get("disable_jit");
settings.networking_proxy = query_args.get("networking_proxy");
settings.audio = query_args.get("audio") && query_args.get("audio") !== "0";
}
if(!settings.bios)
if(!settings.networking_proxy)
{
var bios = $("bios").files[0];
if(bios)
settings.networking_proxy = $("networking_proxy").value;
}
settings.disable_audio = $("disable_audio").checked || !settings.audio;
// some settings cannot be overridden when a state image is used
if(!settings.initial_state)
{
const bios = $("bios").files[0];
if(bios && !settings.initial_state)
{
settings.bios = { buffer: bios };
}
}
if(!settings.fda)
{
var floppy_file = $("floppy_image").files[0];
if(floppy_file)
const fda = $("floppy_image").files[0];
if(fda)
{
settings.fda = { buffer: floppy_file };
settings.fda = { buffer: fda };
}
}
if(!settings.bzimage)
{
var bzimage = $("bzimage").files[0];
const cdrom = $("cdrom_image").files[0];
if(cdrom)
{
settings.cdrom = { buffer: cdrom };
}
const hda = $("hda_image").files[0];
if(hda)
{
settings.hda = { buffer: hda };
}
const hdb = $("hdb_image")?.files[0];
if(hdb)
{
settings.hdb = { buffer: hdb };
}
const multiboot = $("multiboot_image")?.files[0];
if(multiboot)
{
settings.multiboot = { buffer: multiboot };
}
const bzimage = $("bzimage").files[0];
if(bzimage)
{
settings.bzimage = { buffer: bzimage };
}
}
if(!settings.initrd)
{
var initrd = $("initrd").files[0];
const initrd = $("initrd").files[0];
if(initrd)
{
settings.initrd = { buffer: initrd };
}
const title = multiboot?.name || hda?.name || cdrom?.name || hdb?.name || fda?.name || bios?.name;
if(title)
{
set_title(title);
}
const MB = 1024 * 1024;
if(!settings.memory_size)
{
let memory_size = parseInt($("memory_size").value, 10) * MB;
if(!memory_size)
{
alert("Invalid memory size - reset to 128MB");
memory_size = 128 * MB;
}
settings.memory_size = memory_size;
}
if(!settings.vga_memory_size)
{
let vga_memory_size = parseInt($("video_memory_size").value, 10) * MB;
if(!vga_memory_size)
{
alert("Invalid video memory size - reset to 8MB");
vga_memory_size = 8 * MB;
}
settings.vga_memory_size = vga_memory_size;
}
if(!settings.boot_order)
{
settings.boot_order = parseInt($("boot_order").value, 16) || 0;
}
if(settings.acpi === undefined)
{
settings.acpi = $("enable_acpi").checked;
}
if(!settings.bios)
{
const BIOSPATH = "bios/";
if(settings.use_bochs_bios)
{
var biosfile = "bochs-bios.bin";
var vgabiosfile = "bochs-vgabios.bin";
}
else
{
var biosfile = DEBUG ? "seabios-debug.bin" : "seabios.bin";
var vgabiosfile = DEBUG ? "vgabios-debug.bin" : "vgabios.bin";
}
settings.bios = { url: BIOSPATH + biosfile };
settings.vga_bios = { url: BIOSPATH + vgabiosfile };
}
}
const networking_proxy = settings.networking_proxy === undefined ? $("networking_proxy").value : settings.networking_proxy;
const disable_audio = $("disable_audio").checked || !settings.audio;
const enable_acpi = !settings.initial_state && settings.acpi === undefined ? $("enable_acpi").checked : settings.acpi;
/** @const */
var BIOSPATH = "bios/";
if(settings.use_bochs_bios)
{
var biosfile = "bochs-bios.bin";
var vgabiosfile = "bochs-vgabios.bin";
}
else
{
var biosfile = DEBUG ? "seabios-debug.bin" : "seabios.bin";
var vgabiosfile = DEBUG ? "vgabios-debug.bin" : "vgabios.bin";
}
var bios;
var vga_bios;
// a bios is only needed if the machine is booted
if(!settings.initial_state)
{
bios = {
url: BIOSPATH + biosfile,
};
vga_bios = {
url: BIOSPATH + vgabiosfile,
};
}
var emulator = new V86({
memory_size: memory_size,
vga_memory_size: vga_memory_size,
screen_container: settings.disable_vga_display ? null : $("screen_container"),
boot_order: settings.boot_order || parseInt($("boot_order").value, 16) || 0,
const emulator = new V86({
screen_container: $("screen_container"),
net_device: {
type: "ne2k",
relay_url: networking_proxy,
relay_url: settings.networking_proxy,
},
autostart: true,
bios: settings.bios || bios,
vga_bios: settings.bios ? null : vga_bios,
memory_size: settings.memory_size,
vga_memory_size: settings.vga_memory_size,
boot_order: settings.boot_order,
bios: settings.bios,
vga_bios: settings.vga_bios,
fda: settings.fda,
hda: settings.hda,
hdb: settings.hdb,
cdrom: settings.cdrom,
multiboot: settings.multiboot,
bzimage: settings.bzimage,
initrd: settings.initrd,
cmdline: settings.cmdline,
bzimage_initrd_from_filesystem: settings.bzimage_initrd_from_filesystem,
acpi: enable_acpi,
acpi: settings.acpi,
disable_jit: settings.disable_jit,
initial_state: settings.initial_state,
filesystem: settings.filesystem || {},
disable_speaker: disable_audio,
disable_speaker: settings.disable_audio,
mac_address_translation: settings.mac_address_translation,
cpuid_level: settings.cpuid_level,
autostart: true,
});
if(DEBUG) window["emulator"] = emulator;
if(DEBUG) window.emulator = emulator;
emulator.add_listener("emulator-ready", function()
{
@ -1840,7 +1720,7 @@
{
const CLEAR_STATS = false;
var panel = document.createElement("pre");
const panel = document.createElement("pre");
document.body.appendChild(panel);
setInterval(function()
@ -1857,7 +1737,7 @@
}, CLEAR_STATS ? 5000 : 1000);
}
if(settings.id === "dsl" || settings.id === "helenos" || settings.id === "android" || settings.id === "android4")
if(["dsl", "helenos", "android", "android4"].includes(profile?.id))
{
setTimeout(() => {
// hack: Start automatically
@ -1867,7 +1747,21 @@
init_ui(settings, emulator);
done && done(emulator);
if(query_args?.has("c"))
{
setTimeout(function()
{
emulator.keyboard_send_text(query_args.get("c") + "\n");
}, 25);
}
if(query_args?.has("s"))
{
setTimeout(function()
{
emulator.serial0_send(query_args.get("s") + "\n");
}, 25);
}
});
emulator.add_listener("download-progress", function(e)
@ -1877,10 +1771,9 @@
emulator.add_listener("download-error", function(e)
{
var el = $("loading");
const el = $("loading");
el.style.display = "block";
el.textContent = "Loading " + e.file_name + " failed. Check your connection " +
"and reload the page to try again.";
el.textContent = `Loading ${e.file_name} failed. Check your connection and reload the page to try again.`;
});
}
@ -1890,11 +1783,10 @@
*/
function init_ui(settings, emulator)
{
$("boot_options").style.display = "none";
$("loading").style.display = "none";
$("runtime_options").style.display = "block";
$("runtime_infos").style.display = "block";
if(!settings.disable_vga_display) $("screen_container").style.display = "block";
$("screen_container").style.display = "block";
if(settings.filesystem)
{
@ -1952,7 +1844,6 @@
$("toggle_mouse").blur();
};
var last_tick = 0;
var running_time = 0;
var last_instr_counter = 0;
@ -2547,7 +2438,6 @@
}, 1000);
// helps debugging
window.emulator = emulator;
window.cpu = cpu;
window.dump_file = dump_file;
}