perf: fast display (row runs, bulk fills, pixel scroll), terminal draws changed cells only + scrolls pixels, print! batched (was IPC per fmt fragment), snake sleeps on timer
This commit is contained in:
@@ -13,6 +13,8 @@ pub trait Console {
|
||||
/// Shows the cursor at a cell, over what `draw_row` drew there.
|
||||
fn draw_cursor(&mut self, col: usize, row: usize, under: u8);
|
||||
fn flush(&mut self);
|
||||
/// Moves everything drawn up by `rows` text rows, blank below.
|
||||
fn scroll(&mut self, rows: usize);
|
||||
/// Soon calls `TerminalServer::signal`, to flush a burst of output once.
|
||||
fn flush_later(&mut self);
|
||||
/// Blocks until a key is typed: a byte, `\n` for Enter, 8 for Backspace.
|
||||
@@ -37,6 +39,8 @@ pub struct TerminalServer<C: Console> {
|
||||
line_len: usize,
|
||||
line_read: usize,
|
||||
flush_pending: bool,
|
||||
/// How many cells of each row are drawn on the screen.
|
||||
drawn: [usize; crate::grid::MAX_ROWS],
|
||||
typeahead: [u8; TYPEAHEAD],
|
||||
typeahead_len: usize,
|
||||
/// Told about Ctrl+C typed while no line is being read.
|
||||
@@ -61,6 +65,7 @@ impl<C: Console> TerminalServer<C> {
|
||||
line_len: 0,
|
||||
line_read: 0,
|
||||
flush_pending: false,
|
||||
drawn: [0; crate::grid::MAX_ROWS],
|
||||
typeahead: [0; TYPEAHEAD],
|
||||
typeahead_len: 0,
|
||||
on_interrupt: None,
|
||||
@@ -84,16 +89,33 @@ impl<C: Console> TerminalServer<C> {
|
||||
}
|
||||
|
||||
fn redraw(&mut self) {
|
||||
let scrolled = self.grid.take_scrolled();
|
||||
if scrolled > 0 {
|
||||
self.console.scroll(scrolled);
|
||||
let rows = self.grid.rows();
|
||||
self.drawn.copy_within(scrolled.min(rows)..rows, 0);
|
||||
self.drawn[rows - scrolled.min(rows)..rows].fill(0);
|
||||
}
|
||||
let mut dirty = [false; crate::grid::MAX_ROWS];
|
||||
for r in self.grid.take_dirty() {
|
||||
dirty[r] = true;
|
||||
}
|
||||
for (r, _) in dirty.iter().enumerate().filter(|(_, d)| **d) {
|
||||
self.console.draw_row(r, self.grid.row(r));
|
||||
// As far as there is text, or as far as was drawn before (so
|
||||
// what is gone is erased): rarely the whole width.
|
||||
let row = self.grid.row(r);
|
||||
let text = row.iter().rposition(|b| *b != b' ').map_or(0, |i| i + 1);
|
||||
let width = text.max(self.drawn[r]).min(row.len());
|
||||
if width > 0 {
|
||||
self.console.draw_row(r, &row[..width]);
|
||||
}
|
||||
self.drawn[r] = text;
|
||||
}
|
||||
let (col, row) = (self.grid.col, self.grid.row);
|
||||
let under = self.grid.row(row).get(col).copied().unwrap_or(b' ');
|
||||
self.console.draw_cursor(col, row, under);
|
||||
// The cursor cell is drawn too, so it is erased when it moves.
|
||||
self.drawn[row] = self.drawn[row].max(col + 1);
|
||||
}
|
||||
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
@@ -210,7 +232,9 @@ impl<C: Console> Server for TerminalServer<C> {
|
||||
let reply = match words[0] {
|
||||
types::terminal::READ => self.read(),
|
||||
types::terminal::REDRAW => {
|
||||
// Taking focus back clears the screen.
|
||||
self.console.take_focus();
|
||||
self.drawn = [0; crate::grid::MAX_ROWS];
|
||||
self.grid.touch_all();
|
||||
self.redraw();
|
||||
self.console.flush();
|
||||
|
||||
Reference in New Issue
Block a user