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

@@ -12,8 +12,10 @@ mod tests;
use libkos::display::{self, CELL_HEIGHT, CELL_WIDTH};
use libkos::input::{InputEvent, Key};
use libkos::syscall;
use types::badge::Badge;
use types::bounded::CapSlot;
use types::display::{Color, ScreenCoord};
use types::terminal::{KEYS_BIT, TIMER_BIT};
/// Where init put them (its manifest: own endpoint, then `uses`).
const EP: CapSlot = CapSlot::const_new(0);
@@ -26,11 +28,23 @@ const BG: Color = Color::rgb(16, 16, 24);
const FLUSH_DELAY: u64 = 20 * 10_000;
struct Screen {
keys: CapSlot,
/// Bound to the serving thread; the flush timer signals it.
/// Bound to the serving thread: the display signals it with
/// `KEYS_BIT`, the flush timer with `TIMER_BIT`.
notify: CapSlot,
timer: CapSlot,
}
/// A key as the line editor wants it, if it is one.
fn byte(event: InputEvent) -> Option<u8> {
match event {
InputEvent::KeyDown(Key::Char(c)) => Some(c),
InputEvent::KeyDown(Key::Space | Key::Tab) => Some(b' '),
InputEvent::KeyDown(Key::Enter) => Some(b'\n'),
InputEvent::KeyDown(Key::Backspace) => Some(server::BACKSPACE),
_ => None,
}
}
fn at(col: usize, row: usize) -> (ScreenCoord, ScreenCoord) {
(ScreenCoord::new((col * CELL_WIDTH) as u16), ScreenCoord::new((row * CELL_HEIGHT) as u16))
}
@@ -57,34 +71,50 @@ impl server::Console for Screen {
fn next_key(&mut self) -> u8 {
loop {
while let Some(event) = display::poll_input(DISPLAY) {
let InputEvent::KeyDown(key) = event else { continue };
match key {
Key::Char(c) => return c,
Key::Space | Key::Tab => return b' ',
Key::Enter => return b'\n',
Key::Backspace => return server::BACKSPACE,
_ => {}
}
if let Some(key) = self.poll_key() {
return key;
}
let _ = syscall::wait(self.keys);
let _ = syscall::wait(self.notify);
}
}
fn poll_key(&mut self) -> Option<u8> {
while let Some(event) = display::poll_input(DISPLAY) {
if let Some(key) = byte(event) {
return Some(key);
}
}
None
}
fn take_focus(&mut self) {
display::subscribe(DISPLAY, self.notify, Badge::new(KEYS_BIT));
display::clear(DISPLAY, BG);
}
fn interrupt(&mut self, notification: CapSlot) {
let _ = syscall::signal(notification);
}
}
pub fn run() -> ! {
let Some(keys) = display::subscribe_input(DISPLAY) else {
let Some(notify) = syscall::create_notification().ok().filter(|n| syscall::bind_notification(*n).is_ok()) else {
libkos::println!("terminal: no notification, exiting");
syscall::exit();
};
let timer_perms = types::permission::PermissionSet::empty().with(types::permission::Permission::Write);
let Ok(timer) = syscall::mint(notify, Badge::new(TIMER_BIT), timer_perms) else {
libkos::println!("terminal: no timer, exiting");
syscall::exit();
};
if !display::subscribe(DISPLAY, notify, Badge::new(KEYS_BIT)) {
libkos::println!("terminal: no input focus, exiting");
syscall::exit();
};
let Some(timer) = syscall::create_notification().ok().filter(|t| syscall::bind_notification(*t).is_ok()) else {
libkos::println!("terminal: no timer notification, exiting");
syscall::exit();
};
}
let (w, h) = display::get_dimensions(DISPLAY);
display::clear(DISPLAY, BG);
let cols = w.raw() as usize / CELL_WIDTH;
let rows = h.raw() as usize / CELL_HEIGHT;
let mut srv = server::TerminalServer::new(Screen { keys, timer }, cols, rows);
let mut srv = server::TerminalServer::new(Screen { notify, timer }, cols, rows);
server_framework::serve(&mut srv, EP)
}