From 5558e3509d020a0e021a77f0617044497b2f2aee Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 17 Aug 2026 18:56:04 +0200 Subject: Windowed GUI: wm module, input routing, and the paint app - Add the wm window-manager facility per ADR 0004: overlapping pixel-surface windows, damage-rectangle compositing, WM-drawn title bar, focus/minimize/close, and F1-F4 keybindings - Add gop shadow compositing primitives and vt::repaint_region so the console stays layer 0 beneath windows - Route mouse (absolute coords, press/release edges) and keyboard (focus-aware input pump) to the focused window - Add the rspaint drawing-program crate (pen, 8 colors, clear) and shell draw/windows/restore commands - Extend TaskMsg with AppMouse/Focus/Close and document the design in CONTEXT.md + ADR 0004 --- shell/src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 10 deletions(-) (limited to 'shell/src/lib.rs') diff --git a/shell/src/lib.rs b/shell/src/lib.rs index bb048d8..f1addd9 100644 --- a/shell/src/lib.rs +++ b/shell/src/lib.rs @@ -53,14 +53,15 @@ pub fn run() -> ! { term_println("!!! PREVIOUS CRASH REPORT:"); let mut buf = [0u8; 256]; if let Ok(n) = rsnix::fs::read(h, &mut buf, 0) - && n > 0 { - let s = core::str::from_utf8(&buf[..n]).unwrap_or("?"); - for line in s.split('\n') { - if !line.is_empty() { - term_println(line); - } + && n > 0 + { + let s = core::str::from_utf8(&buf[..n]).unwrap_or("?"); + for line in s.split('\n') { + if !line.is_empty() { + term_println(line); } } + } term_println("!!! end of crash report"); } @@ -739,6 +740,9 @@ write `{} TIL `", "touch" => cmd_touch(&parsed), "rm" => cmd_rm(&parsed), "edit" => cmd_edit(&parsed), + "draw" => cmd_draw(&parsed), + "windows" => cmd_windows(&parsed), + "restore" => cmd_restore(&parsed), "bg" => cmd_bg(&parsed), "jobs" => cmd_jobs(&parsed), "msgrecv" => cmd_msgrecv(&parsed), @@ -1387,6 +1391,40 @@ fn cmd_edit(p: &Parsed) { rsnix::gop::clear_screen(); } +fn cmd_draw(_p: &Parsed) { + match rsnix::sched::spawn(rspaint::run, "paint") { + Some(id) => term_println(&alloc::format!( + "draw: paint task {} — F4 or the close button to close", + id + )), + None => term_err("draw: task table full"), + } +} + +fn cmd_windows(_p: &Parsed) { + let list = rsnix::wm::list(); + if list.is_empty() { + term_println("windows: none (console only)"); + return; + } + for (id, title, minimized) in list { + term_println(&alloc::format!( + " {} {}{}", + id, + title, + if minimized { " (minimized)" } else { "" } + )); + } +} + +fn cmd_restore(p: &Parsed) { + let Some(id) = p.args.first().and_then(|s| s.parse::().ok()) else { + term_err("usage: restore "); + return; + }; + rsnix::wm::restore(id); +} + fn cmd_mem(p: &Parsed) { let (map, key) = rsnix::memory::get_map(); @@ -1602,14 +1640,19 @@ fn cmd_heapdbg(_p: &Parsed) { let ov = h.heap_start < fb.saturating_add(fsz) && fb < h.heap_end; term_println(&alloc::format!( "fb {:#x} size={:#x} overlap-heap={}", - fb, fsz, ov + fb, + fsz, + ov )); } let probe = h.heap_start.wrapping_add(0x443e0); // historical crash offset match rsnix::memory::freelist_contains(probe) { Some((b, s, off)) => term_println(&alloc::format!( "probe {:#x} (+0x443e0) IN free blk {:#x} size={:#x} off={:#x}", - probe, b, s, off + probe, + b, + s, + off )), None => term_println(&alloc::format!("probe {:#x} (+0x443e0) NOT free", probe)), } @@ -1682,7 +1725,9 @@ fn cmd_heapdbg(_p: &Parsed) { let (rc, rr) = rsnix::vt::cell_raw(tr, tc); term_println(&alloc::format!( "shadow@{:x} cell={:08x} rendered={:08x}", - f, rc, rr + f, + rc, + rr )); } } @@ -1697,7 +1742,9 @@ fn cmd_heapdbg(_p: &Parsed) { match rsnix::memory::freelist_contains(b) { Some((fb, fs, fo)) => term_println(&alloc::format!( "shadow base IN free list: blk {:#x} size={:#x} off={:#x}", - fb, fs, fo + fb, + fs, + fo )), None => term_println("shadow base NOT in free list"), } -- cgit v1.2.3