feat(userspace): terminal server + shell desktop; US keymap w/ shift; libkos allocator multi-page fix; panics print
This commit is contained in:
89
crates/servers/terminal-server/src/grid.rs
Normal file
89
crates/servers/terminal-server/src/grid.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
//! The terminal's text: a grid of cells, a cursor, and which rows changed.
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub const MAX_COLS: usize = 160;
|
||||
pub const MAX_ROWS: usize = 64;
|
||||
|
||||
/// On the heap: a full screen is more than a program stack holds.
|
||||
pub struct Grid {
|
||||
cells: Vec<u8>,
|
||||
cols: usize,
|
||||
rows: usize,
|
||||
pub col: usize,
|
||||
pub row: usize,
|
||||
/// Rows to redraw.
|
||||
dirty: Vec<bool>,
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
pub fn new(cols: usize, rows: usize) -> Self {
|
||||
let (cols, rows) = (cols.clamp(1, MAX_COLS), rows.clamp(1, MAX_ROWS));
|
||||
Self { cells: vec![b' '; cols * rows], cols, rows, col: 0, row: 0, dirty: vec![true; rows] }
|
||||
}
|
||||
|
||||
pub fn row(&self, r: usize) -> &[u8] {
|
||||
&self.cells[r * self.cols..(r + 1) * self.cols]
|
||||
}
|
||||
|
||||
pub fn rows(&self) -> usize {
|
||||
self.rows
|
||||
}
|
||||
|
||||
/// The rows changed since the last call, each once.
|
||||
pub fn take_dirty(&mut self) -> impl Iterator<Item = usize> + '_ {
|
||||
let rows = self.rows;
|
||||
self.dirty[..rows].iter_mut().enumerate().filter_map(|(r, d)| core::mem::take(d).then_some(r))
|
||||
}
|
||||
|
||||
fn mark(&mut self, r: usize) {
|
||||
self.dirty[r] = true;
|
||||
}
|
||||
|
||||
fn newline(&mut self) {
|
||||
// The row the cursor leaves, so its cursor cell is redrawn.
|
||||
self.mark(self.row);
|
||||
self.col = 0;
|
||||
if self.row + 1 < self.rows {
|
||||
self.row += 1;
|
||||
return;
|
||||
}
|
||||
// At the bottom: everything moves up a row.
|
||||
self.cells.copy_within(self.cols.., 0);
|
||||
let last = (self.rows - 1) * self.cols;
|
||||
self.cells[last..].fill(b' ');
|
||||
self.dirty.fill(true);
|
||||
}
|
||||
|
||||
/// Writes one byte at the cursor: `\n` moves to the next line, other
|
||||
/// bytes wrap at the right edge.
|
||||
pub fn put(&mut self, b: u8) {
|
||||
if b == b'\n' {
|
||||
self.newline();
|
||||
return;
|
||||
}
|
||||
if self.col == self.cols {
|
||||
self.newline();
|
||||
}
|
||||
self.cells[self.row * self.cols + self.col] = b;
|
||||
self.mark(self.row);
|
||||
self.col += 1;
|
||||
}
|
||||
|
||||
/// Erases the cell before the cursor, on this row only.
|
||||
pub fn back(&mut self) {
|
||||
if self.col > 0 {
|
||||
self.col -= 1;
|
||||
self.cells[self.row * self.cols + self.col] = b' ';
|
||||
self.mark(self.row);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.cells.fill(b' ');
|
||||
self.col = 0;
|
||||
self.row = 0;
|
||||
self.dirty.fill(true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user