diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-31 17:28:31 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-31 17:28:31 +0200 |
| commit | 994a2196745637fd0805132711b1ed9354b75426 (patch) | |
| tree | c666940f060aa8db028c75fd3aef799fabbd69b4 /rsnix/src/gop.rs | |
| parent | tty + vt terminal split, rsdosedit editor (1:1 UNIX console) (diff) | |
| download | rs-dos-994a2196745637fd0805132711b1ed9354b75426.tar.gz rs-dos-994a2196745637fd0805132711b1ed9354b75426.zip | |
vt: decode UTF-8, render box-drawing/arrow glyphs procedurally
The editor's gutter '│' (U+2502) appeared as '???' — write_bytes fed
each UTF-8 byte to the state machine, which mapped every byte >= 0x80
to '?', and the Latin-1 font has no box-drawing glyphs anyway.
- vt.write_bytes now decodes UTF-8 sequences; printable non-ASCII
dispatches to the glyph path instead of the byte parser.
- gop: unicode_glyph_bits() procedurally rasterises box-drawing
(U+2500-257F light/heavy/double), arrows (U+2190-2193) and the em
dash (U+2014) — pure geometry, no font data needed; blit_glyph falls
back to it for ch >= 256.
Verified via framebuffer dumps: editor gutter │ renders as a vertical
line, help screen ═ separators and ←→↑↓ all render as lines (no '?').
Shell banner em dash renders too.
Diffstat (limited to 'rsnix/src/gop.rs')
| -rw-r--r-- | rsnix/src/gop.rs | 161 |
1 files changed, 157 insertions, 4 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. | ||
| 143 | fn 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 | |||
| 138 | pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) { | 283 | pub(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); |
