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

@@ -11,8 +11,9 @@ pub mod server;
#[path = "tests/mod.rs"]
mod tests;
use libkos::display::{self, CELL_HEIGHT, CELL_WIDTH};
use libkos::display::{self, Canvas};
use libkos::input::{InputEvent, Key};
use port_framebuffer::Framebuffer;
use libkos::syscall;
use types::badge::Badge;
use types::bounded::CapSlot;
@@ -23,6 +24,9 @@ use types::terminal::{KEYS_BIT, TIMER_BIT};
const EP: CapSlot = CapSlot::const_new(0);
const DISPLAY: CapSlot = CapSlot::const_new(1);
const CELL_WIDTH: usize = raster::font::WIDTH as usize;
const CELL_HEIGHT: usize = raster::font::HEIGHT as usize;
const FG: Color = Color::rgb(220, 220, 220);
const BG: Color = Color::rgb(16, 16, 24);
/// Output is shown at most this long after it is written. The timebase is
@@ -30,12 +34,9 @@ const BG: Color = Color::rgb(16, 16, 24);
const FLUSH_DELAY: u64 = 20 * 10_000;
struct Screen {
/// Bound to the serving thread: the display signals it with
/// `KEYS_BIT`, the flush timer with `TIMER_BIT`.
notify: CapSlot,
/// The terminal's window, all of which scrolls.
canvas: Canvas,
timer: CapSlot,
/// The screen's height, all of which scrolls.
height: Dimension,
}
/// A key as the line editor wants it, if it is one.
@@ -56,21 +57,22 @@ fn at(col: usize, row: usize) -> (ScreenCoord, ScreenCoord) {
impl server::Console for Screen {
fn draw_row(&mut self, row: usize, text: &[u8]) {
let (x, y) = at(0, row);
display::draw_text(DISPLAY, x, y, FG, BG, text);
raster::draw_text(&mut self.canvas, x, y, FG, BG, text);
}
fn draw_cursor(&mut self, col: usize, row: usize, under: u8) {
let (x, y) = at(col, row);
display::draw_text(DISPLAY, x, y, BG, FG, &[under]);
raster::draw_text(&mut self.canvas, x, y, BG, FG, &[under]);
}
fn flush(&mut self) {
display::flush(DISPLAY);
self.canvas.flush();
}
fn scroll(&mut self, rows: usize) {
let by = Dimension::new(u16::try_from(rows * CELL_HEIGHT).unwrap_or(u16::MAX));
display::scroll(DISPLAY, ScreenCoord::new(0), self.height, by, BG);
let height = self.canvas.height();
self.canvas.scroll_up(ScreenCoord::new(0), height, by, BG);
}
fn flush_later(&mut self) {
@@ -79,7 +81,7 @@ impl server::Console for Screen {
}
fn poll_key(&mut self) -> Option<u8> {
while let Some(event) = display::poll_input(DISPLAY) {
while let Some(event) = display::poll_input(self.canvas.window()) {
if let Some(key) = byte(event) {
return Some(key);
}
@@ -95,11 +97,6 @@ impl server::Console for Screen {
let _ = syscall::reply_saved(reply, words, 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);
}
@@ -119,14 +116,17 @@ pub fn run() -> ! {
libkos::println!("terminal: no timer, exiting");
syscall::exit();
};
if !display::subscribe(DISPLAY, notify, Badge::new(KEYS_BIT)) {
libkos::println!("terminal: no input focus, exiting");
let Some(mut canvas) = display::open(DISPLAY, b"Terminal").and_then(|w| Canvas::attach(w.window)) else {
libkos::println!("terminal: no window, exiting");
syscall::exit();
};
if !display::subscribe(canvas.window(), notify, Badge::new(KEYS_BIT)) {
libkos::println!("terminal: no input, 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 { notify, timer, height: h }, cols, rows);
canvas.clear(BG);
let cols = canvas.width().raw() as usize / CELL_WIDTH;
let rows = canvas.height().raw() as usize / CELL_HEIGHT;
let mut srv = server::TerminalServer::new(Screen { canvas, timer }, cols, rows);
server_framework::serve(&mut srv, EP)
}

View File

@@ -24,8 +24,6 @@ pub trait Console {
/// Keeps the caller of the request being handled, to answer later.
fn save_reply(&mut self) -> Option<SavedReply>;
fn answer(&mut self, reply: SavedReply, words: [usize; 4]);
/// Takes input focus back from whoever took it, and clears the screen.
fn take_focus(&mut self);
fn interrupt(&mut self, notification: CapSlot);
/// Lets go of a notification it was given.
fn forget(&mut self, notification: CapSlot);
@@ -259,15 +257,6 @@ impl<C: Console> Server for TerminalServer<C> {
// Kept for later: what goes back now is not sent.
types::terminal::READ => self.read(false).unwrap_or([0; 4]),
types::terminal::READ_SECRET => self.read(true).unwrap_or([0; 4]),
types::terminal::REDRAW => {
// Taking focus back clears the screen.
self.console.take_focus();
self.drawn = [0; crate::grid::MAX_ROWS];
self.grid.touch_all();
self.redraw();
self.console.flush();
[0; 4]
}
types::terminal::ON_INTERRUPT => match cap {
Some(n) if badge & types::terminal::CONTROL != 0 => {
if let Some(old) = self.on_interrupt.replace(n) {

View File

@@ -15,7 +15,6 @@ struct Fake {
typed: Vec<u8>,
flushes: usize,
timers: usize,
focus_taken: usize,
scrolled: usize,
/// Every row drawn, with how many cells.
rows_drawn: Vec<(usize, usize)>,
@@ -52,9 +51,6 @@ impl Console for Fake {
fn answer(&mut self, reply: SavedReply, words: [usize; 4]) {
self.answered.push((reply, words));
}
fn take_focus(&mut self) {
self.focus_taken += 1;
}
fn interrupt(&mut self, notification: CapSlot) {
self.interrupted.push(notification);
}
@@ -149,15 +145,6 @@ fn ctrl_c_drops_the_line_being_typed() {
assert_eq!(line_of(&mut t), b"ls\n");
}
#[test]
fn redraw_takes_focus_back_and_paints_every_row() {
let mut t = term(&[]);
t.handle_call(1, text_words(b"hi"), None);
t.handle_call(1, [types::terminal::REDRAW, 0, 0, 0], None);
assert_eq!(t.console().focus_taken, 1);
assert_eq!(&t.grid().row(0)[..2], b"hi");
}
#[test]
fn a_full_screen_scrolls_its_pixels_and_draws_one_row() {
let mut t = term(&[]);