small software-rendered rust tty
port to dsb for great good
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | rustfmt.toml | 4 | ||||
| -rw-r--r-- | src/keyboard.rs | 12 | ||||
| -rw-r--r-- | src/main.rs | 94 | ||||
| -rw-r--r-- | src/render.rs | 351 | ||||
| -rw-r--r-- | src/term.rs | 46 | ||||
| -rw-r--r-- | src/term/cells.rs | 85 |
7 files changed, 122 insertions, 472 deletions
@@ -6,12 +6,14 @@ edition = "2024" [dependencies] anstream = "0.6.18" anyhow = "1.0.98" +array_chunks = "1.0.0" atools = "0.1.6" chumsky = { git = "https://github.com/zesterer/chumsky", version = "0.11.0", features = [ "nightly", ] } color-hex = "0.2.0" ctlfun = { git = "https://github.com/bend-n/ctlfun" } +dsb = { git = "https://github.com/bend-n/dsb" } fimg = { git = "https://github.com/bend-n/fimg" } implicit-fn = "0.1.0" diff --git a/rustfmt.toml b/rustfmt.toml index 3a18f83..c39da03 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,7 @@ max_width = 75 +format_strings = true group_imports = "StdExternalCrate" imports_granularity = "Module" +match_arm_blocks = false +style_edition = "2024" +use_small_heuristics = "Max" diff --git a/src/keyboard.rs b/src/keyboard.rs index 2b2fcee..9c9235e 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -23,20 +23,12 @@ pub struct Board { } impl Board { pub fn new() -> Self { - Self { - mods: Modifiers { - alt: false, - shift: false, - ctrl: false, - }, - } + Self { mods: Modifiers { alt: false, shift: false, ctrl: false } } } pub fn rx(&mut self, k: Key, s: bool) -> Vec<u8> { use Key::*; - let Self { - mods: Modifiers { shift, ctrl, alt }, - } = self; + let Self { mods: Modifiers { shift, ctrl, alt } } = self; if !s { if k == LeftAlt || k == RightAlt { *alt = false; diff --git a/src/main.rs b/src/main.rs index 4fb4624..864521f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,10 +14,9 @@ #![allow(incomplete_features)] use std::fs::File; use std::io::Write; -use std::iter::successors; use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd}; use std::process::{Command, exit}; -use std::sync::mpsc; +use std::sync::{LazyLock, mpsc}; use std::thread::sleep; use std::time::{Duration, Instant}; @@ -25,14 +24,14 @@ pub mod colors; mod keyboard; use anyhow::Result; +use dsb::F; 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 swash::{FontRef, Instance}; use term::*; -mod render; mod term; use libc::{ F_GETFL, F_SETFL, O_NONBLOCK, TIOCSWINSZ, fcntl, ioctl, winsize, @@ -82,6 +81,7 @@ impl InputCallback for KeyPress { } } fn main() -> Result<()> { + let init = Instant::now(); let mut w = minifb::Window::new( "pattypan", 5, @@ -117,11 +117,7 @@ fn main() -> Result<()> { std::thread::spawn(move || { loop { - let x = successors(read(pty2.as_fd()), |_| read(pty2.as_fd())) - .flatten() - .collect::<Vec<u8>>(); - if !x.is_empty() { - // println!("recv"); + if let Some(x) = read(pty2.as_fd()) { ttx.send(x).unwrap(); } sleep(Duration::from_millis(10)); @@ -138,20 +134,29 @@ fn main() -> Result<()> { }); while w.get_size().0 < 20 || w.get_size().0 > 5000 { - sleep(Duration::from_millis(10)); + sleep(Duration::from_millis(1)); w.update(); } + println!("{:?}", init.elapsed()); 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 - 1; + let mut fonts = dsb::Fonts::new( + F::FontRef(*FONT, &[(2003265652, 550.0)]), + F::instance(*FONT, *BFONT), + F::FontRef(*IFONT, &[(2003265652, 550.0)]), + F::instance(*IFONT, *BIFONT), + ); + + // let (fw, fh) = dsb::dims(&FONT, ppem); + let (cols, rows) = dsb::fit(&FONT, ppem, 20.0, w.get_size()); println!("{}x{}", rows, cols); - let mut t = Terminal::new((cols, rows), false); + let mut t = Terminal::new((cols as _, rows as _), false); t.alternate.as_mut().unwrap().view_o = None; + let mut i = Image::build(w.get_size().0 as _, w.get_size().1 as _) + .fill(colors::BACKGROUND); unsafe { let x = winsize { - ws_row: rows, - ws_col: cols, + ws_row: rows as _, + ws_col: cols as _, ws_xpixel: w.get_size().0 as _, ws_ypixel: w.get_size().1 as _, }; @@ -160,8 +165,6 @@ fn main() -> Result<()> { let cj = swash::FontRef::from_index(&include_bytes!("../cjk.ttc")[..], 0) .unwrap(); - let m = cj.metrics(&[]); - dbg!(m); let mut f = File::create("x").unwrap(); loop { @@ -173,9 +176,9 @@ fn main() -> Result<()> { sleep(Duration::from_millis(10)); w.update(); } - 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!(cols, rows); + let (cols, rows) = + dsb::fit(&FONT, ppem, 20.0, (w.get_size().0, w.get_size().1)); + let [cols, rows] = [cols, rows].map(|x| x as u16 - 1); if (cols + 1, rows + 1) != t.cells.size { println!("{}x{}", rows, cols); @@ -191,21 +194,64 @@ fn main() -> Result<()> { ); }; t.resize((cols, rows)); + i = Image::build(w.get_size().0 as u32, w.get_size().1 as u32) + .fill(colors::BACKGROUND); } t.scroll(w.get_scroll_wheel().unwrap_or_default().1); - while let Ok(x) = trx.recv_timeout(Duration::from_millis(16)) { + let now = Instant::now(); + while let Ok(x) = + trx.recv_deadline(now + Duration::from_millis(16)) + { f.write_all(&x)?; for char in x { t.rx(char, pty.as_fd()); } } - // dbg!(t.cells.get_at((1, 1))); - let i = render::render(&mut t, w.get_size(), ppem); + unsafe { + let z = ( + cols as usize + 1, + (t.cells.cells().len() / cols as usize + 1) + .min(t.cells.r() as _), + ); + dsb::render( + &t.cells.cells[t.view_o.unwrap_or(t.cells.row) + * t.cells.c() as usize..], + z, + ppem, + colors::BACKGROUND, + &mut fonts, + 20.0, + true, + i.as_mut(), + ) + }; + let x = Image::<Box<[u32]>, 1>::from(i.as_ref()); w.update_with_buffer(x.buffer(), w.get_size().0, w.get_size().1)?; } } +pub static FONT: LazyLock<FontRef<'static>> = LazyLock::new(|| { + FontRef::from_index( + &include_bytes!("/home/os/CascadiaCodeNF.ttf")[..], + 0, + ) + .unwrap() +}); +pub static IFONT: LazyLock<FontRef<'static>> = LazyLock::new(|| { + FontRef::from_index( + &include_bytes!("/home/os/CascadiaCodeNFItalic.ttf")[..], + 0, + ) + .unwrap() +}); + +pub static BIFONT: LazyLock<Instance<'static>> = LazyLock::new(|| { + IFONT.instances().find_by_name("Bold Italic").unwrap() +}); + +pub static BFONT: LazyLock<Instance<'static>> = + LazyLock::new(|| FONT.instances().find_by_name("Bold").unwrap()); #[test] fn tpaxrse() { diff --git a/src/render.rs b/src/render.rs deleted file mode 100644 index 49a7a22..0000000 --- a/src/render.rs +++ /dev/null @@ -1,351 +0,0 @@ -use std::iter::zip; -use std::sync::LazyLock; - -use atools::Join; -use fimg::BlendingOverlayAt; -use swash::scale::{Render, ScaleContext, Source}; -use swash::shape::cluster::Glyph; -use swash::shape::{ShapeContext, ShaperBuilder}; -use swash::text::cluster::{CharCluster, CharInfo, Parser, Token}; -use swash::text::{Codepoint, Language, Script}; -use swash::zeno::Format; -use swash::{FontRef, Instance}; - -use crate::colors; -use crate::term::{BOLD, ITALIC}; - -#[implicit_fn::implicit_fn] -pub fn render( - 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); - if w < 60 || h < 60 { - return i; - } - let c = x.cells.c() as usize; - let r = x.cells.r() as usize; - let vo = x.view_o.unwrap_or(x.cells.row); - for (col, k) in x.cells.cells[vo * c..vo * c + r * c] - .chunks_exact(c as _) - .zip(1..) - { - for (&(mut cell), j) in zip(col, 0..) { - if cell.style.flags & crate::term::INVERT != 0 { - std::mem::swap(&mut cell.style.bg, &mut cell.style.color); - } - if cell.style.bg != colors::BACKGROUND { - let cell = Image::<_, 4>::build( - sz.ceil() as u32, - (ppem * 1.25).ceil() as u32, - ) - .fill(cell.style.bg.join(255)); - unsafe { - i.as_mut().overlay_at( - &cell, - 4 + (j as f32 * sz) as u32, - (k as f32 * (ppem * 1.25)) as u32 - 20, - ) - }; - } - } - } - let mut characters: Vec<Glyph> = vec![Glyph::default(); c]; - - for (col, k) in x.cells.cells[vo * c..vo * c + r * c] - .chunks_exact(c as _) - .zip(1..) - { - let tokenized = col.iter().enumerate().map(|(i, cell)| { - let ch = cell.letter.unwrap_or(' '); - ( - Token { - ch, - offset: i as u32, - len: ch.len_utf8() as u8, - info: ch.properties().into(), - data: i as u32, - }, - cell.style.flags, - ) - }); - - macro_rules! input { - ($rule:expr, $font:ident) => { - let mut scx = ShapeContext::new(); - let mut shaper = scx - .builder(*$font) - .size(ppem) - .script(Script::Latin) - .features([ - ("ss19", 1), - ("ss01", 1), - ("ss20", 1), - ("liga", 1), - ("rlig", 1), - ]) - .build(); - - let mut cluster = CharCluster::new(); - - let mut parser = Parser::new( - Script::Latin, - tokenized.clone().filter($rule).map(|x| x.0), - ); - while parser.next(&mut cluster) { - cluster.map(|ch| $font.charmap().map(ch)); - shaper.add_cluster(&cluster); - } - shaper.shape_with(|x| { - x.glyphs.into_iter().for_each(|x| { - characters[x.data as usize] = *x; - }) - }); - }; - } - input!(|x| x.1 & ITALIC == 0, FONT); - input!(|x| x.1 & ITALIC != 0, IFONT); // bifont is an instance of ifont - - for (&(mut cell), glyph) in - characters.iter().map(|x| (&col[x.data as usize], x)) - { - let j = glyph.data as usize; - if cell.style.flags & crate::term::INVERT != 0 { - std::mem::swap(&mut cell.style.bg, &mut cell.style.color); - } - let mut color = cell.style.color; - if (cell.style.flags & crate::term::DIM) != 0 { - color = color.map(|x| x / 2); - } - if (cell.style.flags & crate::term::UNDERLINE) != 0 { - unsafe { - i.as_mut().overlay_at( - &Image::<_, 4>::build(sz.ceil() as u32, 2) - .fill(color.join(255)), - 4 + (j as f32 * sz) as u32, - (k as f32 * (ppem * 1.25)) as u32 + 5, - ) - }; - } - if (cell.style.flags & crate::term::STRIKETHROUGH) != 0 { - unsafe { - i.as_mut().overlay_at( - &Image::<_, 4>::build(sz.ceil() as u32, 2) - .fill(color.join(255)), - 4 + (j as f32 * sz) as u32, - (k as f32 * (ppem * 1.25)) as u32 - 5, - ) - }; - } - if let Some(_) = cell.letter { - let f = if (cell.style.flags & crate::term::ITALIC) != 0 { - *IFONT - } else { - *FONT - }; - let id = glyph.id; - let mut scbd = ScaleContext::new(); - let mut scbd = scbd.builder(f); - scbd = scbd.size(ppem); - if (cell.style.flags & crate::term::BOLD) != 0 { - scbd = scbd.variations( - if (cell.style.flags & crate::term::ITALIC) != 0 { - *BIFONT - } else { - *BFONT - } - .values() - .zip(f.variations()) - .map(|x| (x.1.tag(), x.0)), - ); - } - let x = Render::new(&[Source::Outline]) - .format(Format::Alpha) - .render(&mut scbd.build(), id) - .unwrap(); - unsafe { - if x.placement.width == 0 { - continue; - } - let item = Image::<_, 1>::build( - x.placement.width, - x.placement.height, - ) - .buf_unchecked(x.data); - i.as_mut().blend_alpha_and_color_at( - &item.as_ref(), - color, - 4 + ((j as f32 * sz + glyph.x) - + x.placement.left as f32) - .round() as u32, - ((k as f32 * (ppem * 1.25)) as u32) - .saturating_sub(x.placement.top as u32), - ); - } - } - } - } - - if x.view_o == Some(x.cells.row) || x.view_o.is_none() { - let cell = Image::<_, 4>::build(3, (ppem * 1.25).ceil() as u32) - .fill([0xFF, 0xCC, 0x66, 255]); - unsafe { - i.as_mut().overlay_at( - &cell, - 4 + ((x.cursor.0 - 1) as f32 * sz) as u32, - (x.cursor.1 as f32 * (ppem * 1.25)) as u32 - 20, - ) - }; - } - i -} - -pub fn dims(font: &FontRef, ppem: f32) -> (f32, f32) { - let m = font.metrics(&[]); - (ppem * (m.max_width / m.units_per_em as f32), ppem * 1.25) -} - -pub static FONT: LazyLock<FontRef<'static>> = LazyLock::new(|| { - FontRef::from_index(&include_bytes!("../CascadiaCodeNF.ttf")[..], 0) - .unwrap() -}); - -pub static IFONT: LazyLock<FontRef<'static>> = LazyLock::new(|| { - FontRef::from_index( - &include_bytes!("../CascadiaCodeNFItalic.ttf")[..], - 0, - ) - .unwrap() -}); - -pub static BIFONT: LazyLock<Instance<'static>> = LazyLock::new(|| { - IFONT.instances().find_by_name("Bold Italic").unwrap() -}); - -pub static BFONT: LazyLock<Instance<'static>> = - LazyLock::new(|| FONT.instances().find_by_name("Bold").unwrap()); - -use fimg::{Image, OverlayAt}; -// let x = b"echo -e \"\x1b(0lqqqk\nx \x1b(Bx\nmqqqj"; -// let x = String::from_utf8_lossy(&x); -// println!("{}", x); -#[test] -fn t() { - let f = *FONT; - f.features() - .for_each(|f| drop(dbg!(f.name().unwrap().to_string()))); - // let f = FontRef::from_index(&include_bytes!("../fira.ttf")[..], 0) - // .unwrap(); - - dbg!(f.attributes()); - // let m = f.metrics(&[f.charmap().map('行') as _]); - - let ppem = 30.0; - let d = dims(&FONT, ppem); - let sz = d.0; - let mut scx = ShapeContext::new(); - let mut shaper = scx - .builder(f) - .size(ppem) - .script(Script::Latin) - .features([ - ("ss19", 1), - ("ss01", 1), - ("ss20", 1), - ("liga", 1), - ("rlig", 1), - ]) - .build(); - - let mut cluster = CharCluster::new(); - let mut parser = Parser::new( - Script::Latin, - "!=".char_indices().map(|(i, ch)| Token { - ch, - offset: i as u32, - len: ch.len_utf8() as u8, - info: ch.properties().into(), - data: 0, - }), - ); - - while parser.next(&mut cluster) { - cluster.map(|ch| f.charmap().map(ch)); - shaper.add_cluster(&cluster); - } - let mut characters: Vec<Glyph> = vec![]; - shaper.shape_with(|x| { - dbg!(x.is_ligature()); - dbg!(x); - characters.extend(x.glyphs) - }); - dbg!(f.charmap().map('!')); - let mut grid = Image::<_, 4>::alloc(2000, 500); - grid.chunked_mut().for_each(|x| *x = [0, 0, 0, 255]); - for (letter, i) in characters.iter().zip(0..) { - // grid.as_ref().show(); - // let id = f.charmap().map(letter); - // let id = 1940; - let id = letter.id; - let x = Render::new(&[Source::Outline]) - .format(Format::Alpha) - .render( - &mut ScaleContext::new().builder(f).size(ppem).build(), - id, - ) - .unwrap(); - dbg!(x.placement.width); - dbg!(letter.x); - - unsafe { - if x.placement.width == 0 { - continue; - } - grid.as_mut().overlay_blended_at( - &Image::<Box<[u8]>, 4>::from( - Image::<_, 1>::build( - x.placement.width, - x.placement.height, - ) - .buf(&*x.data) - .show(), - ) - .as_ref(), - ((i as f32 * sz).round() as i32 + x.placement.left) as _, - ppem as u32 - x.placement.top as u32, - ); - } - } - for (letter, i) in "#".repeat(21 * 2).chars().zip(0..) { - // grid.as_ref().show(); - let id = FONT.charmap().map(letter); - let x = Render::new(&[Source::Outline]) - .format(Format::Alpha) - .render( - &mut ScaleContext::new().builder(*FONT).size(ppem).build(), - id, - ) - .unwrap(); - unsafe { - if x.placement.width == 0 { - continue; - } - grid.as_mut().overlay_at( - &Image::<Box<[u8]>, 4>::from( - Image::<_, 1>::build( - x.placement.width, - x.placement.height, - ) - .buf(&*x.data), - ) - .as_ref(), - ((i as f32 * sz) as i32 + x.placement.left) as _, - 30 + ppem as u32 - x.placement.top as u32, - ); - } - } - grid.show(); -} diff --git a/src/term.rs b/src/term.rs index a037a57..9816335 100644 --- a/src/term.rs +++ b/src/term.rs @@ -5,6 +5,7 @@ use cells::*; use ctlfun::Parameter::*; use ctlfun::TerminalInput::*; use ctlfun::{ControlFunction, TerminalInputParser}; +use dsb::cell::Style; pub struct Terminal { pub style: Style, @@ -24,10 +25,10 @@ impl Terminal { pub fn new(sz: (u16, u16), alt: bool) -> Self { Self { view_o: Some(0), - style: default(), + style: DSTYLE, saved_cursor: (1, 1), cursor: (1, 1), - cells: Cells::new(sz), + cells: Cells::new(sz, alt), p: default(), mode: Mode::Normal, @@ -71,7 +72,7 @@ impl Terminal { self.cursor = self.saved_cursor; } fn clear(&mut self) { - self.cells.cells().fill(default()); + self.cells.cells().fill(DCELL); } #[implicit_fn::implicit_fn] pub fn rx(&mut self, x: u8, pty: BorrowedFd<'_>) { @@ -84,7 +85,7 @@ impl Terminal { self.cursor.1 += 1; } while self.cursor.1 >= self.cells.r() { - println!("newline"); + // println!("newline"); self.cursor.1 -= 1; self.cells.grow(1); if let Some(vo) = self.view_o.as_mut() @@ -105,9 +106,7 @@ impl Terminal { self.cursor.0 += 1; } Control(ControlFunction { - start: b'', - params: [], - .. + start: b'', params: [], .. }) => { self.cursor.0 -= 1; } @@ -117,7 +116,7 @@ impl Terminal { end: b'm', .. }) => { - self.style = Style::default(); + self.style = DSTYLE; } Control(ControlFunction { start: b'[', @@ -135,7 +134,7 @@ impl Terminal { { use StyleAction::*; match action { - Reset => self.style = Style::default(), + Reset => self.style = DSTYLE, SetFg(c) => self.style.color = c, SetBg(c) => self.style.bg = c, ModeSet(1) => self.style.flags |= BOLD, @@ -227,7 +226,7 @@ impl Terminal { 3 => 0..0, _ => unreachable!(), } { - self.cells.row(row).fill(default()); + self.cells.row(row).fill(DCELL); } } Control(ControlFunction { @@ -236,7 +235,7 @@ impl Terminal { end: b'K', .. }) => { - self.cells.past(self.cursor).fill(default()); + self.cells.past(self.cursor).fill(DCELL); } Control(ControlFunction { start: b'[', @@ -312,21 +311,17 @@ impl Terminal { for cell in self.cells.row(self.cursor.1).iter_mut().take(x as _) { - *cell = default(); + *cell = DCELL; } } Control(ControlFunction { - start: b'\r', - params: [], - .. + start: b'\r', params: [], .. }) => { self.cursor.0 = 1; } Control(ControlFunction { - start: b'\n', - params: [], - .. + start: b'\n', params: [], .. }) => { self.cursor.1 += 1; } @@ -338,10 +333,7 @@ impl Terminal { .. } | ControlFunction { - start: b'[', - params: [], - end: b's', - .. + start: b'[', params: [], end: b's', .. }, ) => { self.decsc(); @@ -354,10 +346,7 @@ impl Terminal { .. } | ControlFunction { - start: b'[', - params: [], - end: b'u', - .. + start: b'[', params: [], end: b'u', .. }, ) => { self.decrc(); @@ -399,7 +388,7 @@ impl Terminal { end: b'B', .. }) => {} - Control(x) => { + Control(x) => if x.start == b'[' { println!( "CSI {:?} {}", @@ -430,8 +419,7 @@ impl Terminal { String::from_utf8_lossy(&x.bytes), x.end as char, ); - } - } + }, _ => unreachable!(), } } diff --git a/src/term/cells.rs b/src/term/cells.rs index 707fe10..033bd52 100644 --- a/src/term/cells.rs +++ b/src/term/cells.rs @@ -5,38 +5,14 @@ pub struct Cells { pub margin: (u16, u16), pub row: usize, } -#[derive(Clone, Copy, Debug)] -pub struct Style { - pub bg: [u8; 3], - pub color: [u8; 3], - pub flags: u8, -} use std::cmp::Ordering; use std::default::Default::default; use std::fmt::Debug; -use std::iter::{empty, repeat, repeat_n}; +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, - } - } -} +use dsb::Cell; +use dsb::cell::Style; -#[derive(Clone, Copy, Default)] -pub struct Cell { - pub style: Style, - pub letter: Option<char>, -} -impl Debug for Cell { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.letter.unwrap_or(' ')) - } -} impl Debug for Cells { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Cells") @@ -55,24 +31,20 @@ impl Debug for Cells { .finish() } } -impl Cell { - pub fn basic(c: char) -> Self { - Self { - letter: Some(c), - ..default() - } - } -} - +pub const DSTYLE: Style = Style { + bg: crate::colors::BACKGROUND, + color: crate::colors::FOREGROUND, + flags: 0, +}; +pub const DCELL: Cell = Cell { style: DSTYLE, letter: None }; impl Cells { - pub fn new((c, r): (u16, u16)) -> Self { + pub fn new((c, r): (u16, u16), alternate: bool) -> Self { let (c, r) = (c + 1, r + 1); - Self { - size: (c, r), - margin: (1, r), - cells: vec![Cell::default(); (c as usize) * (r as usize)], - row: 0, + let mut cells = vec![DCELL; (c as usize) * (r as usize)]; + if !alternate { + cells.reserve(1070000); } + Self { size: (c, r), margin: (1, r), cells, row: 0 } } fn offset(&mut self) -> usize { self.row as usize * self.size.0 as usize @@ -86,10 +58,10 @@ impl Cells { ); &mut self.cells[o..] } - pub fn r(&mut self) -> u16 { + pub fn r(&self) -> u16 { self.size.1 } - pub fn c(&mut self) -> u16 { + pub fn c(&self) -> u16 { self.size.0 } #[track_caller] @@ -120,10 +92,7 @@ impl Cells { + (self.margin.1 as usize - 1) * self.size.0 as usize; self.cells.splice( at..at, - repeat_n( - Cell::default(), - by as usize * (self.size.0) as usize, - ), + repeat_n(DCELL, by as usize * (self.size.0) as usize), ); } @@ -154,7 +123,7 @@ impl Cells { chunk .iter() .copied() - .chain(repeat_n(default(), (c - oc) as usize)) + .chain(repeat_n(DCELL, (c - oc) as usize)) }) .collect::<Vec<_>>(); assert!(self.cells.len() % c as usize == 0); @@ -171,7 +140,7 @@ impl Cells { if d < 0 { self.row = 0; self.cells.extend(repeat_n( - Cell::default(), + DCELL, d.abs() as usize * c as usize, )); } else { @@ -184,18 +153,16 @@ impl Cells { pub fn insert_chars(&mut self, characters: u16, (x, y): (u16, u16)) { let s = &mut self.row(y)[x as usize - 1..]; s.rotate_right(characters as usize); - s[..characters as usize].fill(Cell::default()); + s[..characters as usize].fill(DCELL); let w = self.c(); - self.row(y)[w as usize - characters as usize..] - .fill(Cell::default()); + self.row(y)[w as usize - characters as usize..].fill(DCELL); } pub fn delete_chars(&mut self, characters: u16, (x, y): (u16, u16)) { let s = &mut self.row(y)[x as usize - 1..]; s.rotate_left(characters as usize); - s[..characters as usize].fill(Cell::default()); + s[..characters as usize].fill(DCELL); let w = self.c(); - self.row(y)[w as usize - characters as usize..] - .fill(Cell::default()); + self.row(y)[w as usize - characters as usize..].fill(DCELL); } pub fn insert_lines(&mut self, lines: u16, below: u16) { let c = self.c(); @@ -219,7 +186,7 @@ mod test { #[test] fn grow() { - let mut cells = Cells::new((4, 4)); + let mut cells = Cells::new((4, 4), false); cells.row(1).fill(Cell::basic('1')); cells.row(2).fill(Cell::basic('2')); cells.row(3).fill(Cell::basic('3')); @@ -232,7 +199,9 @@ mod test { dbg!(&cells); assert_eq!( format!("{cells:?}"), - "Cells { size: (5, 5), margin: (1, 5), row: 1, cells: [2, 2, 2, 2, 2]\n[3, 3, 3, 3, 3]\n[4, 4, 4, 4, 4]\n[5, 5, 5, 5, 5]\n[ , , , , ]\n }" + "Cells { size: (5, 5), margin: (1, 5), row: 1, cells: [2, 2, \ + 2, 2, 2]\n[3, 3, 3, 3, 3]\n[4, 4, 4, 4, 4]\n[5, 5, 5, 5, \ + 5]\n[ , , , , ]\n }" ); } } |