AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
term.c
Go to the documentation of this file.
1
13
14#include <core/system.h>
15#include <core/string.h>
16#include <mm/kvmem.h>
17#include <dev/vt.h>
18
19MALLOC_DEFINE(M_VT_SCREEN, "vt-screen", "virtual terminal screen");
20
21/* where the emulator is in a sequence */
22enum {
25 S_CHARSET, /* ESC ( or ESC ): the G0 or G1 set's letter follows */
26 S_HASH, /* ESC # */
27 S_PERCENT, /* ESC % */
30 S_PALETTE, /* ESC ] P: 7 hex digits follow */
31 S_STRING, /* DCS, SOS, PM, APC: ignored up to ST */
32 S_STRING_ESC, /* ESC in a string: it is ST if \ follows */
33};
34
35#define DEFAULT_FG 7
36#define DEFAULT_BG 0
37
38/* the glyphs of DEC's special graphics (line drawing), 0x5f to 0x7e */
39static const uint8_t dec_graphics[] = {
40 ' ', 0x04, 0xB1, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, /* _ (grave) a b c d e f */
41 0xF1, 0xB0, 0x0F, 0xD9, 0xBF, 0xDA, 0xC0, 0xC5, /* g h i j k l m n */
42 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC3, 0xB4, 0xC1, /* o p q r s t u v */
43 0xC2, 0xB3, 0xF3, 0xF2, 0xE3, '!', 0x9C, 0xFA, /* w x y z { | } ~ */
44};
45
46/* the characters of code page 437's glyphs 0x80 to 0xff */
47static const uint16_t cp437[128] = {
48 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
49 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
50 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
51 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
52 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
53 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
54 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
55 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
56 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
57 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
58 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
59 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
60 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
61 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
62 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
63 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0,
64};
65
66/* the characters of code page 437's symbols, 0x01 to 0x1f and 0x7f */
67static const uint16_t cp437_symbols[32] = {
68 0x2302, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
69 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
70 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
71 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
72};
73
74/* other characters, which have a glyph close enough */
75static const struct { uint16_t u; uint8_t ch; } unicode_extra[] = {
76 {0x25C6, 0x04},
77 {0x256D, 0xDA}, {0x256E, 0xBF}, {0x256F, 0xD9}, {0x2570, 0xC0},
78 {0x2018, '\''}, {0x2019, '\''}, {0x201C, '"'}, {0x201D, '"'},
79 {0x2010, '-'}, {0x2013, '-'}, {0x2014, '-'}, {0x2212, '-'},
80 {0x2026, 0xFA}, {0x00B4, '\''}, {0x00D7, 'x'},
81};
82
85{
86 if (u < 0x80)
87 return u;
88
89 for (int i = 0; i < 128; ++i)
90 if (cp437[i] == u)
91 return 0x80 + i;
92
93 for (int i = 0; i < 32; ++i)
94 if (cp437_symbols[i] == u)
95 return i ? i : 0x7f;
96
97 for (size_t i = 0; i < sizeof(unicode_extra) / sizeof(unicode_extra[0]); ++i)
98 if (unicode_extra[i].u == u)
99 return unicode_extra[i].ch;
100
101 return '?';
102}
103
106static uint8_t rgb_color(int r, int g, int b)
107{
108 int ri = r < 48 ? 0 : r < 115 ? 1 : (r - 35) / 40;
109 int gi = g < 48 ? 0 : g < 115 ? 1 : (g - 35) / 40;
110 int bi = b < 48 ? 0 : b < 115 ? 1 : (b - 35) / 40;
111
112 if (ri == gi && gi == bi) {
113 int gray = (r + g + b) / 3;
114
115 if (gray > 8 && gray < 238)
116 return 232 + (gray - 8) / 10;
117 }
118
119 return 16 + 36 * ri + 6 * gi + bi;
120}
121
123static inline struct vt_cell *cell(struct vt *vt, int row, int col)
124{
125 return &vt->screen[row * vt->cols + col];
126}
127
130static inline struct vt_cell blank(struct vt *vt)
131{
132 return (struct vt_cell) {.ch = ' ', .fg = vt->pen.fg, .bg = vt->pen.bg};
133}
134
136static void erase(struct vt *vt, int row, int from, int to)
137{
138 struct vt_cell b = blank(vt);
139
140 for (int col = from; col < to; ++col)
141 *cell(vt, row, col) = b;
142}
143
146static void scroll_up(struct vt *vt, int top, int bottom, int n)
147{
148 if (n > bottom - top + 1)
149 n = bottom - top + 1;
150
151 memmove(cell(vt, top, 0), cell(vt, top + n, 0),
152 (bottom - top + 1 - n) * vt->cols * sizeof(struct vt_cell));
153
154 for (int row = bottom - n + 1; row <= bottom; ++row)
155 erase(vt, row, 0, vt->cols);
156}
157
160static void scroll_down(struct vt *vt, int top, int bottom, int n)
161{
162 if (n > bottom - top + 1)
163 n = bottom - top + 1;
164
165 memmove(cell(vt, top + n, 0), cell(vt, top, 0),
166 (bottom - top + 1 - n) * vt->cols * sizeof(struct vt_cell));
167
168 for (int row = top; row < top + n; ++row)
169 erase(vt, row, 0, vt->cols);
170}
171
174static void move_to(struct vt *vt, int row, int col)
175{
176 int min = vt->origin ? vt->top : 0;
177 int max = vt->origin ? vt->bottom : vt->rows - 1;
178
179 vt->row = row < min ? min : row > max ? max : row;
180 vt->col = col < 0 ? 0 : col >= vt->cols ? vt->cols - 1 : col;
181 vt->wrap_pending = 0;
182}
183
186static void move_rows(struct vt *vt, int n)
187{
188 int row = vt->row + n;
189
190 if (n < 0 && vt->row >= vt->top && row < vt->top)
191 row = vt->top;
192 if (n > 0 && vt->row <= vt->bottom && row > vt->bottom)
193 row = vt->bottom;
194
195 row = row < 0 ? 0 : row >= vt->rows ? vt->rows - 1 : row;
196
197 vt->row = row;
198 vt->wrap_pending = 0;
199}
200
202static void linefeed(struct vt *vt)
203{
204 if (vt->row == vt->bottom)
205 scroll_up(vt, vt->top, vt->bottom, 1);
206 else if (vt->row < vt->rows - 1)
207 vt->row++;
208
209 vt->wrap_pending = 0;
210}
211
213static void reverse_index(struct vt *vt)
214{
215 if (vt->row == vt->top)
216 scroll_down(vt, vt->top, vt->bottom, 1);
217 else if (vt->row > 0)
218 vt->row--;
219
220 vt->wrap_pending = 0;
221}
222
224static int tab_stop(struct vt *vt, int col)
225{
226 return col < 256 && (vt->tabs[col / 8] & (1 << (col % 8)));
227}
228
230static void tab_set(struct vt *vt, int col, int set)
231{
232 if (col >= 256)
233 return;
234
235 if (set)
236 vt->tabs[col / 8] |= 1 << (col % 8);
237 else
238 vt->tabs[col / 8] &= ~(1 << (col % 8));
239}
240
242static void tab(struct vt *vt, int n)
243{
244 while (n > 0 && vt->col < vt->cols - 1) {
245 vt->col++;
246 if (tab_stop(vt, vt->col))
247 --n;
248 }
249
250 while (n < 0 && vt->col > 0) {
251 vt->col--;
252 if (tab_stop(vt, vt->col))
253 ++n;
254 }
255
256 vt->wrap_pending = 0;
257}
258
261static void put_glyph(struct vt *vt, uint8_t ch)
262{
263 if (vt->wrap_pending && vt->autowrap) {
264 vt->col = 0;
265 linefeed(vt);
266 }
267
268 struct vt_cell *c = cell(vt, vt->row, vt->col);
269
270 if (vt->insert)
271 memmove(c + 1, c, (vt->cols - vt->col - 1) * sizeof(struct vt_cell));
272
273 *c = vt->pen;
274 c->ch = ch;
275 vt->last = ch;
276
277 if (vt->col < vt->cols - 1)
278 vt->col++;
279 else
281}
282
284static void put_char(struct vt *vt, uint32_t u)
285{
286 if (vt->charset[vt->gl] == '0') {
287 if (u >= 0x5f && u <= 0x7e) {
288 put_glyph(vt, dec_graphics[u - 0x5f]);
289 return;
290 }
291
292 /* arrows and a block, as on the Linux console */
293 switch (u) {
294 case '+': put_glyph(vt, 0x1A); return;
295 case ',': put_glyph(vt, 0x1B); return;
296 case '-': put_glyph(vt, 0x18); return;
297 case '.': put_glyph(vt, 0x19); return;
298 case '0': put_glyph(vt, 0xDB); return;
299 }
300 }
301
303}
304
307static void reply(struct vt *vt, const char *s)
308{
309 size_t len = strlen(s);
310
311 if (vt->reply_len + len > sizeof(vt->reply))
312 return;
313
314 memcpy(vt->reply + vt->reply_len, s, len);
315 vt->reply_len += len;
316}
317
319static void save_cursor(struct vt *vt, struct vt_cursor *save)
320{
321 save->row = vt->row;
322 save->col = vt->col;
323 save->pen = vt->pen;
324 save->charset[0] = vt->charset[0];
325 save->charset[1] = vt->charset[1];
326 save->gl = vt->gl;
327 save->origin = vt->origin;
328 save->wrap = vt->autowrap;
329}
330
332static void restore_cursor(struct vt *vt, struct vt_cursor *save)
333{
334 vt->pen = save->pen;
335 vt->charset[0] = save->charset[0];
336 vt->charset[1] = save->charset[1];
337 vt->gl = save->gl;
338 vt->origin = save->origin;
339 vt->autowrap = save->wrap;
340
341 vt->row = save->row >= vt->rows ? vt->rows - 1 : save->row;
342 vt->col = save->col >= vt->cols ? vt->cols - 1 : save->col;
343 vt->wrap_pending = 0;
344}
345
347static void clear_screen(struct vt *vt)
348{
349 for (int row = 0; row < vt->rows; ++row)
350 erase(vt, row, 0, vt->cols);
351}
352
354static void alt_screen(struct vt *vt, int on)
355{
356 if (on && vt->screen != vt->alt) {
357 vt->screen = vt->alt;
359 } else if (!on && vt->screen == vt->alt) {
360 vt->screen = vt->main;
361 }
362}
363
366static void soft_reset(struct vt *vt)
367{
368 vt->pen = (struct vt_cell) {.ch = ' ', .fg = DEFAULT_FG, .bg = DEFAULT_BG};
369 vt->top = 0;
370 vt->bottom = vt->rows - 1;
371 vt->autowrap = 1;
372 vt->origin = 0;
373 vt->insert = 0;
374 vt->newline = 0;
375 vt->cursor_visible = 1;
376 vt->cursor_keys = 0;
377 vt->keypad = 0;
378 /* G1 is the line drawing set, as on the Linux console */
379 vt->charset[0] = 'B';
380 vt->charset[1] = '0';
381 vt->gl = 0;
382 vt->wrap_pending = 0;
383
384 save_cursor(vt, &vt->saved);
385 vt->saved.row = vt->saved.col = 0;
386 vt->saved_alt = vt->saved;
387}
388
395void vt_term_reset(struct vt *vt)
396{
397 vt->screen = vt->main;
398 soft_reset(vt);
399 vt->reverse_screen = 0;
400
401 for (int col = 0; col < 256; ++col)
402 tab_set(vt, col, col && col % 8 == 0);
403
405 vt->row = vt->col = 0;
406 vt->state = S_GROUND;
407 vt->utf8_left = 0;
408 vt->last = 0;
409}
410
420int vt_term_init(struct vt *vt, int rows, int cols)
421{
422 size_t size = rows * cols * sizeof(struct vt_cell);
423
424 vt->main = kmalloc(size, &M_VT_SCREEN, 0);
425 vt->alt = kmalloc(size, &M_VT_SCREEN, 0);
426
427 if (!vt->main || !vt->alt) {
428 if (vt->main) kfree(vt->main);
429 if (vt->alt) kfree(vt->alt);
430 vt->main = vt->alt = NULL;
431 return -ENOMEM;
432 }
433
434 vt->rows = rows;
435 vt->cols = cols;
436
437 /* clear the alternate screen; the reset clears the main one */
438 vt->screen = vt->alt;
439 vt->pen = (struct vt_cell) {.ch = ' ', .fg = DEFAULT_FG, .bg = DEFAULT_BG};
442
443 return 0;
444}
445
447static int param(struct vt *vt, int i, int def)
448{
449 return i < vt->nparams && vt->params[i] ? vt->params[i] : def;
450}
451
453static void sgr(struct vt *vt)
454{
455 struct vt_cell *pen = &vt->pen;
456
457 for (int i = 0; i < vt->nparams; ++i) {
458 int p = vt->params[i];
459
460 switch (p) {
461 case 0:
462 pen->attr = 0;
463 pen->fg = DEFAULT_FG;
464 pen->bg = DEFAULT_BG;
465 break;
466 case 1: pen->attr |= VT_BOLD; break;
467 case 2: pen->attr |= VT_DIM; break;
468 case 3: pen->attr |= VT_ITALIC; break;
469 case 4: pen->attr |= VT_UNDERLINE; break;
470 case 5: pen->attr |= VT_BLINK; break;
471 case 7: pen->attr |= VT_REVERSE; break;
472 case 8: pen->attr |= VT_INVISIBLE; break;
473 case 21:
474 case 22: pen->attr &= ~(VT_BOLD | VT_DIM); break;
475 case 23: pen->attr &= ~VT_ITALIC; break;
476 case 24: pen->attr &= ~VT_UNDERLINE; break;
477 case 25: pen->attr &= ~VT_BLINK; break;
478 case 27: pen->attr &= ~VT_REVERSE; break;
479 case 28: pen->attr &= ~VT_INVISIBLE; break;
480 case 39: pen->fg = DEFAULT_FG; break;
481 case 49: pen->bg = DEFAULT_BG; break;
482 case 38:
483 case 48: {
484 /* 38;5;n (one of 256) or 38;2;r;g;b (24 bits) */
485 int color = -1;
486
487 if (i + 2 < vt->nparams && vt->params[i + 1] == 5) {
488 color = vt->params[i + 2] & 0xff;
489 i += 2;
490 } else if (i + 4 < vt->nparams && vt->params[i + 1] == 2) {
491 color = rgb_color(vt->params[i + 2], vt->params[i + 3], vt->params[i + 4]);
492 i += 4;
493 } else {
494 i = vt->nparams;
495 }
496
497 if (color >= 0) {
498 if (p == 38)
499 pen->fg = color;
500 else
501 pen->bg = color;
502 }
503 break;
504 }
505 default:
506 if (p >= 30 && p <= 37)
507 pen->fg = p - 30;
508 else if (p >= 40 && p <= 47)
509 pen->bg = p - 40;
510 else if (p >= 90 && p <= 97)
511 pen->fg = p - 90 + 8;
512 else if (p >= 100 && p <= 107)
513 pen->bg = p - 100 + 8;
514 }
515 }
516}
517
520static void decset(struct vt *vt, int set)
521{
522 for (int i = 0; i < vt->nparams; ++i) {
523 switch (vt->params[i]) {
524 case 1: vt->cursor_keys = set; break;
525 case 5: vt->reverse_screen = set; break;
526 case 6:
527 vt->origin = set;
528 move_to(vt, vt->origin ? vt->top : 0, 0);
529 break;
530 case 7: vt->autowrap = set; break;
531 case 25: vt->cursor_visible = set; break;
532 case 47:
533 case 1047:
534 alt_screen(vt, set);
535 break;
536 case 1048:
537 if (set)
538 save_cursor(vt, &vt->saved);
539 else
540 restore_cursor(vt, &vt->saved);
541 break;
542 case 1049:
543 if (set) {
545 alt_screen(vt, 1);
546 } else {
547 alt_screen(vt, 0);
549 }
550 break;
551 }
552 }
553}
554
557static void csi(struct vt *vt, char f)
558{
559 int n = param(vt, 0, 1);
560 char buf[24];
561
562 if (vt->intermediate) {
563 /* CSI ! p (DECSTR); the others (the cursor style, CSI SP q) are
564 * ignored */
565 if (vt->intermediate == '!' && f == 'p')
566 soft_reset(vt);
567 return;
568 }
569
570 if (vt->private == '?') {
571 if (f == 'h' || f == 'l')
572 decset(vt, f == 'h');
573 return;
574 }
575
576 if (vt->private)
577 return;
578
579 switch (f) {
580 case '@': { /* ICH */
581 int count = MIN(n, vt->cols - vt->col);
582 struct vt_cell *c = cell(vt, vt->row, vt->col);
583
584 memmove(c + count, c, (vt->cols - vt->col - count) * sizeof(struct vt_cell));
585 erase(vt, vt->row, vt->col, vt->col + count);
586 vt->wrap_pending = 0;
587 break;
588 }
589 case 'A': move_rows(vt, -n); break;
590 case 'B':
591 case 'e': move_rows(vt, n); break;
592 case 'C':
593 case 'a': vt->col = MIN(vt->col + n, vt->cols - 1); vt->wrap_pending = 0; break;
594 case 'D': vt->col = MAX(vt->col - n, 0); vt->wrap_pending = 0; break;
595 case 'E': move_rows(vt, n); vt->col = 0; break;
596 case 'F': move_rows(vt, -n); vt->col = 0; break;
597 case 'G':
598 case '`': vt->col = MIN(n, vt->cols) - 1; vt->wrap_pending = 0; break;
599 case 'H':
600 case 'f':
601 move_to(vt, (vt->origin ? vt->top : 0) + param(vt, 0, 1) - 1, param(vt, 1, 1) - 1);
602 break;
603 case 'I': tab(vt, n); break;
604 case 'Z': tab(vt, -n); break;
605 case 'J':
606 switch (param(vt, 0, 0)) {
607 case 0:
608 erase(vt, vt->row, vt->col, vt->cols);
609 for (int row = vt->row + 1; row < vt->rows; ++row)
610 erase(vt, row, 0, vt->cols);
611 break;
612 case 1:
613 for (int row = 0; row < vt->row; ++row)
614 erase(vt, row, 0, vt->cols);
615 erase(vt, vt->row, 0, vt->col + 1);
616 break;
617 case 2:
619 break;
620 }
621 break;
622 case 'K':
623 switch (param(vt, 0, 0)) {
624 case 0: erase(vt, vt->row, vt->col, vt->cols); break;
625 case 1: erase(vt, vt->row, 0, vt->col + 1); break;
626 case 2: erase(vt, vt->row, 0, vt->cols); break;
627 }
628 break;
629 case 'L': /* IL */
630 if (vt->row >= vt->top && vt->row <= vt->bottom) {
631 scroll_down(vt, vt->row, vt->bottom, n);
632 vt->col = 0;
633 vt->wrap_pending = 0;
634 }
635 break;
636 case 'M': /* DL */
637 if (vt->row >= vt->top && vt->row <= vt->bottom) {
638 scroll_up(vt, vt->row, vt->bottom, n);
639 vt->col = 0;
640 vt->wrap_pending = 0;
641 }
642 break;
643 case 'P': { /* DCH */
644 int count = MIN(n, vt->cols - vt->col);
645 struct vt_cell *c = cell(vt, vt->row, vt->col);
646
647 memmove(c, c + count, (vt->cols - vt->col - count) * sizeof(struct vt_cell));
648 erase(vt, vt->row, vt->cols - count, vt->cols);
649 vt->wrap_pending = 0;
650 break;
651 }
652 case 'X': /* ECH */
653 erase(vt, vt->row, vt->col, MIN(vt->col + n, vt->cols));
654 vt->wrap_pending = 0;
655 break;
656 case 'b': /* REP */
657 if (vt->last)
658 while (n--)
659 put_glyph(vt, vt->last);
660 break;
661 case 'c': /* DA: a VT102, as the Linux console says */
662 if (!param(vt, 0, 0))
663 reply(vt, "\033[?6c");
664 break;
665 case 'd': /* VPA */
666 move_to(vt, (vt->origin ? vt->top : 0) + n - 1, vt->col);
667 break;
668 case 'g': /* TBC */
669 if (param(vt, 0, 0) == 0)
670 tab_set(vt, vt->col, 0);
671 else if (param(vt, 0, 0) == 3)
672 for (int col = 0; col < 256; ++col)
673 tab_set(vt, col, 0);
674 break;
675 case 'h':
676 case 'l':
677 for (int i = 0; i < vt->nparams; ++i) {
678 if (vt->params[i] == 4)
679 vt->insert = f == 'h';
680 else if (vt->params[i] == 20)
681 vt->newline = f == 'h';
682 }
683 break;
684 case 'm':
685 sgr(vt);
686 break;
687 case 'n': /* DSR */
688 if (param(vt, 0, 0) == 5) {
689 reply(vt, "\033[0n");
690 } else if (param(vt, 0, 0) == 6) {
691 snprintf(buf, sizeof(buf), "\033[%d;%dR",
692 vt->row - (vt->origin ? vt->top : 0) + 1, vt->col + 1);
693 reply(vt, buf);
694 }
695 break;
696 case 'r': { /* DECSTBM */
697 int top = param(vt, 0, 1) - 1;
698 int bottom = param(vt, 1, vt->rows) - 1;
699
700 if (bottom >= vt->rows)
701 bottom = vt->rows - 1;
702
703 if (top < bottom) {
704 vt->top = top;
705 vt->bottom = bottom;
706 move_to(vt, vt->origin ? vt->top : 0, 0);
707 }
708 break;
709 }
710 case 's':
711 save_cursor(vt, &vt->saved);
712 break;
713 case 'u':
714 restore_cursor(vt, &vt->saved);
715 break;
716 }
717}
718
720static void esc(struct vt *vt, char c)
721{
722 vt->state = S_GROUND;
723
724 switch (c) {
725 case '[':
726 vt->state = S_CSI;
727 vt->params[0] = 0;
728 vt->nparams = 1;
729 vt->private = 0;
730 vt->intermediate = 0;
731 break;
732 case ']':
733 vt->state = S_OSC;
734 vt->nparams = 0; /* counts the string's characters so far */
735 break;
736 case 'P':
737 case 'X':
738 case '^':
739 case '_':
740 vt->state = S_STRING;
741 break;
742 case '(':
743 case ')':
744 case '*':
745 case '+':
746 vt->state = S_CHARSET;
747 vt->intermediate = c;
748 break;
749 case '#':
750 vt->state = S_HASH;
751 break;
752 case '%':
753 vt->state = S_PERCENT;
754 break;
755 case '7': save_cursor(vt, &vt->saved); break;
756 case '8': restore_cursor(vt, &vt->saved); break;
757 case 'D': linefeed(vt); break;
758 case 'E': vt->col = 0; linefeed(vt); break;
759 case 'H': tab_set(vt, vt->col, 1); break;
760 case 'M': reverse_index(vt); break;
761 case 'Z': reply(vt, "\033[?6c"); break;
762 case 'c': vt_term_reset(vt); break;
763 case '=': vt->keypad = 1; break;
764 case '>': vt->keypad = 0; break;
765 }
766}
767
769static void control(struct vt *vt, char c)
770{
771 switch (c) {
772 case '\b':
773 if (vt->col > 0)
774 vt->col--;
775 vt->wrap_pending = 0;
776 break;
777 case '\t':
778 tab(vt, 1);
779 break;
780 case '\n':
781 case '\v':
782 case '\f':
783 if (vt->newline)
784 vt->col = 0;
785 linefeed(vt);
786 break;
787 case '\r':
788 vt->col = 0;
789 vt->wrap_pending = 0;
790 break;
791 case 0x0e: /* SO */
792 vt->gl = 1;
793 break;
794 case 0x0f: /* SI */
795 vt->gl = 0;
796 break;
797 case 0x18: /* CAN */
798 case 0x1a: /* SUB */
799 vt->state = S_GROUND;
800 break;
801 case 0x1b:
802 vt->state = S_ESC;
803 break;
804 }
805}
806
809static void utf8(struct vt *vt, uint8_t c)
810{
811 if (c < 0x80) {
812 vt->utf8_left = 0;
813 put_char(vt, c);
814 } else if ((c & 0xc0) == 0x80) {
815 if (!vt->utf8_left) {
816 put_glyph(vt, '?');
817 return;
818 }
819
820 vt->utf8 = (vt->utf8 << 6) | (c & 0x3f);
821
822 /* C1 controls aren't shown */
823 if (!--vt->utf8_left && (vt->utf8 < 0x80 || vt->utf8 > 0x9f))
824 put_char(vt, vt->utf8);
825 } else {
826 if (vt->utf8_left)
827 put_glyph(vt, '?');
828
829 if ((c & 0xe0) == 0xc0) {
830 vt->utf8 = c & 0x1f;
831 vt->utf8_left = 1;
832 } else if ((c & 0xf0) == 0xe0) {
833 vt->utf8 = c & 0x0f;
834 vt->utf8_left = 2;
835 } else if ((c & 0xf8) == 0xf0) {
836 vt->utf8 = c & 0x07;
837 vt->utf8_left = 3;
838 } else {
839 vt->utf8_left = 0;
840 put_glyph(vt, '?');
841 }
842 }
843}
844
846static int hexdigit(char c)
847{
848 return c >= '0' && c <= '9' ? c - '0' :
849 c >= 'a' && c <= 'f' ? c - 'a' + 10 :
850 c >= 'A' && c <= 'F' ? c - 'A' + 10 : -1;
851}
852
855static void term_char(struct vt *vt, uint8_t c)
856{
857 switch (vt->state) {
858 case S_OSC:
859 /* the Linux palette sequences: ESC ] P nrrggbb, ESC ] R */
860 if (!vt->nparams && c == 'P') {
861 vt->state = S_PALETTE;
862 return;
863 }
864
865 if (!vt->nparams && c == 'R') {
866 vt->state = S_GROUND;
867 return;
868 }
869
870 vt->nparams++;
871 /* fall through */
872 case S_STRING:
873 if (c == 0x07 || c == 0x18 || c == 0x1a)
874 vt->state = S_GROUND;
875 else if (c == 0x1b)
877 return;
878 case S_STRING_ESC:
879 if (c == '\\') {
880 vt->state = S_GROUND;
881 return;
882 }
883
884 vt->state = S_ESC;
885 break;
886 case S_PALETTE:
887 /* palettes aren't supported: the digits are skipped */
888 if (hexdigit(c) < 0 || ++vt->nparams == 7)
889 vt->state = S_GROUND;
890 return;
891 }
892
893 if (c < 0x20) {
894 control(vt, c);
895 return;
896 }
897
898 if (c == 0x7f)
899 return;
900
901 switch (vt->state) {
902 case S_GROUND:
903 utf8(vt, c);
904 break;
905 case S_ESC:
906 esc(vt, c);
907 break;
908 case S_CHARSET:
909 if (vt->intermediate == '(')
910 vt->charset[0] = c;
911 else if (vt->intermediate == ')')
912 vt->charset[1] = c;
913 vt->state = S_GROUND;
914 break;
915 case S_HASH:
916 /* DECALN: the screen is filled with E */
917 if (c == '8') {
918 for (int row = 0; row < vt->rows; ++row)
919 for (int col = 0; col < vt->cols; ++col)
920 *cell(vt, row, col) = (struct vt_cell) {.ch = 'E', .fg = DEFAULT_FG, .bg = DEFAULT_BG};
921 move_to(vt, 0, 0);
922 }
923 vt->state = S_GROUND;
924 break;
925 case S_PERCENT:
926 /* UTF-8 is always on: nothing to switch */
927 vt->state = S_GROUND;
928 break;
929 case S_CSI:
930 if (c >= '0' && c <= '9') {
931 int *p = &vt->params[vt->nparams - 1];
932 if (*p < 10000)
933 *p = *p * 10 + c - '0';
934 } else if (c == ';' || c == ':') {
935 if (vt->nparams < (int) (sizeof(vt->params) / sizeof(vt->params[0])))
936 vt->params[vt->nparams++] = 0;
937 } else if (c >= 0x3c && c <= 0x3f) {
938 vt->private = c;
939 } else if (c >= 0x20 && c <= 0x2f) {
940 vt->intermediate = c;
941 } else {
942 vt->state = S_GROUND;
943 if (c >= 0x40 && c <= 0x7e)
944 csi(vt, c);
945 }
946 break;
947 }
948}
949
957void vt_term_write(struct vt *vt, const char *buf, size_t size)
958{
959 for (size_t i = 0; i < size; ++i)
960 term_char(vt, buf[i]);
961}
#define MIN(a, b)
Definition system.h:11
#define MAX(a, b)
Definition system.h:10
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void * memmove(void *dst, const void *src, size_t n)
Copies n bytes; dst and src may overlap.
Definition libc.c:34
void * memcpy(void *dst, const void *src, size_t n)
Copies n bytes; dst and src must not overlap.
Definition libc.c:11
int snprintf(char *s, size_t n, const char *fmt,...)
Formats a string into a buffer.
Definition snprintf.c:226
#define NULL
Definition stddef.h:27
unsigned int uint32_t
Definition stddef.h:22
unsigned char uint8_t
Definition stddef.h:20
unsigned short int uint16_t
Definition stddef.h:21
A character cell.
Definition vt.h:35
uint8_t fg
Definition vt.h:38
uint8_t ch
Definition vt.h:36
uint8_t bg
Definition vt.h:39
uint8_t attr
Definition vt.h:37
The cursor's state, saved by DECSC.
Definition vt.h:68
int col
Definition vt.h:69
struct vt_cell pen
Definition vt.h:70
int charset[2]
Definition vt.h:71
int wrap
Definition vt.h:72
int gl
Definition vt.h:71
int origin
Definition vt.h:72
int row
Definition vt.h:69
A virtual terminal: its screen, its emulator's state, and its tty.
Definition vt.h:76
int newline
Definition vt.h:94
uint8_t last
Definition vt.h:110
int reverse_screen
Definition vt.h:95
int rows
Definition vt.h:78
uint32_t utf8
Definition vt.h:113
size_t reply_len
Definition vt.h:118
int params[16]
Definition vt.h:108
int row
Definition vt.h:85
char reply[32]
Definition vt.h:117
struct vt_cursor saved saved_alt
Definition vt.h:104
struct vt_cell * main
Definition vt.h:81
int col
Definition vt.h:85
int cursor_visible
Definition vt.h:94
int wrap_pending
Definition vt.h:85
int cursor_keys
Definition vt.h:94
struct vt_cell * screen
Definition vt.h:81
uint8_t tabs[32]
Definition vt.h:102
int top
Definition vt.h:91
struct vt_cell pen
Definition vt.h:88
int origin
Definition vt.h:94
int bottom
Definition vt.h:91
int cols
Definition vt.h:78
int keypad
Definition vt.h:95
int utf8_left
Definition vt.h:114
int nparams
Definition vt.h:108
int autowrap
Definition vt.h:94
int charset[2]
Definition vt.h:99
char intermediate
Definition vt.h:109
int state
Definition vt.h:107
int insert
Definition vt.h:94
struct vt_cell * alt
Definition vt.h:81
int gl
Definition vt.h:99
char private
Definition vt.h:109
static void put_char(struct vt *vt, uint32_t u)
Puts a character at the cursor, through the character set in use.
Definition term.c:284
static void linefeed(struct vt *vt)
Moves the cursor down a row, scrolling at the region's bottom.
Definition term.c:202
static uint8_t rgb_color(int r, int g, int b)
Returns the closest colour of the palette's 6 x 6 x 6 cube or greys, for a 24-bit colour.
Definition term.c:106
#define DEFAULT_BG
Definition term.c:36
static void tab_set(struct vt *vt, int col, int set)
Sets or clears the tab stop at a column.
Definition term.c:230
static const uint8_t dec_graphics[]
Definition term.c:39
static const uint16_t cp437[128]
Definition term.c:47
static void reverse_index(struct vt *vt)
Moves the cursor up a row, scrolling at the region's top.
Definition term.c:213
static void put_glyph(struct vt *vt, uint8_t ch)
Puts a glyph at the cursor, in the current colours and attributes, and moves the cursor on,...
Definition term.c:261
static void erase(struct vt *vt, int row, int from, int to)
Erases the cells of a row from column from up to to.
Definition term.c:136
static void csi(struct vt *vt, char f)
Runs a CSI sequence whose final character is f, with its parameters.
Definition term.c:557
static void sgr(struct vt *vt)
Sets the pen's colours and attributes (SGR, CSI ... m).
Definition term.c:453
static void soft_reset(struct vt *vt)
Sets the modes and the pen back to the defaults, but not the screen (DECSTR, a soft reset).
Definition term.c:366
static void decset(struct vt *vt, int set)
Sets or resets DEC private modes.
Definition term.c:520
static const uint16_t cp437_symbols[32]
Definition term.c:67
static void term_char(struct vt *vt, uint8_t c)
Takes one byte of what programs write, in the state of the sequence it may be in.
Definition term.c:855
void vt_term_write(struct vt *vt, const char *buf, size_t size)
Takes what programs write to a terminal, and changes its screen.
Definition term.c:957
static const struct @306303231117275265135041376256327001361236134117 unicode_extra[]
static int tab_stop(struct vt *vt, int col)
Returns whether there is a tab stop at a column.
Definition term.c:224
#define DEFAULT_FG
Definition term.c:35
static void alt_screen(struct vt *vt, int on)
Shows the alternate screen (xterm's), cleared, or the main one again.
Definition term.c:354
void vt_term_reset(struct vt *vt)
Resets a terminal (RIS, ESC c): the defaults, the tab stops every 8 columns, the main screen cleared,...
Definition term.c:395
static void esc(struct vt *vt, char c)
Runs an escape sequence, ESC c, or starts a longer one.
Definition term.c:720
static void move_to(struct vt *vt, int row, int col)
Moves the cursor, within the screen, or within the scrolling region in origin mode.
Definition term.c:174
static struct vt_cell * cell(struct vt *vt, int row, int col)
Returns a cell of the screen.
Definition term.c:123
static void scroll_down(struct vt *vt, int top, int bottom, int n)
Scrolls rows top to bottom down by n rows: n erased rows come at the top.
Definition term.c:160
static void utf8(struct vt *vt, uint8_t c)
Decodes UTF-8, a byte at a time, and puts each character.
Definition term.c:809
static void control(struct vt *vt, char c)
Runs a control character.
Definition term.c:769
static void save_cursor(struct vt *vt, struct vt_cursor *save)
Saves the cursor: its position, colours, character sets and modes.
Definition term.c:319
int vt_term_init(struct vt *vt, int rows, int cols)
Sets a terminal's screens up, the main one and the alternate one, both cleared, and resets it.
Definition term.c:420
@ S_PERCENT
Definition term.c:27
@ S_STRING
Definition term.c:31
@ S_PALETTE
Definition term.c:30
@ S_CSI
Definition term.c:28
@ S_STRING_ESC
Definition term.c:32
@ S_ESC
Definition term.c:24
@ S_CHARSET
Definition term.c:25
@ S_GROUND
Definition term.c:23
@ S_OSC
Definition term.c:29
@ S_HASH
Definition term.c:26
static struct vt_cell blank(struct vt *vt)
Returns an erased cell: it takes the current colours, as the Linux console does ("bce").
Definition term.c:130
static void clear_screen(struct vt *vt)
Erases the whole screen.
Definition term.c:347
static void move_rows(struct vt *vt, int n)
Moves the cursor up or down n rows.
Definition term.c:186
static int param(struct vt *vt, int i, int def)
Returns a sequence's parameter i, or def if it is 0 or missing.
Definition term.c:447
static void reply(struct vt *vt, const char *s)
Queues a reply to a request (DSR, DA), which vt.c gives the terminal as input.
Definition term.c:307
static void scroll_up(struct vt *vt, int top, int bottom, int n)
Scrolls rows top to bottom up by n rows: n erased rows come at the bottom.
Definition term.c:146
static void tab(struct vt *vt, int n)
Moves the cursor n tab stops forward, or back if n is negative.
Definition term.c:242
static void restore_cursor(struct vt *vt, struct vt_cursor *save)
Restores a saved cursor, within the screen.
Definition term.c:332
static uint8_t unicode_glyph(uint32_t u)
Returns the code page 437 glyph that shows a character best, or '?
Definition term.c:84
static int hexdigit(char c)
Returns a hex digit's value, or -1.
Definition term.c:846
#define VT_ITALIC
Definition vt.h:31
#define VT_UNDERLINE
Definition vt.h:27
#define VT_BLINK
Definition vt.h:28
#define VT_REVERSE
Definition vt.h:29
#define VT_DIM
Definition vt.h:26
#define VT_INVISIBLE
Definition vt.h:30
#define VT_BOLD
Definition vt.h:25