Files
k-os/crates/servers/terminal-server/src/lib.rs

132 lines
4.2 KiB
Rust

#![no_std]
#![forbid(unsafe_code)]
extern crate alloc;
pub mod grid;
pub mod line;
pub mod server;
#[cfg(test)]
#[path = "tests/mod.rs"]
mod tests;
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;
use types::display::{Color, Dimension, 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);
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.
const FLUSH_DELAY: u64 = 20 * types::time::TICKS_PER_MS;
struct Screen {
/// The terminal's window, all of which scrolls.
canvas: Canvas,
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))
}
impl server::Console for Screen {
fn draw_row(&mut self, row: usize, text: &[u8]) {
let (x, y) = at(0, row);
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);
raster::draw_text(&mut self.canvas, x, y, BG, FG, &[under]);
}
fn flush(&mut self) {
self.canvas.flush();
}
fn scroll(&mut self, rows: usize) {
let by = Dimension::new(u16::try_from(rows * CELL_HEIGHT).unwrap_or(u16::MAX));
let height = self.canvas.height();
self.canvas.scroll_up(ScreenCoord::new(0), height, by, BG);
}
fn flush_later(&mut self) {
let at = syscall::get_time().raw() + FLUSH_DELAY;
let _ = syscall::set_timer(self.timer, types::time::Ticks::from_raw(at));
}
fn poll_key(&mut self) -> Option<u8> {
while let Some(event) = display::poll_input(self.canvas.window()) {
if let Some(key) = byte(event) {
return Some(key);
}
}
None
}
fn save_reply(&mut self) -> Option<types::bounded::SavedReply> {
syscall::save_reply().ok()
}
fn answer(&mut self, reply: types::bounded::SavedReply, words: [usize; 4]) {
let _ = syscall::reply_saved(reply, words, None);
}
fn interrupt(&mut self, notification: CapSlot) {
let _ = syscall::signal(notification);
}
fn forget(&mut self, notification: CapSlot) {
syscall::drop_cap(notification);
}
}
pub fn run() -> ! {
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();
};
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();
}
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)
}