feat(userspace): programs in /bin w/o .elf; bin manifests (needs = display) so shell runs snake; Ctrl+C kills foreground via terminal interrupt notification; ctrl keys; sleep

This commit is contained in:
2026-10-27 21:16:00 +01:00
parent 1fc5a54f06
commit 83809bbe94
33 changed files with 608 additions and 121 deletions

View File

@@ -55,18 +55,21 @@ pub const CELL_HEIGHT: usize = 16;
/// arrive; read them with `poll_input`.
pub fn subscribe_input(ep: CapSlot) -> Option<CapSlot> {
use types::badge::Badge;
use types::permission::{Permission, PermissionSet};
let notify = syscall::create_notification().ok()?;
subscribe(ep, notify, Badge::NONE).then_some(notify)
}
/// Takes input focus, to be told through `notify` with `badge`'s bits.
pub fn subscribe(ep: CapSlot, notify: CapSlot, badge: types::badge::Badge) -> bool {
use types::permission::{Permission, PermissionSet};
// Signal only (Delegate just to send it; the transfer strips it).
let signal = PermissionSet::empty().with(Permission::Write).with(Permission::Delegate);
let copy = syscall::mint(notify, Badge::NONE, signal).ok()?;
let sent = syscall::call(
ep,
[types::display::input::SUBSCRIBE, 0, 0, 0],
Some(copy.into()),
);
let Ok(copy) = syscall::mint(notify, badge, signal) else {
return false;
};
let sent = syscall::call(ep, [types::display::input::SUBSCRIBE, 0, 0, 0], Some(copy.into()));
syscall::drop_cap(copy);
(sent.ok()?.words()[0] == 0).then_some(notify)
sent.is_ok_and(|r| r.words()[0] == 0)
}
/// The next input event, if this process has focus and one is waiting.

View File

@@ -57,6 +57,12 @@ impl Spawned {
syscall::delegate(cap, self.process, perms.without(Permission::Delegate))
}
/// Like `give`, but an endpoint the program may send capabilities
/// through (which takes Delegate, so it could pass the endpoint on too).
pub fn give_sending(&self, endpoint: CapSlot, perms: PermissionSet) -> Option<CapSlot> {
syscall::delegate(endpoint, self.process, perms.with(Permission::Delegate))
}
/// Puts `args` (its name first) where the program finds them: a page
/// this process fills and then moves into the program, which owns it.
pub fn set_args(&self, args: &[&str]) -> bool {

View File

@@ -38,6 +38,11 @@ pub fn storage() -> Option<CapSlot> {
StartArg::decode(arg()).storage
}
/// The display server endpoint this process was given, if any.
pub fn display() -> Option<CapSlot> {
StartArg::decode(arg()).display
}
/// This program's arguments, its name first; none without an args page.
pub fn args() -> impl Iterator<Item = &'static str> {
let page: &'static [u8] = if StartArg::decode(arg()).args {

View File

@@ -27,3 +27,22 @@ pub fn read_line(terminal: CapSlot, buf: &mut [u8]) -> usize {
pub fn clear(terminal: CapSlot) {
let _ = syscall::request(terminal, [types::terminal::CLEAR, 0, 0, 0]);
}
/// Repaints the terminal and gives it input focus again, after a program
/// that drew on the screen.
pub fn redraw(terminal: CapSlot) {
let _ = syscall::request(terminal, [types::terminal::REDRAW, 0, 0, 0]);
}
/// Has the terminal signal `notify` with `badge`'s bits when Ctrl+C is
/// typed while no line is being read. Only the first caller is heard.
pub fn on_interrupt(terminal: CapSlot, notify: CapSlot, badge: types::badge::Badge) -> bool {
use types::permission::{Permission, PermissionSet};
let signal = PermissionSet::empty().with(Permission::Write).with(Permission::Delegate);
let Ok(copy) = syscall::mint(notify, badge, signal) else {
return false;
};
let sent = syscall::call(terminal, [types::terminal::ON_INTERRUPT, 0, 0, 0], Some(copy.into()));
syscall::drop_cap(copy);
sent.is_ok_and(|r| r.words()[0] == 0)
}