diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-27 17:34:10 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-27 17:34:10 +0200 |
| commit | 81fee80d8aaf6ee6b95ca20a30eac5d0a41d4d79 (patch) | |
| tree | 033ebeeb60b876ac91247b68e133ca36d6e9e0ee | |
| parent | GUI: window/event exports, paint binary, event-loop gate (diff) | |
| download | rs-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.rs | 46 |
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). |
| 1255 | fn exec_external(p: &Parsed) -> bool { | 1255 | fn 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 | |||
| 1269 | fn 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 | |||
| 1302 | fn 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(_) => { |
