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:
@@ -12,3 +12,5 @@ bench = false
|
||||
types = { workspace = true }
|
||||
libkos = { workspace = true }
|
||||
server-framework = { workspace = true }
|
||||
port-framebuffer = { workspace = true }
|
||||
raster = { workspace = true }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(&[]);
|
||||
|
||||
Reference in New Issue
Block a user