small software-rendered rust tty
-rw-r--r--Cargo.toml2
-rw-r--r--rustfmt.toml4
-rw-r--r--src/keyboard.rs12
-rw-r--r--src/main.rs94
-rw-r--r--src/render.rs351
-rw-r--r--src/term.rs46
-rw-r--r--src/term/cells.rs85
7 files changed, 122 insertions, 472 deletions
diff --git a/Cargo.toml b/Cargo.toml
index a61893a..85ab458 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 }"
);
}
}