term: simple utf8 decoder
This commit is contained in:
@@ -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)
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
#![feature(yggdrasil_os, rustc_private)]
|
||||
#![feature(yggdrasil_os, rustc_private, if_let_guard)]
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
|
||||
+74
-38
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user