feat(userspace): terminal server + shell desktop; US keymap w/ shift; libkos allocator multi-page fix; panics print

This commit is contained in:
2026-10-27 16:50:35 +01:00
parent 2845d190f0
commit b80303f553
26 changed files with 739 additions and 53 deletions

View 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);
}
}