ui and stuff

This commit is contained in:
Alessandro Mauri 2025-10-27 11:52:07 +01:00
parent 4def485a6d
commit 60e77cd611
5 changed files with 186 additions and 36 deletions

View File

@ -15,6 +15,6 @@
}
},
"safe": true,
"opt": "O1",
"opt": "O0",
"debug-info": "full"
}

View File

@ -9,7 +9,6 @@ div {
separator {
bg: #fbf1c7ff;
size: 1;
padding: 0 5 0 5;
}
button {

View File

@ -5,9 +5,17 @@ import std::os::env;
import std::core::mem::allocator;
import std::collections::object;
import std::collections::list;
import std::encoding::json;
struct ConfigFile {
Path path;
String name;
}
alias StrList = list::List{String};
alias ConfFList = list::List{ConfigFile};
fn Path? String.to_expanded_path(&str, Allocator allocator)
{
@ -46,40 +54,75 @@ fn Path? String.to_expanded_path(&str, Allocator allocator)
};
}
fn StrList get_qemu_cmdline(Allocator allocator, Object* conf)
fn StrList? get_qemu_cmdline(Allocator allocator, Object* conf)
{
// parse the disk path
String disk = conf.get_string("disk")!!;
Path disk_path = disk.to_expanded_path(mem)!!;
defer disk_path.free();
// compose the command line for the vm
StrList cmd;
cmd.init(allocator);
@pool() {
// parse the disk path
String disk = conf.get_string("disk")!;
Path disk_path = disk.to_expanded_path(tmem)!;
// first the executable
cmd.push(conf.get_string("qemu"))!!;
// then the drive
cmd.push("-drive");
cmd.push(string::format(allocator, "file=%s", disk_path.str_view()));
//cmd.push(string::format(allocator, "file=%s,format=%s", disk_path.str_view(), disk_path.extension()))!!;
// memory
cmd.push("-m");
cmd.push(conf.get_string("memory"))!!;
// processors
cmd.push("-smp");
cmd.push(conf.get_string("processors") ?? "1");
// then all the parameters
Object* parameters = conf.get("parameters")!!;
for (usz i = 0; i < parameters.get_len(); i++) {
Object* p = parameters.get_at(i);
if (p.is_string()) {
cmd.push(string::format(allocator, "-%s", p.s));
} else if (p.is_indexable()) {
cmd.push(string::format(allocator, "-%s", p.get_string_at(0)))!!;
cmd.push(p.get_string_at(1))!!;
// compose the command line for the vm
cmd.init(allocator);
// first the executable
cmd.push(conf.get_string("qemu"))!!;
// then the drive
cmd.push("-drive");
cmd.push(string::format(allocator, "file=%s", disk_path.str_view()));
// memory
cmd.push("-m");
cmd.push(conf.get_string("memory"))!!;
// processors
cmd.push("-smp");
cmd.push(conf.get_string("processors") ?? "1");
// then all the parameters
Object* parameters = conf.get("parameters")!!;
for (usz i = 0; i < parameters.get_len(); i++) {
Object* p = parameters.get_at(i);
if (p.is_string()) {
cmd.push(string::format(allocator, "-%s", p.s));
} else if (p.is_indexable()) {
cmd.push(string::format(allocator, "-%s", p.get_string_at(0)))!!;
cmd.push(p.get_string_at(1))!!;
}
}
}
};
return cmd;
}
fn ConfFList? get_config_list(Allocator allocator)
{
ConfFList l;
l.init(allocator);
@pool() {
// find the right config directory
Path conf_dir = env::get_config_dir(tmem).append(tmem, "simple-qemu-manager")!;
if (!file::exists(conf_dir.str_view()) || !file::is_dir(conf_dir.str_view())) {
path::mkdir(conf_dir, recursive:true)!;
return l;
}
// list all json files
PathList pl = path::ls(tmem, conf_dir)!;
foreach (fname : pl) @pool() {
if ((fname.extension() ?? "") != "json") continue;
Path fpath = conf_dir.append(tmem, fname.str_view())!;
File cf = file::open_path(fpath, "r")!;
defer (void)cf.close();
Object* c = json::parse(tmem, &cf)!;
ConfigFile e = {
.path = path::new(allocator, fpath.str_view(), fpath.env)!,
.name = string::format(allocator, "%s", c.get_string("name"))!,
};
l.push(e);
};
};
return l;
}

View File

@ -28,7 +28,7 @@ fn int main(String[] args)
Object* conf = json::parse(mem, &file)!!;
defer conf.free();
// UI initialization
// ---------------------------------------------- UI initialization ---------------------------------------------- //
ArenaAllocator arena;
char[] arena_mem = mem::new_array(char, 1024*1024);
defer (void)mem::free(arena_mem);
@ -59,9 +59,9 @@ fn int main(String[] args)
ui.import_style_from_file(STYLESHEET_PATH);
ren::pre(ren.win);
// End UI initialization
// -------------------------------------------- End UI initialization -------------------------------------------- //
StrList cmd = conf::get_qemu_cmdline(mem, conf);
StrList cmd = conf::get_qemu_cmdline(mem, conf)!!;
defer cmd.free();
String vm_name = conf.get_string("name")!!;
String vm_disk = conf.get_string("disk").to_expanded_path(tmem).str_view()!!;
@ -71,6 +71,11 @@ fn int main(String[] args)
SubProcess vm_proc;
defer (void)vm_proc.join();
ConfFList conf_list = conf::get_config_list(tmem)!!;
foreach (cf : conf_list) {
io::printn(cf);
}
bool quit;
Clock sleep_clock;
while (!quit) {
@ -84,10 +89,23 @@ fn int main(String[] args)
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true;
ui.@div(ugui::@grow(), ugui::@grow(), COLUMN) {
ui.@div(ugui::@grow(), ugui::@fit(20)) {
ui.@div(ugui::@grow(), ugui::@fit(20), ROW, LEFT) {
ui.text("Machine:")!!;
ui.separator(ugui::@grow(), ugui::@grow())!!;
ui.text(vm_name)!!;
static bool popup;
static Point popup_pos;
if (ui.button("v")!!.mouse_release) {
popup = true;
popup_pos = ui.input.mouse.pos;
}
ui.@popup(&popup, popup_pos, ugui::@fit(), ugui::@fit(), COLUMN) {
foreach (idx, c : conf_list) {
ui.text(c.name, idx)!!;
}
}!!;
}!!;
ui.hor_line()!!;
ui.@div(ugui::@grow(), ugui::@grow(), COLUMN, scroll_y: true) {

90
src/vm.c3 Normal file
View File

@ -0,0 +1,90 @@
import std::io;
import std::io::path;
import std::io::file;
import std::os::process;
import std::collections::object;
import std::encoding::json;
import conf;
struct VirtualMachineDesc {
Path config_path;
Object* config;
String name;
StrList cmdline;
Path disk_path;
SubProcess process;
}
fn VirtualMachineDesc? new_from_path(Allocator allocator, Path path)
{
VirtualMachineDesc desc;
desc.config_path = path::new(allocator, path.str_view(), path.env)!;
File file = file::open_path(path, "r")!;
defer (void)file.close();
desc.config = json::parse(allocator, &file)!;
desc.name = desc.config.get_string("name")!;
desc.disk_path = desc.config.get_string("disk").to_expanded_path(allocator)!;
desc.cmdline = conf::get_qemu_cmdline(allocator, desc.config)!;
return desc;
}
<* @param[&in] desc *>
fn bool VirtualMachineDesc.is_initialized(&desc)
{
return desc.config_path.str_view() != ""
&& desc.config != null
&& desc.name != ""
&& desc.disk_path.str_view() != ""
&& desc.cmdline.len() != 0;
}
<* @param[&inout] desc *>
fn void VirtualMachineDesc.free(&desc)
{
if (!desc.is_initialized()) return;
(void)desc.stop();
desc.config_path.free();
desc.config.free();
// desc.name.free() name is just a string view into desc.config
desc.disk_path.free();
desc.cmdline.free();
*desc = {};
}
<* @param[&inout] desc *>
fn void? VirtualMachineDesc.start(&desc)
{
if (!desc.is_initialized()) return;
if ((desc.process.is_running() ?? false)) return;
desc.process = process::create(desc.cmdline.array_view(), {.inherit_stdio=true, .inherit_environment=true})!;
}
<* @param[&inout] desc *>
fn void? VirtualMachineDesc.stop(&desc)
{
if (!desc.is_initialized()) return;
if (!(desc.process.is_running() ?? false)) return;
desc.process.terminate()!;
desc.process.join()!;
}
<* @param[&in] desc *>
fn bool VirtualMachineDesc.is_running(&desc)
{
if (!desc.is_initialized()) return false;
return desc.process.is_running() ?? false;
}