132 lines
4.2 KiB
Rust
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)
|
|
}
|