From dfa74e5c8756e6b83e626e41e751a3c96d26469d Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sat, 1 Mar 2025 15:23:33 +0200 Subject: [PATCH] term: simple utf8 decoder --- userspace/colors/src/sys/yggdrasil.rs | 2 +- userspace/lib/cross/src/mem.rs | 4 +- userspace/lib/cross/src/sys/mod.rs | 2 +- userspace/lib/cross/src/sys/unix/mem.rs | 10 +- userspace/lib/cross/src/sys/unix/socket.rs | 4 + userspace/lib/cross/src/sys/yggdrasil/mem.rs | 2 +- .../lib/libcolors/src/application/window.rs | 2 +- userspace/sysutils/Cargo.toml | 8 +- userspace/sysutils/src/grep.rs | 11 +- userspace/term/src/main.rs | 2 +- userspace/term/src/state.rs | 112 ++++++++++++------ 11 files changed, 101 insertions(+), 58 deletions(-) diff --git a/userspace/colors/src/sys/yggdrasil.rs b/userspace/colors/src/sys/yggdrasil.rs index 94670ea2..fc9926d3 100644 --- a/userspace/colors/src/sys/yggdrasil.rs +++ b/userspace/colors/src/sys/yggdrasil.rs @@ -194,7 +194,7 @@ impl Display<'_> { let width = framebuffer.width as usize; let height = framebuffer.height as usize; - let mut mapping = FileMapping::map(file, framebuffer.size)?; + let mut mapping = FileMapping::map(file, framebuffer.size, true)?; let data = unsafe { std::slice::from_raw_parts_mut(mapping.as_mut_ptr() as *mut u32, width * height) }; diff --git a/userspace/lib/cross/src/mem.rs b/userspace/lib/cross/src/mem.rs index a88e999c..da50a66f 100644 --- a/userspace/lib/cross/src/mem.rs +++ b/userspace/lib/cross/src/mem.rs @@ -29,8 +29,8 @@ impl AsRawFd for SharedMemory { } impl FileMapping { - pub fn map>(file: F, size: usize) -> io::Result { - FileMappingImpl::map(file, size).map(Self) + pub fn map>(file: F, size: usize, write: bool) -> io::Result { + FileMappingImpl::map(file, size, write).map(Self) } } diff --git a/userspace/lib/cross/src/sys/mod.rs b/userspace/lib/cross/src/sys/mod.rs index d1efa82d..7910f7d6 100644 --- a/userspace/lib/cross/src/sys/mod.rs +++ b/userspace/lib/cross/src/sys/mod.rs @@ -94,7 +94,7 @@ pub(crate) trait LocalPacketSocket: AsRawFd + Sized { // Memory pub(crate) trait FileMapping: AsRawFd + DerefMut + Sized { - fn map>(file: F, size: usize) -> io::Result; + fn map>(file: F, size: usize, write: bool) -> io::Result; } pub(crate) trait SharedMemory: AsRawFd + Sized { diff --git a/userspace/lib/cross/src/sys/unix/mem.rs b/userspace/lib/cross/src/sys/unix/mem.rs index 76316a5e..c0ab126a 100644 --- a/userspace/lib/cross/src/sys/unix/mem.rs +++ b/userspace/lib/cross/src/sys/unix/mem.rs @@ -23,7 +23,7 @@ impl sys::SharedMemory for SharedMemoryImpl { type Mapping = FileMappingImpl; fn map(self) -> io::Result { - ::map(self.fd, self.size) + ::map(self.fd, self.size, true) } fn new(size: usize) -> io::Result { @@ -46,13 +46,17 @@ impl AsRawFd for SharedMemoryImpl { } impl sys::FileMapping for FileMappingImpl { - fn map>(file: F, size: usize) -> io::Result { + fn map>(file: F, size: usize, write: bool) -> io::Result { let fd: OwnedFd = file.into(); + let mut flags = libc::PROT_READ; + if write { + flags |= libc::PROT_WRITE; + } let pointer = unsafe { libc::mmap( null_mut(), size, - libc::PROT_READ | libc::PROT_WRITE, + flags, libc::MAP_SHARED, fd.as_raw_fd(), 0, diff --git a/userspace/lib/cross/src/sys/unix/socket.rs b/userspace/lib/cross/src/sys/unix/socket.rs index 3be0df8a..ac9208bb 100644 --- a/userspace/lib/cross/src/sys/unix/socket.rs +++ b/userspace/lib/cross/src/sys/unix/socket.rs @@ -80,6 +80,10 @@ impl LocalPacketSocket for LocalPacketSocketImpl { }) } + fn set_nonblocking(&self, nb: bool) -> io::Result<()> { + self.inner.set_nonblocking(nb) + } + fn bind>(path: P) -> io::Result { let inner = UnixDatagram::bind(path)?; Ok(Self { diff --git a/userspace/lib/cross/src/sys/yggdrasil/mem.rs b/userspace/lib/cross/src/sys/yggdrasil/mem.rs index 494bbafa..b6484a1f 100644 --- a/userspace/lib/cross/src/sys/yggdrasil/mem.rs +++ b/userspace/lib/cross/src/sys/yggdrasil/mem.rs @@ -38,7 +38,7 @@ impl AsRawFd for SharedMemoryImpl { } impl FileMapping for FileMappingImpl { - fn map>(file: F, size: usize) -> io::Result { + fn map>(file: F, size: usize, _write: bool) -> io::Result { let inner = map::FileMapping::new(file, 0, size)?; Ok(Self { inner }) } diff --git a/userspace/lib/libcolors/src/application/window.rs b/userspace/lib/libcolors/src/application/window.rs index 4d06a80c..7e1e0772 100644 --- a/userspace/lib/libcolors/src/application/window.rs +++ b/userspace/lib/libcolors/src/application/window.rs @@ -60,7 +60,7 @@ impl<'a> Window<'a> { assert_eq!(create_info.surface_stride % 4, 0); let mut surface_mapping = - FileMapping::map(surface_shm_fd, create_info.surface_mapping_size)?; + FileMapping::map(surface_shm_fd, create_info.surface_mapping_size, true)?; let surface_data = unsafe { std::slice::from_raw_parts_mut( surface_mapping.as_mut_ptr() as *mut u32, diff --git a/userspace/sysutils/Cargo.toml b/userspace/sysutils/Cargo.toml index e8804082..0f3f7d8d 100644 --- a/userspace/sysutils/Cargo.toml +++ b/userspace/sysutils/Cargo.toml @@ -8,12 +8,9 @@ authors = ["Mark Poliakov "] [dependencies] libterm.workspace = true -yggdrasil-abi.workspace = true -yggdrasil-rt.workspace = true cross.workspace = true logsink.workspace = true libutil.workspace = true -runtime.workspace = true log.workspace = true rand.workspace = true @@ -30,6 +27,11 @@ pci-ids = { version = "0.2.5" } init = { path = "../init" } +[target.'cfg(target_os = "yggdrasil")'.dependencies] +yggdrasil-abi.workspace = true +yggdrasil-rt.workspace = true +runtime.workspace = true + [lib] path = "src/lib.rs" diff --git a/userspace/sysutils/src/grep.rs b/userspace/sysutils/src/grep.rs index 0acfe9cf..beb46a00 100644 --- a/userspace/sysutils/src/grep.rs +++ b/userspace/sysutils/src/grep.rs @@ -1,5 +1,6 @@ #![feature(seek_stream_len, slice_split_once, slice_internals)] #![allow(internal_features)] + use std::{ fs::File, io::{self, stdout, Seek, Write}, @@ -33,7 +34,7 @@ impl FileInput { let mut file = File::open(path)?; let size = file.stream_len()?.try_into().unwrap(); // TODO handle non-mmap()able files - let mmap = FileMapping::map(file, size)?; + let mmap = FileMapping::map(file, size, false)?; Ok(Self { mmap, position: 0 }) } } @@ -88,12 +89,8 @@ fn run(args: &Args) -> Result { fn main() -> ExitCode { let args = Args::parse(); match run(&args) { - Ok(true) => { - ExitCode::SUCCESS - } - Ok(false) => { - ExitCode::FAILURE - } + Ok(true) => ExitCode::SUCCESS, + Ok(false) => ExitCode::FAILURE, Err(error) => { eprintln!("{error}"); ExitCode::FAILURE diff --git a/userspace/term/src/main.rs b/userspace/term/src/main.rs index f543bf0b..526014dc 100644 --- a/userspace/term/src/main.rs +++ b/userspace/term/src/main.rs @@ -1,4 +1,4 @@ -#![feature(yggdrasil_os, rustc_private)] +#![feature(yggdrasil_os, rustc_private, if_let_guard)] use std::{ fs::File, diff --git a/userspace/term/src/state.rs b/userspace/term/src/state.rs index 2f08fa29..ada52c4b 100644 --- a/userspace/term/src/state.rs +++ b/userspace/term/src/state.rs @@ -34,9 +34,16 @@ enum EscapeState { Csi, } +#[derive(Default)] +struct Utf8Decoder { + buffer: [u8; 4], + len: usize, +} + pub struct State { pub buffer: Buffer, + utf8_decode: Utf8Decoder, esc_state: EscapeState, esc_args: Vec, @@ -49,6 +56,26 @@ pub struct State { pub attributes: CellAttributes, } +impl Utf8Decoder { + pub fn push(&mut self, byte: u8) -> Option { + self.buffer[self.len] = byte; + self.len += 1; + if let Ok(str) = std::str::from_utf8(&self.buffer[..self.len]) { + let ch = str.chars().next().unwrap(); + self.len = 0; + Some(ch) + } else { + if self.len == 4 { + // Got 4 bytes and could not decode a single character + self.len = 0; + Some('\u{25A1}') + } else { + None + } + } + } +} + impl GridCell { pub fn new(char: char, attrs: CellAttributes) -> Self { Self { char, attrs } @@ -139,7 +166,11 @@ impl Buffer { } } - pub fn visible_rows_mut(&mut self, scroll: usize, mut handler: F) { + pub fn visible_rows_mut( + &mut self, + scroll: usize, + mut handler: F, + ) { self.iter_rows_mut(scroll, |y, row| { if row.dirty { row.dirty = false; @@ -202,6 +233,7 @@ impl State { Self { buffer: Buffer::new(width, height, default_attributes.bg), + utf8_decode: Utf8Decoder::default(), esc_args: Vec::new(), esc_state: EscapeState::Normal, @@ -229,30 +261,30 @@ impl State { } } - fn putc_normal(&mut self, ch: u8) -> bool { + fn putc_normal(&mut self, ch: char) -> bool { let mut redraw = match ch { - c if c >= 127 => { - let attr = CellAttributes { - fg: Color::Black, - bg: Color::Red, - bright: false, - }; - self.buffer.set_cell(self.cursor, GridCell::new('?', attr)); - self.cursor.col += 1; - true - } - b'\x1B' => { + // c if c >= 127 => { + // let attr = CellAttributes { + // fg: Color::Black, + // bg: Color::Red, + // bright: false, + // }; + // self.buffer.set_cell(self.cursor, GridCell::new('?', attr)); + // self.cursor.col += 1; + // true + // } + '\x1B' => { self.esc_state = EscapeState::Escape; self.esc_args.clear(); self.esc_args.push(0); return false; } - b'\r' => { + '\r' => { self.buffer.rows[self.cursor.row].dirty = true; self.cursor.col = 0; true } - b'\n' => { + '\n' => { self.buffer.rows[self.cursor.row].dirty = true; self.cursor.row += 1; self.cursor.col = 0; @@ -285,10 +317,10 @@ impl State { redraw } - fn handle_ctlseq(&mut self, c: u8) -> bool { + fn handle_ctlseq(&mut self, c: char) -> bool { let redraw = match c { // Move back one character - b'D' => { + 'D' => { if self.cursor.col > 0 { self.buffer.set_row_dirty(self.cursor.row); self.cursor.col -= 1; @@ -298,7 +330,7 @@ impl State { } } // Character attributes - b'm' => match self.esc_args[0] { + 'm' => match self.esc_args[0] { 0 => { self.attributes = self.default_attributes; false @@ -328,7 +360,7 @@ impl State { _ => false, }, // Move cursor to position - b'f' => { + 'f' => { let row = self.esc_args[0].clamp(1, self.buffer.height as u32) - 1; let col = self.esc_args[1].clamp(1, self.buffer.width as u32) - 1; @@ -341,7 +373,7 @@ impl State { true } // Clear rows/columns/screen - b'J' => match self.esc_args[0] { + 'J' => match self.esc_args[0] { // Erase lines down 0 => false, // Erase lines up @@ -353,7 +385,7 @@ impl State { } _ => false, }, - b'K' => match self.esc_args[0] { + 'K' => match self.esc_args[0] { // Erase to right 0 => { self.buffer.rows[self.cursor.row] @@ -374,15 +406,15 @@ impl State { redraw } - fn handle_ctlseq_byte(&mut self, c: u8) -> bool { + fn handle_ctlseq_byte(&mut self, c: char) -> bool { match c { - b'0'..=b'9' => { + c if let Some(digit) = c.to_digit(10) => { let arg = self.esc_args.last_mut().unwrap(); *arg *= 10; - *arg += (c - b'0') as u32; + *arg += digit; false } - b';' => { + ';' => { self.esc_args.push(0); false } @@ -391,19 +423,23 @@ impl State { } pub fn handle_shell_output(&mut self, ch: u8) -> bool { - match self.esc_state { - EscapeState::Normal => self.putc_normal(ch), - EscapeState::Escape => match ch { - b'[' => { - self.esc_state = EscapeState::Csi; - false - } - _ => { - self.esc_state = EscapeState::Normal; - false - } - }, - EscapeState::Csi => self.handle_ctlseq_byte(ch), + if let Some(ch) = self.utf8_decode.push(ch) { + match self.esc_state { + EscapeState::Normal => self.putc_normal(ch), + EscapeState::Escape => match ch { + '[' => { + self.esc_state = EscapeState::Csi; + false + } + _ => { + self.esc_state = EscapeState::Normal; + false + } + }, + EscapeState::Csi => self.handle_ctlseq_byte(ch), + } + } else { + false } }