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:
@@ -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
|
||||
|
||||
@@ -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),
|
||||
];
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user