summaryrefslogtreecommitdiffstats
path: root/rsnix/src
diff options
context:
space:
mode:
Diffstat (limited to 'rsnix/src')
-rw-r--r--rsnix/src/gop.rs161
-rw-r--r--rsnix/src/ps2.rs24
-rw-r--r--rsnix/src/vt.rs87
3 files changed, 247 insertions, 25 deletions
diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs
index 81b8945..9cb0d7d 100644
--- a/rsnix/src/gop.rs
+++ b/rsnix/src/gop.rs
@@ -135,13 +135,166 @@ impl Framebuffer {
135} 135}
136 136
137/// Blit one glyph cell at (col, row) with explicit fg/bg colours. 137/// Blit one glyph cell at (col, row) with explicit fg/bg colours.
138/// Procedurally-rendered bitmaps for Unicode glyphs missing from the
139/// 256-glyph Latin-1 font: box-drawing (U+2500–U+257F), arrows
140/// (U+2190–U+2193) and the em dash (U+2014). Box drawing is pure
141/// geometry, so these are computed, not stored. Same format as the font:
142/// one byte per row, bit 7 = leftmost column.
143fn unicode_glyph_bits(ch: u16) -> Option<[u8; 16]> {
144 let mut g = [0u8; 16];
145 match ch {
146 // ─ light horizontal, ━ heavy horizontal
147 0x2500 => {
148 g[7] = 0xFF;
149 g[8] = 0xFF;
150 }
151 0x2501 => {
152 g[7] = 0xFF;
153 g[8] = 0xFF;
154 g[9] = 0xFF;
155 }
156 // │ light vertical, ┃ heavy vertical
157 0x2502 => {
158 for y in 0..16 {
159 g[y] = 0x18;
160 }
161 }
162 0x2503 => {
163 for y in 0..16 {
164 g[y] = 0x3C;
165 }
166 }
167 // corners ┌ ┐ └ ┘
168 0x250C => {
169 g[7] = 0xF0;
170 g[8] = 0xF8;
171 for y in 9..16 {
172 g[y] = 0x18;
173 }
174 }
175 0x2510 => {
176 g[7] = 0x0F;
177 g[8] = 0x1F;
178 for y in 9..16 {
179 g[y] = 0x18;
180 }
181 }
182 0x2514 => {
183 for y in 0..7 {
184 g[y] = 0x18;
185 }
186 g[7] = 0xF8;
187 g[8] = 0xF0;
188 }
189 0x2518 => {
190 for y in 0..7 {
191 g[y] = 0x18;
192 }
193 g[7] = 0x1F;
194 g[8] = 0x0F;
195 }
196 // T junctions ├ ┤ ┬ ┴ and cross ┼
197 0x251C => {
198 for y in 0..16 {
199 g[y] = 0x18;
200 }
201 g[7] = 0xF8;
202 g[8] = 0xF8;
203 }
204 0x2524 => {
205 for y in 0..16 {
206 g[y] = 0x18;
207 }
208 g[7] = 0x1F;
209 g[8] = 0x1F;
210 }
211 0x252C => {
212 g[7] = 0xFF;
213 g[8] = 0xFF;
214 for y in 9..16 {
215 g[y] = 0x18;
216 }
217 }
218 0x2534 => {
219 for y in 0..7 {
220 g[y] = 0x18;
221 }
222 g[7] = 0xFF;
223 g[8] = 0xFF;
224 }
225 0x253C => {
226 for y in 0..16 {
227 g[y] = 0x18;
228 }
229 g[7] = 0xFF;
230 g[8] = 0xFF;
231 }
232 // ═ double horizontal, ║ double vertical
233 0x2550 => {
234 g[6] = 0xFF;
235 g[9] = 0xFF;
236 }
237 0x2551 => {
238 for y in 0..16 {
239 g[y] = 0x24;
240 }
241 }
242 // — em dash
243 0x2014 => {
244 g[7] = 0xFF;
245 g[8] = 0xFF;
246 }
247 // ← → ↑ ↓
248 0x2190 => {
249 g[8] = 0xFF;
250 g[6] = 0xC0;
251 g[7] = 0xC0;
252 g[9] = 0xC0;
253 g[10] = 0xC0;
254 }
255 0x2192 => {
256 g[8] = 0xFF;
257 g[6] = 0x03;
258 g[7] = 0x03;
259 g[9] = 0x03;
260 g[10] = 0x03;
261 }
262 0x2191 => {
263 g[0] = 0x18;
264 g[1] = 0x3C;
265 g[2] = 0x7E;
266 for y in 3..16 {
267 g[y] = 0x18;
268 }
269 }
270 0x2193 => {
271 for y in 0..13 {
272 g[y] = 0x18;
273 }
274 g[13] = 0x7E;
275 g[14] = 0x3C;
276 g[15] = 0x18;
277 }
278 _ => return None,
279 }
280 Some(g)
281}
282
138pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) { 283pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) {
139 if let Some(fb) = unsafe { FB.as_ref() } { 284 if let Some(fb) = unsafe { FB.as_ref() } {
140 let glyph_idx = if ch < 256 { ch as usize } else { 32 };
141 let glyph = font::GLYPH_8X16; 285 let glyph = font::GLYPH_8X16;
142 let data = match glyph.get(glyph_idx) { 286 let data = if ch < 256 {
143 Some(d) => *d, 287 match glyph.get(ch as usize) {
144 None => return, 288 Some(d) => *d,
289 None => return,
290 }
291 } else if let Some(bits) = unicode_glyph_bits(ch) {
292 bits
293 } else {
294 match glyph.get(32) {
295 Some(d) => *d,
296 None => return,
297 }
145 }; 298 };
146 299
147 let fg = rgb(fg.0, fg.1, fg.2); 300 let fg = rgb(fg.0, fg.1, fg.2);
diff --git a/rsnix/src/ps2.rs b/rsnix/src/ps2.rs
index 8dd58a0..4747813 100644
--- a/rsnix/src/ps2.rs
+++ b/rsnix/src/ps2.rs
@@ -337,10 +337,26 @@ fn process_e0(byte: u8) -> Option<KeyEvent> {
337 update_modifiers(byte, true, true); 337 update_modifiers(byte, true, true);
338 338
339 match byte { 339 match byte {
340 0x48 => Some(if ctrl_held() { KeyEvent::CtrlArrow(0) } else { KeyEvent::ArrowUp }), 340 0x48 => Some(if ctrl_held() {
341 0x50 => Some(if ctrl_held() { KeyEvent::CtrlArrow(1) } else { KeyEvent::ArrowDown }), 341 KeyEvent::CtrlArrow(0)
342 0x4B => Some(if ctrl_held() { KeyEvent::CtrlArrow(2) } else { KeyEvent::ArrowLeft }), 342 } else {
343 0x4D => Some(if ctrl_held() { KeyEvent::CtrlArrow(3) } else { KeyEvent::ArrowRight }), 343 KeyEvent::ArrowUp
344 }),
345 0x50 => Some(if ctrl_held() {
346 KeyEvent::CtrlArrow(1)
347 } else {
348 KeyEvent::ArrowDown
349 }),
350 0x4B => Some(if ctrl_held() {
351 KeyEvent::CtrlArrow(2)
352 } else {
353 KeyEvent::ArrowLeft
354 }),
355 0x4D => Some(if ctrl_held() {
356 KeyEvent::CtrlArrow(3)
357 } else {
358 KeyEvent::ArrowRight
359 }),
344 0x47 => Some(KeyEvent::Home), 360 0x47 => Some(KeyEvent::Home),
345 0x4F => Some(KeyEvent::End), 361 0x4F => Some(KeyEvent::End),
346 0x49 => Some(KeyEvent::PageUp), 362 0x49 => Some(KeyEvent::PageUp),
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}