#![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 { 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 { 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 { 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) }