summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-27 17:34:10 +0200
committersalmonik <salmonik@tuta.io>2026-08-27 17:34:10 +0200
commit81fee80d8aaf6ee6b95ca20a30eac5d0a41d4d79 (patch)
tree033ebeeb60b876ac91247b68e133ca36d6e9e0ee
parentGUI: window/event exports, paint binary, event-loop gate (diff)
downloadrs-dos-81fee80d8aaf6ee6b95ca20a30eac5d0a41d4d79.tar.gz
rs-dos-81fee80d8aaf6ee6b95ca20a30eac5d0a41d4d79.zip
GUI: fire-and-forget spawn for /bin/gui binaries
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.
-rw-r--r--shell/src/lib.rs46
1 files changed, 45 insertions, 1 deletions
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 {
1253/// Run `cmd` as an external PE32+ binary from /bin/cli/. Returns false 1253/// Run `cmd` as an external PE32+ binary from /bin/cli/. Returns false
1254/// when no external binary resolved (the caller falls back to built-ins). 1254/// when no external binary resolved (the caller falls back to built-ins).
1255fn exec_external(p: &Parsed) -> bool { 1255fn exec_external(p: &Parsed) -> bool {
1256 let Some(h) = rsnix::exec::resolve("cli", &p.cmd) else { 1256 // Try CLI first (blocking), then GUI (fire-and-forget).
1257 let cli_h = rsnix::exec::resolve("cli", &p.cmd);
1258 let gui_h = if cli_h.is_none() { rsnix::exec::resolve("gui", &p.cmd) } else { None };
1259
1260 if let Some(h) = cli_h {
1261 exec_cli(p, h)
1262 } else if let Some(h) = gui_h {
1263 exec_gui(p, h)
1264 } else {
1257 return false; 1265 return false;
1266 }
1267}
1268
1269fn exec_gui(p: &Parsed, h: usize) -> bool {
1270 // Fire-and-forget: spawn the GUI binary, return immediately.
1271 let bytes = match rsnix::exec::read_file(h) {
1272 Ok(b) => b,
1273 Err(_) => {
1274 term_err(&alloc::format!("{}: read failed", p.cmd));
1275 return true;
1276 }
1277 };
1278 let image = match rsnix::load::load_image(&bytes) {
1279 Ok(img) => img,
1280 Err(e) => {
1281 term_err(&alloc::format!("{}: cannot load ({:?})", p.cmd, e));
1282 return true;
1283 }
1258 }; 1284 };
1285 let mut argv: Vec<String> = Vec::with_capacity(p.args.len() + 1);
1286 argv.push(p.cmd.clone());
1287 argv.extend(p.args.iter().cloned());
1288 let name = alloc::format!("{}", p.cmd);
1289 let exports = Box::leak(Box::new(build_exports()));
1290 match rsnix::exec::spawn(image, exports, &argv, &name) {
1291 Ok(_id) => {
1292 // Fire-and-forget: do NOT wait for completion.
1293 // The binary runs its own event loop; when it exits,
1294 // the task is cleaned up by the scheduler.
1295 term_println(&alloc::format!("{}: started", p.cmd));
1296 }
1297 Err(_) => term_err(&alloc::format!("{}: cannot start", p.cmd)),
1298 }
1299 true
1300}
1301
1302fn exec_cli(p: &Parsed, h: usize) -> bool {
1259 let bytes = match rsnix::exec::read_file(h) { 1303 let bytes = match rsnix::exec::read_file(h) {
1260 Ok(b) => b, 1304 Ok(b) => b,
1261 Err(_) => { 1305 Err(_) => {