summaryrefslogtreecommitdiffstats
path: root/shell
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-17 18:56:04 +0200
committersalmonik <salmonik@tuta.io>2026-08-17 18:56:04 +0200
commit5558e3509d020a0e021a77f0617044497b2f2aee (patch)
treecd7b6cee9f7c327f9fae7ad9ac27602d1a97b222 /shell
parentUSB2/1.1 (EHCI/UHCI) drivers + real-hardware bring-up hardening (diff)
downloadrs-dos-5558e3509d020a0e021a77f0617044497b2f2aee.tar.gz
rs-dos-5558e3509d020a0e021a77f0617044497b2f2aee.zip
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
Diffstat (limited to '')
-rw-r--r--shell/Cargo.toml1
-rw-r--r--shell/src/lib.rs67
2 files changed, 58 insertions, 10 deletions
diff --git a/shell/Cargo.toml b/shell/Cargo.toml
index bc35524..f95b574 100644
--- a/shell/Cargo.toml
+++ b/shell/Cargo.toml
@@ -9,3 +9,4 @@ workspace = true
9[dependencies] 9[dependencies]
10rsnix = { path = "../rsnix" } 10rsnix = { path = "../rsnix" }
11rsdosedit = { path = "../rsdosedit" } 11rsdosedit = { path = "../rsdosedit" }
12rspaint = { path = "../rspaint" }
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() -> ! {
53 term_println("!!! PREVIOUS CRASH REPORT:"); 53 term_println("!!! PREVIOUS CRASH REPORT:");
54 let mut buf = [0u8; 256]; 54 let mut buf = [0u8; 256];
55 if let Ok(n) = rsnix::fs::read(h, &mut buf, 0) 55 if let Ok(n) = rsnix::fs::read(h, &mut buf, 0)
56 && n > 0 { 56 && n > 0
57 let s = core::str::from_utf8(&buf[..n]).unwrap_or("?"); 57 {
58 for line in s.split('\n') { 58 let s = core::str::from_utf8(&buf[..n]).unwrap_or("?");
59 if !line.is_empty() { 59 for line in s.split('\n') {
60 term_println(line); 60 if !line.is_empty() {
61 } 61 term_println(line);
62 } 62 }
63 } 63 }
64 }
64 term_println("!!! end of crash report"); 65 term_println("!!! end of crash report");
65 } 66 }
66 67
@@ -739,6 +740,9 @@ write `{} <path> TIL <marker>`",
739 "touch" => cmd_touch(&parsed), 740 "touch" => cmd_touch(&parsed),
740 "rm" => cmd_rm(&parsed), 741 "rm" => cmd_rm(&parsed),
741 "edit" => cmd_edit(&parsed), 742 "edit" => cmd_edit(&parsed),
743 "draw" => cmd_draw(&parsed),
744 "windows" => cmd_windows(&parsed),
745 "restore" => cmd_restore(&parsed),
742 "bg" => cmd_bg(&parsed), 746 "bg" => cmd_bg(&parsed),
743 "jobs" => cmd_jobs(&parsed), 747 "jobs" => cmd_jobs(&parsed),
744 "msgrecv" => cmd_msgrecv(&parsed), 748 "msgrecv" => cmd_msgrecv(&parsed),
@@ -1387,6 +1391,40 @@ fn cmd_edit(p: &Parsed) {
1387 rsnix::gop::clear_screen(); 1391 rsnix::gop::clear_screen();
1388} 1392}
1389 1393
1394fn cmd_draw(_p: &Parsed) {
1395 match rsnix::sched::spawn(rspaint::run, "paint") {
1396 Some(id) => term_println(&alloc::format!(
1397 "draw: paint task {} — F4 or the close button to close",
1398 id
1399 )),
1400 None => term_err("draw: task table full"),
1401 }
1402}
1403
1404fn cmd_windows(_p: &Parsed) {
1405 let list = rsnix::wm::list();
1406 if list.is_empty() {
1407 term_println("windows: none (console only)");
1408 return;
1409 }
1410 for (id, title, minimized) in list {
1411 term_println(&alloc::format!(
1412 " {} {}{}",
1413 id,
1414 title,
1415 if minimized { " (minimized)" } else { "" }
1416 ));
1417 }
1418}
1419
1420fn cmd_restore(p: &Parsed) {
1421 let Some(id) = p.args.first().and_then(|s| s.parse::<usize>().ok()) else {
1422 term_err("usage: restore <id>");
1423 return;
1424 };
1425 rsnix::wm::restore(id);
1426}
1427
1390fn cmd_mem(p: &Parsed) { 1428fn cmd_mem(p: &Parsed) {
1391 let (map, key) = rsnix::memory::get_map(); 1429 let (map, key) = rsnix::memory::get_map();
1392 1430
@@ -1602,14 +1640,19 @@ fn cmd_heapdbg(_p: &Parsed) {
1602 let ov = h.heap_start < fb.saturating_add(fsz) && fb < h.heap_end; 1640 let ov = h.heap_start < fb.saturating_add(fsz) && fb < h.heap_end;
1603 term_println(&alloc::format!( 1641 term_println(&alloc::format!(
1604 "fb {:#x} size={:#x} overlap-heap={}", 1642 "fb {:#x} size={:#x} overlap-heap={}",
1605 fb, fsz, ov 1643 fb,
1644 fsz,
1645 ov
1606 )); 1646 ));
1607 } 1647 }
1608 let probe = h.heap_start.wrapping_add(0x443e0); // historical crash offset 1648 let probe = h.heap_start.wrapping_add(0x443e0); // historical crash offset
1609 match rsnix::memory::freelist_contains(probe) { 1649 match rsnix::memory::freelist_contains(probe) {
1610 Some((b, s, off)) => term_println(&alloc::format!( 1650 Some((b, s, off)) => term_println(&alloc::format!(
1611 "probe {:#x} (+0x443e0) IN free blk {:#x} size={:#x} off={:#x}", 1651 "probe {:#x} (+0x443e0) IN free blk {:#x} size={:#x} off={:#x}",
1612 probe, b, s, off 1652 probe,
1653 b,
1654 s,
1655 off
1613 )), 1656 )),
1614 None => term_println(&alloc::format!("probe {:#x} (+0x443e0) NOT free", probe)), 1657 None => term_println(&alloc::format!("probe {:#x} (+0x443e0) NOT free", probe)),
1615 } 1658 }
@@ -1682,7 +1725,9 @@ fn cmd_heapdbg(_p: &Parsed) {
1682 let (rc, rr) = rsnix::vt::cell_raw(tr, tc); 1725 let (rc, rr) = rsnix::vt::cell_raw(tr, tc);
1683 term_println(&alloc::format!( 1726 term_println(&alloc::format!(
1684 "shadow@{:x} cell={:08x} rendered={:08x}", 1727 "shadow@{:x} cell={:08x} rendered={:08x}",
1685 f, rc, rr 1728 f,
1729 rc,
1730 rr
1686 )); 1731 ));
1687 } 1732 }
1688 } 1733 }
@@ -1697,7 +1742,9 @@ fn cmd_heapdbg(_p: &Parsed) {
1697 match rsnix::memory::freelist_contains(b) { 1742 match rsnix::memory::freelist_contains(b) {
1698 Some((fb, fs, fo)) => term_println(&alloc::format!( 1743 Some((fb, fs, fo)) => term_println(&alloc::format!(
1699 "shadow base IN free list: blk {:#x} size={:#x} off={:#x}", 1744 "shadow base IN free list: blk {:#x} size={:#x} off={:#x}",
1700 fb, fs, fo 1745 fb,
1746 fs,
1747 fo
1701 )), 1748 )),
1702 None => term_println("shadow base NOT in free list"), 1749 None => term_println("shadow base NOT in free list"),
1703 } 1750 }