term: simple utf8 decoder

This commit is contained in:
2025-03-01 15:23:33 +02:00
parent 770021df6a
commit dfa74e5c87
11 changed files with 101 additions and 58 deletions
+1 -1
View File
@@ -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)
};
+2 -2
View File
@@ -29,8 +29,8 @@ impl AsRawFd for SharedMemory {
}
impl FileMapping {
pub fn map<F: Into<OwnedFd>>(file: F, size: usize) -> io::Result<Self> {
FileMappingImpl::map(file, size).map(Self)
pub fn map<F: Into<OwnedFd>>(file: F, size: usize, write: bool) -> io::Result<Self> {
FileMappingImpl::map(file, size, write).map(Self)
}
}
+1 -1
View File
@@ -94,7 +94,7 @@ pub(crate) trait LocalPacketSocket: AsRawFd + Sized {
// Memory
pub(crate) trait FileMapping: AsRawFd + DerefMut<Target = [u8]> + Sized {
fn map<F: Into<OwnedFd>>(file: F, size: usize) -> io::Result<Self>;
fn map<F: Into<OwnedFd>>(file: F, size: usize, write: bool) -> io::Result<Self>;
}
pub(crate) trait SharedMemory: AsRawFd + Sized {
+7 -3
View File
@@ -23,7 +23,7 @@ impl sys::SharedMemory for SharedMemoryImpl {
type Mapping = FileMappingImpl;
fn map(self) -> io::Result<Self::Mapping> {
<FileMappingImpl as sys::FileMapping>::map(self.fd, self.size)
<FileMappingImpl as sys::FileMapping>::map(self.fd, self.size, true)
}
fn new(size: usize) -> io::Result<Self> {
@@ -46,13 +46,17 @@ impl AsRawFd for SharedMemoryImpl {
}
impl sys::FileMapping for FileMappingImpl {
fn map<F: Into<OwnedFd>>(file: F, size: usize) -> io::Result<Self> {
fn map<F: Into<OwnedFd>>(file: F, size: usize, write: bool) -> io::Result<Self> {
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,
@@ -80,6 +80,10 @@ impl LocalPacketSocket for LocalPacketSocketImpl {
})
}
fn set_nonblocking(&self, nb: bool) -> io::Result<()> {
self.inner.set_nonblocking(nb)
}
fn bind<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let inner = UnixDatagram::bind(path)?;
Ok(Self {
+1 -1
View File
@@ -38,7 +38,7 @@ impl AsRawFd for SharedMemoryImpl {
}
impl FileMapping for FileMappingImpl {
fn map<F: Into<OwnedFd>>(file: F, size: usize) -> io::Result<Self> {
fn map<F: Into<OwnedFd>>(file: F, size: usize, _write: bool) -> io::Result<Self> {
let inner = map::FileMapping::new(file, 0, size)?;
Ok(Self { inner })
}
@@ -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,
+5 -3
View File
@@ -8,12 +8,9 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
[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"
+4 -7
View File
@@ -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<bool, Error> {
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
+1 -1
View File
@@ -1,4 +1,4 @@
#![feature(yggdrasil_os, rustc_private)]
#![feature(yggdrasil_os, rustc_private, if_let_guard)]
use std::{
fs::File,
+74 -38
View File
@@ -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<u32>,
@@ -49,6 +56,26 @@ pub struct State {
pub attributes: CellAttributes,
}
impl Utf8Decoder {
pub fn push(&mut self, byte: u8) -> Option<char> {
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<F: FnMut(usize, &mut GridRow)>(&mut self, scroll: usize, mut handler: F) {
pub fn visible_rows_mut<F: FnMut(usize, &mut GridRow)>(
&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
}
}