diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 840f030..c1a17bd 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -123,6 +123,7 @@ version = "0.1.0" dependencies = [ "mammoth", "victoriafalls", + "voyageurs", "yellowstone", ] @@ -149,6 +150,17 @@ dependencies = [ "yunqc", ] +[[package]] +name = "voyageurs" +version = "0.1.0" +dependencies = [ + "mammoth", + "yellowstone", + "yunq", + "yunq-derive", + "yunqc", +] + [[package]] name = "yellowstone" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index eb3c943..24f2adc 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = [ - "lib/mammoth", "lib/victoriafalls", "lib/yellowstone", "lib/yunq", "lib/yunq-derive", "sys/teton", "usr/testbed", + "lib/mammoth", "lib/victoriafalls", "lib/voyageurs", "lib/yellowstone", "lib/yunq", "lib/yunq-derive", "sys/teton", "usr/testbed", ] resolver = "2" diff --git a/rust/lib/mammoth/src/lib.rs b/rust/lib/mammoth/src/lib.rs index 3cc833d..f58e774 100644 --- a/rust/lib/mammoth/src/lib.rs +++ b/rust/lib/mammoth/src/lib.rs @@ -10,6 +10,7 @@ pub mod macros; pub mod init; pub mod mem; +pub mod port; pub mod syscall; pub mod thread; pub mod zion; diff --git a/rust/lib/mammoth/src/port.rs b/rust/lib/mammoth/src/port.rs new file mode 100644 index 0000000..bd25405 --- /dev/null +++ b/rust/lib/mammoth/src/port.rs @@ -0,0 +1,42 @@ +use crate::syscall::{cap_duplicate, cap_release, port_create, port_recv}; +use crate::zion::{kZionPerm_Read, z_cap_t, ZError}; + +pub struct PortServer { + port_cap: z_cap_t, +} + +impl PortServer { + pub fn new() -> Result { + Ok(Self { + port_cap: port_create()?, + }) + } + + pub fn create_client_cap(&self) -> Result { + cap_duplicate(self.port_cap, !kZionPerm_Read) + } + + pub fn recv_byte(&self) -> Result { + let mut caps: [z_cap_t; 0] = []; + let mut bytes: [u8; 1] = [0]; + + port_recv(self.port_cap, &mut bytes, &mut caps)?; + + Ok(bytes[0]) + } + + pub fn recv_u16(&self) -> Result { + let mut caps: [z_cap_t; 0] = []; + let mut bytes: [u8; 2] = [0; 2]; + + port_recv(self.port_cap, &mut bytes, &mut caps)?; + + Ok(u16::from_le_bytes(bytes)) + } +} + +impl Drop for PortServer { + fn drop(&mut self) { + cap_release(self.port_cap).expect("Failed to release port cap"); + } +} diff --git a/rust/lib/mammoth/src/syscall.rs b/rust/lib/mammoth/src/syscall.rs index a41b761..d0ea030 100644 --- a/rust/lib/mammoth/src/syscall.rs +++ b/rust/lib/mammoth/src/syscall.rs @@ -140,6 +140,37 @@ pub fn address_space_unmap(lower_addr: u64, upper_addr: u64) -> Result<(), ZErro ) } +pub fn port_create() -> Result { + let mut port_cap = 0; + syscall( + zion::kZionPortCreate, + &zion::ZPortCreateReq { + port_cap: &mut port_cap, + }, + )?; + Ok(port_cap) +} + +pub fn port_recv( + port_cap: z_cap_t, + bytes: &mut [u8], + caps: &mut [u64], +) -> Result<(u64, u64), ZError> { + let mut num_bytes = bytes.len() as u64; + let mut num_caps = caps.len() as u64; + syscall( + zion::kZionPortRecv, + &zion::ZPortRecvReq { + port_cap, + data: bytes.as_mut_ptr() as *mut c_void, + num_bytes: &mut num_bytes as *mut u64, + caps: caps.as_mut_ptr(), + num_caps: &mut num_caps as *mut u64, + }, + )?; + Ok((num_bytes, num_caps)) +} + pub fn port_poll( port_cap: z_cap_t, bytes: &mut [u8], @@ -248,6 +279,19 @@ pub fn reply_port_recv( Ok((num_bytes, num_caps)) } +pub fn cap_duplicate(cap: z_cap_t, perm_mask: u64) -> Result { + let mut new_cap = 0; + syscall( + zion::kZionCapDuplicate, + &zion::ZCapDuplicateReq { + cap_in: cap, + perm_mask, + cap_out: &mut new_cap, + }, + )?; + Ok(new_cap) +} + pub fn cap_release(cap: z_cap_t) -> Result<(), ZError> { syscall(zion::kZionCapRelease, &zion::ZCapReleaseReq { cap }) } diff --git a/rust/lib/voyageurs/Cargo.toml b/rust/lib/voyageurs/Cargo.toml new file mode 100644 index 0000000..a007692 --- /dev/null +++ b/rust/lib/voyageurs/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "voyageurs" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../mammoth" } +yellowstone = { path = "../yellowstone" } +yunq = {path = "../yunq"} +yunq-derive = {path = "../yunq-derive"} + +[build-dependencies] +yunqc = {path = "../../../yunq/rust"} + diff --git a/rust/lib/voyageurs/build.rs b/rust/lib/voyageurs/build.rs new file mode 100644 index 0000000..52b5dfc --- /dev/null +++ b/rust/lib/voyageurs/build.rs @@ -0,0 +1,14 @@ +use std::fs; + +fn main() { + let input_file = "../../../sys/voyageurs/lib/voyageurs/voyageurs.yunq"; + + println!("cargo::rerun-if-changed={input_file}"); + + let input = fs::read_to_string(input_file).expect("Failed to read input file"); + + let code = yunqc::codegen(&input).expect("Failed to generate yunq code."); + + let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs"; + fs::write(out, code).expect("Failed to write generated code."); +} diff --git a/rust/lib/voyageurs/src/lib.rs b/rust/lib/voyageurs/src/lib.rs new file mode 100644 index 0000000..e92646c --- /dev/null +++ b/rust/lib/voyageurs/src/lib.rs @@ -0,0 +1,8 @@ +#![no_std] +#![feature(box_into_inner)] + +use core::include; + +include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +pub mod listener; diff --git a/rust/lib/voyageurs/src/listener.rs b/rust/lib/voyageurs/src/listener.rs new file mode 100644 index 0000000..a64626e --- /dev/null +++ b/rust/lib/voyageurs/src/listener.rs @@ -0,0 +1,262 @@ +use core::cell::RefCell; + +use alloc::boxed::Box; +use alloc::rc::Rc; +use alloc::string::ToString; +use mammoth::port::PortServer; +use mammoth::thread::Thread; +use mammoth::zion::ZError; + +#[repr(u8)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +enum Keycode { + UnknownKeycode = 0x0, + + A = 0x1, + B = 0x2, + C = 0x3, + D = 0x4, + E = 0x5, + F = 0x6, + G = 0x7, + H = 0x8, + I = 0x9, + J = 0xA, + K = 0xB, + L = 0xC, + M = 0xD, + N = 0xE, + O = 0xF, + P = 0x10, + Q = 0x11, + R = 0x12, + S = 0x13, + T = 0x14, + U = 0x15, + V = 0x16, + W = 0x17, + X = 0x18, + Y = 0x19, + Z = 0x1A, + + NUM1 = 0x20, + NUM2 = 0x21, + NUM3 = 0x22, + NUM4 = 0x23, + NUM5 = 0x24, + NUM6 = 0x25, + NUM7 = 0x26, + NUM8 = 0x27, + NUM9 = 0x28, + NUM0 = 0x29, + + Space = 0x30, + Enter = 0x31, + Tab = 0x32, + Backspace = 0x33, + Delete = 0x34, + + Minus = 0x40, + Equals = 0x41, + LBrace = 0x42, + RBrace = 0x43, + BSlash = 0x44, + FSlash = 0x45, + Semicolon = 0x46, + Quote = 0x47, + Comma = 0x48, + Period = 0x49, + Backtick = 0x4A, + + LShift = 0x50, + RShift = 0x51, + LCtrl = 0x52, + RCtrl = 0x53, + LAlt = 0x54, + RAlt = 0x55, + Super = 0x56, + Esc = 0x57, + Up = 0x58, + Down = 0x59, + Left = 0x5A, + Right = 0x5B, +} + +impl Keycode { + fn from_scancode(scancode: u16) -> Self { + match scancode as u8 { + 0x04 => Keycode::A, + 0x05 => Keycode::B, + 0x06 => Keycode::C, + 0x07 => Keycode::D, + 0x08 => Keycode::E, + 0x09 => Keycode::F, + 0x0A => Keycode::G, + 0x0B => Keycode::H, + 0x0C => Keycode::I, + 0x0D => Keycode::J, + 0x0E => Keycode::K, + 0x0F => Keycode::L, + 0x10 => Keycode::M, + 0x11 => Keycode::N, + 0x12 => Keycode::O, + 0x13 => Keycode::P, + 0x14 => Keycode::Q, + 0x15 => Keycode::R, + 0x16 => Keycode::S, + 0x17 => Keycode::T, + 0x18 => Keycode::U, + 0x19 => Keycode::V, + 0x1A => Keycode::W, + 0x1B => Keycode::X, + 0x1C => Keycode::Y, + 0x1D => Keycode::Z, + 0x1E => Keycode::NUM1, + 0x1F => Keycode::NUM2, + 0x20 => Keycode::NUM3, + 0x21 => Keycode::NUM4, + 0x22 => Keycode::NUM5, + 0x23 => Keycode::NUM6, + 0x24 => Keycode::NUM7, + 0x25 => Keycode::NUM8, + 0x26 => Keycode::NUM9, + 0x27 => Keycode::NUM0, + 0x28 => Keycode::Enter, + 0x29 => Keycode::Esc, + 0x2A => Keycode::Backspace, + 0x2B => Keycode::Tab, + 0x2C => Keycode::Space, + 0x2D => Keycode::Minus, + 0x2E => Keycode::Equals, + 0x2F => Keycode::LBrace, + 0x30 => Keycode::RBrace, + 0x31 => Keycode::BSlash, + 0x33 => Keycode::Semicolon, + 0x34 => Keycode::Quote, + 0x35 => Keycode::Backtick, + 0x36 => Keycode::Comma, + 0x37 => Keycode::Period, + 0x38 => Keycode::FSlash, + 0x39 => Keycode::Esc, + _ => Keycode::UnknownKeycode, + } + } +} + +struct Modifiers(u8); + +impl Modifiers { + fn from_scancode(scancode: u16) -> Self { + Self((scancode >> 8) as u8) + } + + fn is_shift(&self) -> bool { + ((self.0 & 0x20) == 0x20) || ((self.0 & 0x2) == 0x2) + } +} + +fn into_char(keycode: Keycode, modifiers: Modifiers) -> char { + match keycode { + k if (Keycode::A..=Keycode::Z).contains(&k) => { + if modifiers.is_shift() { + let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + chars.as_bytes()[k as usize - Keycode::A as usize] as char + } else { + let chars = "abcdefghijklmnopqrstuvwxyz"; + chars.as_bytes()[k as usize - Keycode::A as usize] as char + } + } + k if (Keycode::NUM1..=Keycode::NUM0).contains(&k) => { + if modifiers.is_shift() { + let chars = "!@#$%^&*()"; + chars.as_bytes()[k as usize - Keycode::NUM1 as usize] as char + } else { + let chars = "12345687890"; + chars.as_bytes()[k as usize - Keycode::NUM1 as usize] as char + } + } + k if (Keycode::Minus..=Keycode::Backtick).contains(&k) => { + if modifiers.is_shift() { + let chars = "_+{}|?:\"<>~"; + chars.as_bytes()[k as usize - Keycode::Minus as usize] as char + } else { + let chars = "-=[]\\/;',.`"; + chars.as_bytes()[k as usize - Keycode::Minus as usize] as char + } + } + Keycode::Enter => '\n', + Keycode::Space => ' ', + Keycode::Tab => '\t', + Keycode::Backspace => '\x08', + _ => '\0', + } +} + +pub trait KeyboardHandler { + fn handle_char(&mut self, c: char); +} + +pub struct KeyboardListener { + listen_port: PortServer, + listen_thread: Option>, + handler: Rc>, +} + +impl KeyboardListener { + pub fn new(handler: Rc>) -> Result, ZError> { + let mut listnr = Box::new(Self { + listen_port: PortServer::new()?, + listen_thread: None, + handler, + }); + + let voyageur_endpoint = yellowstone::from_init_endpoint() + .get_endpoint(&yellowstone::GetEndpointRequest { + endpoint_name: "voyageurs".to_string(), + })? + .endpoint; + + let mut voyageur_client = crate::VoyageursClient::new(voyageur_endpoint); + + voyageur_client.register_keyboard_listener(&crate::KeyboardListener { + port_capability: listnr.listen_port.create_client_cap()?, + })?; + + let thread_entry = |self_raw| { + let listener = unsafe { + (self_raw as *mut KeyboardListener) + .as_mut() + .expect("Failed to convert to keyboard listener") + }; + listener.listen_loop(); + }; + + listnr.listen_thread = Some(Thread::spawn( + thread_entry, + &*listnr as *const Self as *const core::ffi::c_void, + )?); + + Ok(listnr) + } + + fn listen_loop(&mut self) { + loop { + let scancode = self + .listen_port + .recv_u16() + .expect("Failed to recieve scancode"); + + let keycode = Keycode::from_scancode(scancode); + let modifiers = Modifiers::from_scancode(scancode); + + self.handler + .as_ref() + .borrow_mut() + .handle_char(into_char(keycode, modifiers)) + } + } + + pub fn join(&self) -> Result<(), ZError> { + self.listen_thread.as_ref().unwrap().join() + } +} diff --git a/rust/sys/teton/Cargo.toml b/rust/sys/teton/Cargo.toml index 2b897ce..a355119 100644 --- a/rust/sys/teton/Cargo.toml +++ b/rust/sys/teton/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] mammoth = { path = "../../lib/mammoth" } victoriafalls = { path = "../../lib/victoriafalls" } +voyageurs = { path = "../../lib/voyageurs" } yellowstone = { path = "../../lib/yellowstone" } diff --git a/rust/sys/teton/src/console.rs b/rust/sys/teton/src/console.rs index c6e0ccb..e90e841 100644 --- a/rust/sys/teton/src/console.rs +++ b/rust/sys/teton/src/console.rs @@ -4,15 +4,56 @@ use crate::psf::Psf; pub struct Console { framebuffer: Framebuffer, psf: Psf, + row: u32, + col: u32, } impl Console { pub fn new(framebuffer: Framebuffer, psf: Psf) -> Self { - Self { framebuffer, psf } + Self { + framebuffer, + psf, + row: 0, + col: 0, + } } - pub fn write_char(&self, c: char) { - let glyph = self.psf.glyph(c as u32); - self.framebuffer.draw_glyph(glyph, 0, 0) + fn incr_cursor(&mut self) { + self.col += 1; + if self.col >= self.cols() { + self.col = 0; + self.row += 1; + } + + if self.row >= self.rows() { + panic!("Scroll unimplemented") + } + } + + pub fn write_char(&mut self, chr: char) { + if chr == '\x08' { + // Backspace. + if self.col > 1 { + self.col -= 1; + self.write_char(' '); + self.col -= 1; + } + return; + } + let glyph = self.psf.glyph(chr as u32); + self.framebuffer.draw_glyph( + glyph, + self.row * (self.psf.height() + 1), + self.col * (self.psf.width() + 1), + ); + self.incr_cursor() + } + + fn cols(&self) -> u32 { + self.framebuffer.width() / (self.psf.width() + 1) + } + + fn rows(&self) -> u32 { + self.framebuffer.height() / (self.psf.height() + 1) } } diff --git a/rust/sys/teton/src/framebuffer.rs b/rust/sys/teton/src/framebuffer.rs index 268c573..856b0c1 100644 --- a/rust/sys/teton/src/framebuffer.rs +++ b/rust/sys/teton/src/framebuffer.rs @@ -35,4 +35,12 @@ impl Framebuffer { } } } + + pub fn width(&self) -> u32 { + self.fb_info.width as u32 + } + + pub fn height(&self) -> u32 { + self.fb_info.height as u32 + } } diff --git a/rust/sys/teton/src/main.rs b/rust/sys/teton/src/main.rs index 5599784..73765a5 100644 --- a/rust/sys/teton/src/main.rs +++ b/rust/sys/teton/src/main.rs @@ -6,8 +6,13 @@ extern crate alloc; mod console; mod framebuffer; mod psf; +mod terminal; +use core::cell::RefCell; + +use alloc::rc::Rc; use mammoth::{debug, define_entry, zion::z_err_t}; +use voyageurs::listener::KeyboardListener; define_entry!(); @@ -33,18 +38,16 @@ extern "C" fn main() -> z_err_t { .expect("Failed to create framebuffer"); let psf = psf::Psf::new("/default8x16.psfu").expect("Failed to open font file."); - let console = console::Console::new(framebuffer, psf); + let mut console = console::Console::new(framebuffer, psf); console.write_char('>'); - /* + let terminal = Rc::new(RefCell::new(terminal::Terminal::new(console))); - Terminal terminal(console); - terminal.Register(); + let kb_listener = KeyboardListener::new(terminal).expect("Failed to create keyboard listener"); - Thread lthread = terminal.Listen(); - - check(lthread.Join()); - */ + kb_listener + .join() + .expect("Failed to wait on keyboard listener"); 0 } diff --git a/rust/sys/teton/src/psf.rs b/rust/sys/teton/src/psf.rs index 6be3da5..f27aba4 100644 --- a/rust/sys/teton/src/psf.rs +++ b/rust/sys/teton/src/psf.rs @@ -68,4 +68,12 @@ impl Psf { let len: usize = self.header.bytes_per_glyph as usize; &self.file.slice(offset, len) } + + pub fn width(&self) -> u32 { + self.header.width + } + + pub fn height(&self) -> u32 { + self.header.height + } } diff --git a/rust/sys/teton/src/terminal.rs b/rust/sys/teton/src/terminal.rs new file mode 100644 index 0000000..436fd48 --- /dev/null +++ b/rust/sys/teton/src/terminal.rs @@ -0,0 +1,18 @@ +use crate::console::Console; +use voyageurs::listener::KeyboardHandler; + +pub struct Terminal { + console: Console, +} + +impl KeyboardHandler for Terminal { + fn handle_char(&mut self, c: char) { + self.console.write_char(c) + } +} + +impl Terminal { + pub fn new(console: Console) -> Self { + Self { console } + } +}