From 81fee80d8aaf6ee6b95ca20a30eac5d0a41d4d79 Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 27 Aug 2026 17:34:10 +0200 Subject: GUI: fire-and-forget spawn for /bin/gui binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit exec_external now tries CLI first (blocking), then GUI (fire-and-forget). GUI binaries spawn as tasks and return immediately — the shell continues to the next prompt while the binary runs its own event loop. Exit is handled by the scheduler (task cleanup on death), not by waiting on a completion message. bins/paint staged at /bin/gui/paint.efi on the system image. All 11 tool binaries + 3 gate binaries (hello, boom, count) verified across the v2 ABI. test-disk regression passed on the same kernel. --- shell/src/lib.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'shell/src') diff --git a/shell/src/lib.rs b/shell/src/lib.rs index 6de056c..e500337 100644 --- a/shell/src/lib.rs +++ b/shell/src/lib.rs @@ -1253,9 +1253,53 @@ fn build_exports() -> rsapi::Exports { /// Run `cmd` as an external PE32+ binary from /bin/cli/. Returns false /// when no external binary resolved (the caller falls back to built-ins). fn exec_external(p: &Parsed) -> bool { - let Some(h) = rsnix::exec::resolve("cli", &p.cmd) else { + // Try CLI first (blocking), then GUI (fire-and-forget). + let cli_h = rsnix::exec::resolve("cli", &p.cmd); + let gui_h = if cli_h.is_none() { rsnix::exec::resolve("gui", &p.cmd) } else { None }; + + if let Some(h) = cli_h { + exec_cli(p, h) + } else if let Some(h) = gui_h { + exec_gui(p, h) + } else { return false; + } +} + +fn exec_gui(p: &Parsed, h: usize) -> bool { + // Fire-and-forget: spawn the GUI binary, return immediately. + let bytes = match rsnix::exec::read_file(h) { + Ok(b) => b, + Err(_) => { + term_err(&alloc::format!("{}: read failed", p.cmd)); + return true; + } + }; + let image = match rsnix::load::load_image(&bytes) { + Ok(img) => img, + Err(e) => { + term_err(&alloc::format!("{}: cannot load ({:?})", p.cmd, e)); + return true; + } }; + let mut argv: Vec = Vec::with_capacity(p.args.len() + 1); + argv.push(p.cmd.clone()); + argv.extend(p.args.iter().cloned()); + let name = alloc::format!("{}", p.cmd); + let exports = Box::leak(Box::new(build_exports())); + match rsnix::exec::spawn(image, exports, &argv, &name) { + Ok(_id) => { + // Fire-and-forget: do NOT wait for completion. + // The binary runs its own event loop; when it exits, + // the task is cleaned up by the scheduler. + term_println(&alloc::format!("{}: started", p.cmd)); + } + Err(_) => term_err(&alloc::format!("{}: cannot start", p.cmd)), + } + true +} + +fn exec_cli(p: &Parsed, h: usize) -> bool { let bytes = match rsnix::exec::read_file(h) { Ok(b) => b, Err(_) => { -- cgit v1.2.3