summaryrefslogtreecommitdiffstats
path: root/rsnix/src/vt.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsnix/src/vt.rs')
-rw-r--r--rsnix/src/vt.rs87
1 files changed, 70 insertions, 17 deletions
diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs
index eb60e11..3b81d68 100644
--- a/rsnix/src/vt.rs
+++ b/rsnix/src/vt.rs
@@ -48,7 +48,11 @@ struct Cell {
48 bg: u8, 48 bg: u8,
49} 49}
50 50
51const BLANK: Cell = Cell { ch: 32, fg: 7, bg: 0 }; 51const BLANK: Cell = Cell {
52 ch: 32,
53 fg: 7,
54 bg: 0,
55};
52 56
53// ---- screen state ---------------------------------------------------------- 57// ---- screen state ----------------------------------------------------------
54 58
@@ -115,7 +119,9 @@ pub fn size() -> (usize, usize) {
115} 119}
116 120
117pub fn set_cursor_visible(v: bool) { 121pub fn set_cursor_visible(v: bool) {
118 unsafe { CURSOR_VISIBLE = v; } 122 unsafe {
123 CURSOR_VISIBLE = v;
124 }
119 flush(); 125 flush();
120} 126}
121 127
@@ -180,15 +186,7 @@ fn put_byte(b: u8) {
180 CURSOR_C = (CURSOR_C + n).min(COLS - 1); 186 CURSOR_C = (CURSOR_C + n).min(COLS - 1);
181 } 187 }
182 0x00..=0x1f => {} // other control chars: ignored 188 0x00..=0x1f => {} // other control chars: ignored
183 _ => { 189 _ => put_printable(b as u16),
184 let ch = if b < 0x80 { b as u16 } else { b'?' as u16 };
185 put_cell(CURSOR_R, CURSOR_C, ch);
186 CURSOR_C += 1;
187 if CURSOR_C >= COLS {
188 CURSOR_C = 0;
189 newline();
190 }
191 }
192 }, 190 },
193 Parser::Escape => match b { 191 Parser::Escape => match b {
194 b'[' => { 192 b'[' => {
@@ -234,6 +232,34 @@ fn param(i: usize, default: usize) -> usize {
234 } 232 }
235} 233}
236 234
235/// Place a printable glyph at the cursor and advance (wrapping/scrolling).
236/// `ch` is the character code: ASCII or a Unicode codepoint that the
237/// rasteriser can draw (box-drawing / arrows are rendered procedurally).
238fn put_printable(ch: u16) {
239 unsafe {
240 put_cell(CURSOR_R, CURSOR_C, ch);
241 CURSOR_C += 1;
242 if CURSOR_C >= COLS {
243 CURSOR_C = 0;
244 newline();
245 }
246 }
247}
248
249/// Dispatch one character to the terminal: ASCII goes through the byte
250/// state machine (control/escape handling), printable non-ASCII goes
251/// straight to the glyph path (replaced by '?' when not drawable).
252fn write_char(c: char) {
253 let u = c as u32;
254 if u < 0x80 {
255 put_byte(u as u8);
256 } else if (0x2500..=0x257f).contains(&u) || (0x2190..=0x2193).contains(&u) || u == 0x2014 {
257 put_printable(c as u16);
258 } else {
259 put_byte(b'?');
260 }
261}
262
237fn csi_final(b: u8) { 263fn csi_final(b: u8) {
238 match b { 264 match b {
239 // Cursor position (1-based; 0 means 1) 265 // Cursor position (1-based; 0 means 1)
@@ -392,19 +418,46 @@ fn scroll_up() {
392// public writing API 418// public writing API
393// --------------------------------------------------------------------------- 419// ---------------------------------------------------------------------------
394 420
395/// Write raw bytes (ANSI-capable). Used by full-screen apps. 421/// Write raw bytes (ANSI-capable). Used by full-screen apps. UTF-8
422/// sequences are decoded so Unicode glyphs (box-drawing, arrows) render;
423/// undecodable bytes become '?'.
396pub fn write_bytes(b: &[u8]) { 424pub fn write_bytes(b: &[u8]) {
397 for &x in b { 425 let mut i = 0;
398 put_byte(x); 426 while i < b.len() {
427 let byte = b[i];
428 if byte < 0x80 {
429 put_byte(byte);
430 i += 1;
431 continue;
432 }
433 // Decode the next UTF-8 character starting at i.
434 match core::str::from_utf8(&b[i..]) {
435 Ok(s) => {
436 let c = s.chars().next().unwrap_or('?');
437 write_char(c);
438 i += c.len_utf8();
439 }
440 Err(e) => {
441 let valid = e.valid_up_to();
442 if valid > 0 {
443 let s = unsafe { core::str::from_utf8_unchecked(&b[i..i + valid]) };
444 let c = s.chars().next().unwrap_or('?');
445 write_char(c);
446 i += c.len_utf8();
447 } else {
448 put_byte(b'?');
449 i += 1;
450 }
451 }
452 }
399 } 453 }
400 flush(); 454 flush();
401} 455}
402 456
403/// Write a UTF-8 string (plain text; non-ASCII becomes '?'). 457/// Write a UTF-8 string (plain text; Unicode glyphs render, others '?').
404pub fn write_str(s: &str) { 458pub fn write_str(s: &str) {
405 for c in s.chars() { 459 for c in s.chars() {
406 let b = if (c as u32) < 0x80 { c as u8 } else { b'?' }; 460 write_char(c);
407 put_byte(b);
408 } 461 }
409 flush(); 462 flush();
410} 463}