small software-rendered rust tty
add one-indexed cell array
| -rw-r--r-- | src/main.rs | 22 | ||||
| -rw-r--r-- | src/render.rs | 12 | ||||
| -rw-r--r-- | src/term.rs | 107 | ||||
| -rw-r--r-- | src/term/cells.rs | 86 |
4 files changed, 132 insertions, 95 deletions
diff --git a/src/main.rs b/src/main.rs index 22f6c6f..944318e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,8 @@ use ctlfun::TerminalInputParser; use fimg::Image; use minifb::{InputCallback, Key, WindowOptions}; use nix::pty::{ForkptyResult, forkpty}; +use nix::sys::wait::waitpid; +use nix::unistd::Pid; use render::FONT; use term::*; mod render; @@ -30,7 +32,7 @@ mod term; use libc::{ F_GETFL, F_SETFL, O_NONBLOCK, TIOCSWINSZ, fcntl, ioctl, winsize, }; -fn spawn(shell: &str) -> Result<OwnedFd> { +fn spawn(shell: &str) -> Result<(OwnedFd, Pid)> { let x = unsafe { forkpty(None, None)? }; match x { ForkptyResult::Child => { @@ -49,7 +51,7 @@ fn spawn(shell: &str) -> Result<OwnedFd> { 0 ) }; - Ok(master) + Ok((master, child)) } } } @@ -95,7 +97,7 @@ fn main() -> Result<()> { w.set_input_callback(Box::new(KeyPress(ktx))); w.update(); - let pty = spawn("bash")?; + let (pty, pid) = spawn("bash")?; let pty1 = pty.try_clone()?; let pty2 = pty.try_clone()?; @@ -184,13 +186,21 @@ fn main() -> Result<()> { sleep(Duration::from_millis(10)) } }); + std::thread::spawn(move || { + loop { + match waitpid(pid, None).unwrap() { + nix::sys::wait::WaitStatus::Exited(..) => exit(0), + _ => (), + } + } + }); sleep(Duration::from_millis(100)); w.update(); let ppem = 20.0; let (fw, fh) = render::dims(&FONT, ppem); - let cols = (w.get_size().0 as f32 / fw).floor() as u16 + 1; - let rows = (w.get_size().1 as f32 / fh).floor() as u16; + let cols = (w.get_size().0 as f32 / fw).floor() as u16 - 1; + let rows = (w.get_size().1 as f32 / fh).floor() as u16 - 1; dbg!(rows, cols); let mut t = Terminal::new((cols, rows), false); unsafe { @@ -216,7 +226,7 @@ fn main() -> Result<()> { t.rx(char); } } - let i = render::render(&t, w.get_size(), ppem); + let i = render::render(&mut t, w.get_size(), ppem); let x = Image::<Box<[u32]>, 1>::from(i.as_ref()); w.update_with_buffer(x.buffer(), w.get_size().0, w.get_size().1)?; } diff --git a/src/render.rs b/src/render.rs index 6ab9dc5..845f2d2 100644 --- a/src/render.rs +++ b/src/render.rs @@ -10,19 +10,15 @@ use crate::colors; #[implicit_fn::implicit_fn] pub fn render( - x: &super::Terminal, + x: &mut super::Terminal, (w, h): (usize, usize), ppem: f32, ) -> Image<Box<[u8]>, 3> { let m = FONT.metrics(&[]); let sz = ppem * (m.max_width / m.units_per_em as f32); let mut i = Image::build(w as _, h as _).fill(colors::BACKGROUND); - for (col, k) in x.cells[x.size.0 as usize * x.row..] - .chunks_exact(x.size.0 as _) - .zip(0..) - .skip(1) - { - for (&(mut cell), j) in col.iter().skip(2).zip(0..) { + for (col, k) in x.cells.rows().zip(1..) { + for (&(mut cell), j) in col.iter().zip(1..) { if cell.style.flags & crate::term::INVERT != 0 { std::mem::swap(&mut cell.style.bg, &mut cell.style.color); } @@ -123,7 +119,7 @@ pub fn render( unsafe { i.as_mut().overlay_at( &cell, - 4 + ((x.cursor.0 - 1) as f32 * sz) as u32, + 4 + ((x.cursor.0 + 1) as f32 * sz) as u32, (x.cursor.1 as f32 * (ppem * 1.25)) as u32 - 20, ) }; diff --git a/src/term.rs b/src/term.rs index 3eaa3e3..aaf1cec 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,6 +1,7 @@ use std::iter::repeat_n; use std::ops::Not; - +mod cells; +use cells::*; use ctlfun::Parameter::*; use ctlfun::TerminalInput::*; use ctlfun::{ControlFunction, TerminalInputParser}; @@ -9,10 +10,8 @@ pub struct Terminal { pub style: Style, pub cursor: (u16, u16), pub saved_cursor: (u16, u16), - pub size: (u16, u16), - pub cells: Vec<Cell>, - pub row: usize, + pub cells: Cells, pub p: TerminalInputParser, pub mode: Mode, @@ -21,17 +20,12 @@ pub struct Terminal { use std::default::Default::default; impl Terminal { - pub fn new(sz @ (c, r): (u16, u16), alt: bool) -> Self { + pub fn new(sz: (u16, u16), alt: bool) -> Self { Self { style: default(), saved_cursor: (1, 1), cursor: (1, 1), - size: (c + 1, r + 1), - row: 0, - cells: vec![ - Cell::default(); - (c as usize + 1) * (r as usize + 1) - ], + cells: Cells::new(sz), p: default(), mode: Mode::Normal, @@ -47,29 +41,6 @@ pub enum Mode { Raw, } -#[derive(Clone, Copy)] -pub struct Style { - pub bg: [u8; 3], - pub color: [u8; 3], - pub flags: u8, -} -use crate::colors; -impl std::default::Default for Style { - fn default() -> Self { - Self { - bg: colors::BACKGROUND, - flags: 0, - color: colors::FOREGROUND, - } - } -} - -#[derive(Clone, Copy, Default)] -pub struct Cell { - pub style: Style, - pub letter: Option<char>, -} - impl Terminal { fn decsc(&mut self) { self.saved_cursor = self.cursor; @@ -78,15 +49,7 @@ impl Terminal { self.cursor = self.saved_cursor; } fn clear(&mut self) { - self.cells[self.row * self.size.0 as usize..] - .fill(Cell::default()); - } - fn grow(&mut self, by: usize) { - self.row += by; - self.cells.extend(repeat_n( - Cell::default(), - (by * (self.size.0) as usize) as usize, - )); + self.cells.cells().fill(Cell::default()); } #[implicit_fn::implicit_fn] pub fn rx(&mut self, x: u8) { @@ -94,34 +57,25 @@ impl Terminal { Continue => {} Char(x) => { self.cursor.0 += 1; - if self.cursor.0 == self.size.0 { + if self.cursor.0 >= self.cells.c() { println!("overflow"); self.cursor.0 = 1; self.cursor.1 += 1; } - while self.cursor.1 >= self.size.1 { + while self.cursor.1 >= self.cells.r() { println!("newline"); self.cursor.1 -= 1; - self.grow(1); + self.cells.grow(1); } - assert!( - self.cells.len() - == self.row * (self.size.0 as usize) - + (self.size.0 as usize) - * (self.size.1 as usize) - ); - // assert!( - // self.cells[self.row * self.size.0 as usize..].len() - // == self.size.0 as usize * self.size.1 as usize - // ); - // dbg!(self.cursor); - let c = &mut self.cells[self.row * self.size.0 as usize..] - // y*w+x - [(self.cursor.1 * self.size.0 + self.cursor.0) - as usize]; + let w = self.cells.c(); + let c = self.cells.get_at(self.cursor); c.letter = Some(x); c.style = self.style; - dbg!(x); + eprintln!( + "@ {} (mx {w}) c={}", + self.cursor.0, + c.letter.unwrap() + ); } Control(ControlFunction { start: b'', @@ -239,18 +193,13 @@ impl Terminal { .. }) => { for row in match x.value_or(0) { - 0 => self.cursor.1 + 1..self.size.1, - 1 => 0..self.cursor.1, - 2 => 0..self.size.1, + 0 => self.cursor.1 + 1..self.cells.r(), + 1 => 1..self.cursor.1, + 2 => 1..self.cells.r(), 3 => 0..0, _ => unreachable!(), } { - for cell in self.cells - [self.row * self.size.0 as usize..] - [row as usize * self.size.0 as usize..] - .iter_mut() - .take(self.size.0 as _) - { + for cell in self.cells.row(row) { *cell = Cell::default(); } } @@ -261,12 +210,7 @@ impl Terminal { end: b'K', .. }) => { - for cell in &mut self.cells - [self.row * self.size.0 as usize..] - [(self.cursor.1 * self.size.0 + self.cursor.0) as usize - ..(self.cursor.1 * self.size.0 + self.size.0) - as usize] - { + for cell in self.cells.past(self.cursor) { *cell = Cell::default(); } } @@ -277,7 +221,7 @@ impl Terminal { .. }) => { let x = x.value_or(1); - self.grow(x as _); + self.cells.grow(x as _); } Control(ControlFunction { start: b'[', @@ -286,12 +230,11 @@ impl Terminal { .. }) => { let x = x.value_or(1); - for cell in &mut self.cells - [self.row * self.size.0 as usize..][..x as usize] + for cell in + self.cells.row(self.cursor.1).iter_mut().take(x as _) { *cell = Cell::default(); } - self.grow(x as _); } Control(ControlFunction { @@ -426,6 +369,8 @@ fn value<'a>( any().filter(move |x| r.contains(x)) } use chumsky::prelude::*; + +use crate::colors; #[implicit_fn::implicit_fn] fn parse_sgr<'a>() -> impl Parser<'a, &'a [u16], Vec<StyleAction>> { use StyleAction::*; diff --git a/src/term/cells.rs b/src/term/cells.rs new file mode 100644 index 0000000..06f0415 --- /dev/null +++ b/src/term/cells.rs @@ -0,0 +1,86 @@ +// one-indexed cell array +pub struct Cells { + pub size: (u16, u16), + pub cells: Vec<Cell>, + pub row: u16, +} +#[derive(Clone, Copy)] +pub struct Style { + pub bg: [u8; 3], + pub color: [u8; 3], + pub flags: u8, +} +use std::iter::repeat_n; + +use crate::colors; +impl std::default::Default for Style { + fn default() -> Self { + Self { + bg: colors::BACKGROUND, + flags: 0, + color: colors::FOREGROUND, + } + } +} + +#[derive(Clone, Copy, Default)] +pub struct Cell { + pub style: Style, + pub letter: Option<char>, +} + +impl Cells { + pub fn new((c, r): (u16, u16)) -> Self { + Self { + size: (c + 1, r + 1), + cells: vec![ + Cell::default(); + (c as usize + 1) * (r as usize + 1) + ], + row: 0, + } + } + pub fn cells(&mut self) -> &mut [Cell] { + assert!( + self.cells.len() + == self.row as usize * (self.size.0 as usize) + + (self.size.0 as usize) * (self.size.1 as usize) + ); + assert!( + self.cells[self.row as usize * self.size.0 as usize..].len() + == self.size.0 as usize * self.size.1 as usize + ); + &mut self.cells[self.row as usize * self.size.0 as usize..] + } + pub fn r(&mut self) -> u16 { + self.size.1 + } + pub fn c(&mut self) -> u16 { + self.size.0 + } + #[track_caller] + pub fn get_at(&mut self, (x, y): (u16, u16)) -> &mut Cell { + let w = self.c(); + assert!(x < self.c() && y < self.r(), "out of bounds"); + &mut self.cells()[((y - 1) * w + x - 1) as usize] + } + pub fn rows(&mut self) -> impl Iterator<Item = &mut [Cell]> { + let w = self.c(); + self.cells().chunks_exact_mut(w as _) + } + pub fn row(&mut self, row: u16) -> &mut [Cell] { + let w = self.c(); + &mut self.cells() + [(row as usize - 1) * w as usize..row as usize * w as usize] + } + pub fn past(&mut self, (x, row): (u16, u16)) -> &mut [Cell] { + &mut self.rows().nth(row as usize - 1).unwrap()[x as usize - 1..] + } + pub fn grow(&mut self, by: u16) { + self.row += by; + self.cells.extend(repeat_n( + Cell::default(), + by as usize * (self.size.0) as usize, + )); + } +} |