diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-31 17:19:09 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-31 17:19:09 +0200 |
| commit | fa06681a8418b7e31af96e010bb424e178402f4d (patch) | |
| tree | 3b2fa8674384078f73b62274c71e8279ca6d0644 /rsnix/src/strbuf.rs | |
| parent | shell: ^C cancels the line / aborts streams, ^D = EOF → poweroff (diff) | |
| download | rs-dos-fa06681a8418b7e31af96e010bb424e178402f4d.tar.gz rs-dos-fa06681a8418b7e31af96e010bb424e178402f4d.zip | |
tty + vt terminal split, rsdosedit editor (1:1 UNIX console)
Kernel now has the classic UNIX console split, so full-screen ANSI apps
can run in ring 0:
- vt.rs: terminal emulator owning the text screen — cell grid (char +
fg/bg), logical cursor (inverse-video), scrolling, and a small ANSI
CSI parser (cursor position H/f, erase J/K, moves A-D, SGR 16-colour
with correct ANSI→VGA mapping, cursor show/hide ?25). Blits only
changed cells; scroll shifts cell grids + pixels and re-rasterises
only the exposed bottom row. gop_println!/clear_screen now render
through the vt; gop.rs is the raw rasteriser (colored glyph blits,
pixel shift).
- tty.rs: line discipline — canonical read_line (echo, line buffer,
^C cancel, ^D EOF) moved out of the shell, and raw read_byte that
translates KeyEvents to the terminal byte protocol (arrows → ESC[A-D,
Backspace → 0x7f, ^X → 0x18, Ctrl+arrows → ESC[1;5A-D, Home/End/PgUp/
PgDn/Delete). interrupt_pressed() stays for streaming commands.
- ps2: generic Ctrl(char) + CtrlArrow events (was: only CtrlC/CtrlD).
- rsdosedit: no_std port of the RNG rsedit editor as the first real
app — undo, syntax highlighting (verified green/gray/yellow on the
framebuffer), search, word nav, smart indent, ^S save / ^X exit /
^X again to discard. termios → tty raw, stdout → vt, std::fs → VFS
(ramfs), SIGWINCH → fixed geometry, force-exit window → key counter.
- shell: 'edit <path>' command; read_line/strbuf moved to rsnix.
- CONTEXT.md: layout + milestones + known-issue perf notes.
Verified in QEMU: full edit → type → arrow-move → insert → save → exit
round trip with byte-exact file content; ANSI colors via screendump.
Diffstat (limited to 'rsnix/src/strbuf.rs')
| -rw-r--r-- | rsnix/src/strbuf.rs | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/rsnix/src/strbuf.rs b/rsnix/src/strbuf.rs new file mode 100644 index 0000000..262720d --- /dev/null +++ b/rsnix/src/strbuf.rs | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | //! Fixed-capacity ASCII string, allocated entirely on the stack. | ||
| 2 | //! Shared by the line discipline (`tty`) and the shell. | ||
| 3 | |||
| 4 | use core::fmt; | ||
| 5 | |||
| 6 | /// A fixed-capacity string allocated entirely on the stack. | ||
| 7 | pub struct String<const N: usize> { | ||
| 8 | buf: [u8; N], | ||
| 9 | len: usize, | ||
| 10 | } | ||
| 11 | |||
| 12 | impl<const N: usize> Default for String<N> { | ||
| 13 | fn default() -> Self { | ||
| 14 | Self::new() | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<const N: usize> String<N> { | ||
| 19 | pub const fn new() -> Self { | ||
| 20 | String { | ||
| 21 | buf: [0u8; N], | ||
| 22 | len: 0, | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | pub fn as_str(&self) -> &str { | ||
| 27 | // SAFETY: we only write valid ASCII bytes. | ||
| 28 | unsafe { core::str::from_utf8_unchecked(&self.buf[..self.len]) } | ||
| 29 | } | ||
| 30 | |||
| 31 | pub fn push(&mut self, c: char) -> Result<(), ()> { | ||
| 32 | if self.len >= N { | ||
| 33 | return Err(()); | ||
| 34 | } | ||
| 35 | self.buf[self.len] = c as u8; | ||
| 36 | self.len += 1; | ||
| 37 | Ok(()) | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn pop(&mut self) -> Option<char> { | ||
| 41 | if self.len == 0 { | ||
| 42 | return None; | ||
| 43 | } | ||
| 44 | self.len -= 1; | ||
| 45 | Some(self.buf[self.len] as char) | ||
| 46 | } | ||
| 47 | |||
| 48 | pub fn is_empty(&self) -> bool { | ||
| 49 | self.len == 0 | ||
| 50 | } | ||
| 51 | |||
| 52 | pub fn clear(&mut self) { | ||
| 53 | self.len = 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn trim(&self) -> &str { | ||
| 57 | self.as_str().trim() | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | impl<const N: usize> fmt::Display for String<N> { | ||
| 62 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| 63 | write!(f, "{}", self.as_str()) | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | impl<const N: usize> fmt::Debug for String<N> { | ||
| 68 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| 69 | write!(f, "{:?}", self.as_str()) | ||
| 70 | } | ||
| 71 | } | ||
