feat(display): windows: full-screen, F1 cycles, OS bar (names, front, user); canvas-only (display-owned buffers, 4 max, reused); launchers OPEN (pm per app, terminal own), window caps minted by display; FORGET on launcher death via init courier; raster crate (font, chunked text); F-keys in input; terminal+snake on canvas; REDRAW gone

This commit is contained in:
2026-10-28 02:48:39 +01:00
parent d04dcfffa1
commit 864f039bc7
45 changed files with 1382 additions and 824 deletions

View File

@@ -63,6 +63,9 @@ pub(crate) struct Launched {
/// to take back when it dies.
pub(crate) grants: [Option<CapSlot>; 2],
pub(crate) spawn: Option<Badge>,
/// The badge on its copy of the display, if it has one: the windows it
/// opened close when it goes.
pub(crate) display_badge: Option<usize>,
}
impl<'a> Launcher<'a> {
@@ -101,7 +104,7 @@ impl<'a> Launcher<'a> {
(self.storage, self.scratch)
}
fn endpoint(&self, name: &str) -> Option<CapSlot> {
pub(crate) fn endpoint(&self, name: &str) -> Option<CapSlot> {
self.served.iter().flatten().find(|(n, _)| *n == name).map(|(_, ep)| *ep)
}
@@ -117,8 +120,10 @@ impl<'a> Launcher<'a> {
Some(ep) => ep,
None => syscall::create_endpoint().ok_or("no endpoint")?,
};
// Read to receive on it; init keeps the original.
let server = perms(&[Permission::Read, Permission::Execute]);
// Read to receive on it; Delegate to mint badged copies of
// its own (storage's grants, the display's windows); init
// keeps the original.
let server = perms(&[Permission::Read, Permission::Execute, Permission::Delegate]);
syscall::delegate(ep, child, server).ok_or("cannot give its endpoint")?;
Some(ep)
}
@@ -127,10 +132,14 @@ impl<'a> Launcher<'a> {
// A client calls (Execute) and may send capabilities through it and
// pass it on (Delegate); never Read, so it cannot pose as the server
// and take other clients' requests.
let mut display_badge = None;
for name in p.uses {
let ep = self.endpoint(name).ok_or("needs a server that is not running")?;
let badge = Badge::new(self.next_badge);
self.next_badge += 1;
if *name == "display" {
display_badge = Some(badge.raw());
}
let copy = syscall::mint(ep, badge, perms(&[Permission::Execute, Permission::Delegate]))
.map_err(|_| "cannot mint an endpoint")?;
let given = syscall::delegate(copy, child, perms(&[Permission::Execute, Permission::Delegate]));
@@ -192,7 +201,7 @@ impl<'a> Launcher<'a> {
note = ", console handed over";
}
}
Ok(Launched { note, process: child, ep, grants, spawn })
Ok(Launched { note, process: child, ep, grants, spawn, display_badge })
}
/// The path on disk `scope` names: inside the user's folder if it is a

View File

@@ -103,12 +103,16 @@ pub const SERVERS: &[Program] = &[
uses: &["input"],
// Its clients subscribe and take focus.
restarts_clients: true,
// The framebuffer alone is ~1000 pages at 1280x800.
..server("display", b"bin/display", Limits { pages: 1280, regions: 4, ..SMALL })
// Its windows' state (input queues among it) is KiBs.
stack_pages: 8,
// The framebuffer is ~1000 pages at 1280x800, and so is each of
// the (at most 4) windows' pixel buffers, which it owns.
..server("display", b"bin/display", Limits { pages: 5200, regions: 8, ..SMALL })
},
// Text on the display, lines from the keyboard (it takes input focus).
// Its clients wait on lines and Ctrl+C.
Program { uses: &["display"], restarts_clients: true, ..server("terminal", b"bin/terminal", SMALL) },
// Its clients wait on lines and Ctrl+C. It draws its own text now,
// beside a screenful of grid.
Program { uses: &["display"], restarts_clients: true, stack_pages: 8, ..server("terminal", b"bin/terminal", SMALL) },
server("session", b"bin/session", SMALL),
];

View File

@@ -2,7 +2,8 @@
//! server, so a blocking call to it could wait on itself. Text goes into a
//! ring; a thread of init's own takes it to the log and waits there when
//! the log is down, instead of init. Until that thread runs (early boot),
//! init prints to the console directly.
//! init prints to the console directly. The same thread carries the few
//! messages init sends other servers it restarts (`post`).
use core::fmt;
use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
@@ -20,6 +21,31 @@ static LOST: AtomicUsize = AtomicUsize::new(0);
/// The printer's notification, once it runs.
static WAKE: AtomicUsize = AtomicUsize::new(usize::MAX);
/// Messages waiting: an endpoint slot and four words each.
const POSTS: usize = 16;
static POSTED: [[AtomicUsize; 5]; POSTS] = [const { [const { AtomicUsize::new(0) }; 5] }; POSTS];
static POST_HEAD: AtomicUsize = AtomicUsize::new(0);
static POST_TAIL: AtomicUsize = AtomicUsize::new(0);
/// Has `words` called to `ep` by the printer thread, so init never waits
/// on it; dropped (and said so) if too many are waiting.
pub(crate) fn post(ep: CapSlot, words: [usize; 4]) {
let tail = POST_TAIL.load(Ordering::Relaxed);
if tail - POST_HEAD.load(Ordering::Acquire) == POSTS || wake().is_none() {
crate::outln!("init: a message to a server was dropped");
return;
}
let slot = &POSTED[tail % POSTS];
slot[0].store(ep.value() as usize, Ordering::Relaxed);
for (w, v) in slot[1..].iter().zip(words) {
w.store(v, Ordering::Relaxed);
}
POST_TAIL.store(tail + 1, Ordering::Release);
if let Some(wake) = wake() {
let _ = syscall::signal(wake);
}
}
fn wake() -> Option<CapSlot> {
u8::try_from(WAKE.load(Ordering::Acquire)).ok().and_then(CapSlot::new)
}
@@ -87,6 +113,17 @@ fn printer(_: &'static AtomicUsize) -> ! {
if lost > 0 {
libkos::println!("init: {} bytes of output lost while the log was down", lost);
}
while POST_HEAD.load(Ordering::Relaxed) != POST_TAIL.load(Ordering::Acquire) {
let head = POST_HEAD.load(Ordering::Relaxed);
let slot = &POSTED[head % POSTS];
let words = core::array::from_fn(|i| slot[i + 1].load(Ordering::Relaxed));
let ep = u8::try_from(slot[0].load(Ordering::Relaxed)).ok().and_then(CapSlot::new);
POST_HEAD.store(head + 1, Ordering::Release);
if let Some(ep) = ep {
// Waits while that server is down; init goes on.
let _ = syscall::request(ep, words);
}
}
}
}

View File

@@ -108,6 +108,9 @@ pub(crate) fn sign_in(l: &mut Launcher) -> Result<(), &'static str> {
crate::factory::prepare(fs, folder, scratch)?;
let name = core::str::from_utf8(&h.name[..h.name_len]).unwrap_or("someone");
crate::outln!("init: {} is in", name);
if let Some(display) = l.endpoint("display") {
crate::output::post(display, libkos::display::set_user_words(name.as_bytes()));
}
l.user = Some(User { folder, guest });
start_session(l);
Ok(())
@@ -129,6 +132,9 @@ fn start_session(l: &mut Launcher) {
pub(crate) fn sign_out(l: &mut Launcher) {
l.spawner.clear();
l.stop_session();
if let Some(display) = l.endpoint("display") {
crate::output::post(display, libkos::display::set_user_words(b""));
}
let user = l.user.take();
let (fs, _) = l.storage();
let locked = fs.lock().is_ok();

View File

@@ -83,14 +83,23 @@ pub(crate) struct Supervised {
ep: Option<CapSlot>,
/// Killed for hanging: the watchdog's ping is queued on its endpoint.
hung: bool,
/// The badge on its copy of the display: its windows close with it.
display_badge: Option<usize>,
}
/// What a program held through init, to take back when it goes.
struct Held {
grants: [Option<CapSlot>; 2],
spawn: Option<Badge>,
display_badge: Option<usize>,
}
impl Supervised {
/// What this run held through init, the first time only: by the time
/// its death is heard, a slot it freed may hold someone else's
/// capability.
fn take_held(&mut self) -> Option<([Option<CapSlot>; 2], Option<Badge>)> {
let held = (self.grants, self.spawn);
fn take_held(&mut self) -> Option<Held> {
let held = Held { grants: self.grants, spawn: self.spawn, display_badge: self.display_badge };
self.grants = [None; 2];
self.spawn = None;
core::mem::take(&mut self.holding).then_some(held)
@@ -128,6 +137,7 @@ impl Launcher<'_> {
holding: true,
ep,
hung: false,
display_badge: started.display_badge,
});
self.ping(index);
Ok(note)
@@ -168,8 +178,12 @@ impl Launcher<'_> {
/// Takes back what supervised program `index` held through init.
fn let_go(&mut self, index: usize) {
// Never a call to a server: it may be the one that is down.
let Some((grants, spawn)) = self.supervised[index].as_mut().and_then(Supervised::take_held) else { return };
self.release(grants, spawn);
let Some(held) = self.supervised[index].as_mut().and_then(Supervised::take_held) else { return };
self.release(held.grants, held.spawn);
// The windows it opened (the terminal's, its apps') close.
if let (Some(badge), Some(display)) = (held.display_badge, self.endpoint("display")) {
crate::output::post(display, libkos::display::forget_words(badge));
}
}
/// Has the watchdog ping program `index`, if it is to be pinged.
@@ -306,6 +320,7 @@ impl Launcher<'_> {
spawn: started.spawn,
holding: true,
ep: started.ep,
display_badge: started.display_badge,
..s
});
self.ping(index);