@samitouri / QOSamiQemu / commits / c3cb2067d9

ui/console-vc: move VT100 emulation into separate unit

Move the VT100 terminal emulation code into dedicated ui/vt100.c and ui/vt100.h files, completing the extraction from console-vc.c started in the previous patches. This makes the VT100 layer a self-contained module that can be reused independently of the chardev/console infrastructure. The code is moved as-is, with minor coding style fixes (adding missing braces, fixing whitespace) applied during the move. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Marc-André Lureau committed Feb 22, 2026 at 19:46 UTC c3cb2067d99f12d88dc87b44ba200b2c018e91f2
7 files changed +1086 -1034
ui/console-priv.h
-1
@@ -30,7 +30,6 @@ struct QemuConsole {
30 };
31
32 void qemu_text_console_update_size(QemuTextConsole *c);
33 -void vt100_update_cursor(void);
33 void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym);
34
35 #endif
ui/console-vc-stubs.c
+1
@@ -9,6 +9,7 @@
9 #include "qemu/option.h"
10 #include "chardev/char.h"
11 #include "ui/console-priv.h"
12 +#include "vt100.h"
13
14 void qemu_text_console_update_size(QemuTextConsole *c)
15 {
ui/console-vc.c
+1 -1032
@@ -6,93 +6,17 @@
6
7 #include "chardev/char.h"
8 #include "qapi/error.h"
9 -#include "qemu/fifo8.h"
9 #include "qemu/option.h"
10 #include "qemu/queue.h"
11 #include "qom/compat-properties.h"
12 #include "ui/console.h"
13 #include "ui/vgafont.h"
15 -#include "ui/cp437.h"
14 +#include "ui/vt100.h"
15
16 #include "pixman.h"
17 #include "trace.h"
18 #include "console-priv.h"
19
21 -#define DEFAULT_BACKSCROLL 512
22 -#define CONSOLE_CURSOR_PERIOD 500
23 -
24 -typedef struct TextAttributes {
25 - uint8_t fgcol:4;
26 - uint8_t bgcol:4;
27 - uint8_t bold:1;
28 - uint8_t uline:1;
29 - uint8_t blink:1;
30 - uint8_t invers:1;
31 - uint8_t unvisible:1;
32 -} TextAttributes;
33 -
34 -#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
35 - .fgcol = QEMU_COLOR_WHITE, \
36 - .bgcol = QEMU_COLOR_BLACK \
37 -})
38 -
39 -typedef struct TextCell {
40 - uint8_t ch;
41 - TextAttributes t_attrib;
42 -} TextCell;
43 -
44 -#define MAX_ESC_PARAMS 3
45 -
46 -enum TTYState {
47 - TTY_STATE_NORM,
48 - TTY_STATE_ESC,
49 - TTY_STATE_CSI,
50 - TTY_STATE_G0,
51 - TTY_STATE_G1,
52 - TTY_STATE_OSC,
53 -};
54 -
55 -typedef struct QemuVT100 QemuVT100;
56 -
57 -struct QemuVT100 {
58 - pixman_image_t *image;
59 - void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height);
60 -
61 - ChardevVCEncoding encoding;
62 - int width;
63 - int height;
64 - int total_height;
65 - int backscroll_height;
66 - int x, y;
67 - int y_displayed;
68 - int y_base;
69 - TextCell *cells;
70 - int text_x[2], text_y[2], cursor_invalidate;
71 - int echo;
72 -
73 - int update_x0;
74 - int update_y0;
75 - int update_x1;
76 - int update_y1;
77 -
78 - enum TTYState state;
79 - int esc_params[MAX_ESC_PARAMS];
80 - int nb_esc_params;
81 - uint32_t utf8_state; /* UTF-8 DFA decoder state */
82 - uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
83 - TextAttributes t_attrib; /* currently active text attributes */
84 - TextAttributes t_attrib_saved;
85 - int x_saved, y_saved;
86 - /* fifo for key pressed */
87 - Fifo8 out_fifo;
88 - void (*out_flush)(QemuVT100 *vt);
89 -
90 - QTAILQ_ENTRY(QemuVT100) list;
91 -};
92 -
93 -static QTAILQ_HEAD(QemuVT100Head, QemuVT100) vt100s =
94 - QTAILQ_HEAD_INITIALIZER(vt100s);
95 -
20 typedef struct QemuTextConsole {
21 QemuConsole parent;
22
@@ -120,32 +44,6 @@ struct VCChardev {
44 };
45 typedef struct VCChardev VCChardev;
46
123 -static const pixman_color_t color_table_rgb[2][8] = {
124 - { /* dark */
125 - [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
126 - [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa), /* blue */
127 - [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00), /* green */
128 - [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa), /* cyan */
129 - [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00), /* red */
130 - [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa), /* magenta */
131 - [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00), /* yellow */
132 - [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR_GRAY,
133 - },
134 - { /* bright */
135 - [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
136 - [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff), /* blue */
137 - [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00), /* green */
138 - [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff), /* cyan */
139 - [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00), /* red */
140 - [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff), /* magenta */
141 - [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00), /* yellow */
142 - [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff), /* white */
143 - }
144 -};
145 -
146 -static bool cursor_visible_phase;
147 -static QEMUTimer *cursor_timer;
148 -
47 static char *
48 qemu_text_console_get_label(const QemuConsole *c)
49 {
@@ -154,157 +52,6 @@ qemu_text_console_get_label(const QemuConsole *c)
52 return tc->chr ? g_strdup(tc->chr->label) : NULL;
53 }
54
157 -static void image_fill_rect(pixman_image_t *image, int posx, int posy,
158 - int width, int height, pixman_color_t color)
159 -{
160 - pixman_rectangle16_t rect = {
161 - .x = posx, .y = posy, .width = width, .height = height
162 - };
163 -
164 - pixman_image_fill_rectangles(PIXMAN_OP_SRC, image, &color, 1, &rect);
165 -}
166 -
167 -/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
168 -static void image_bitblt(pixman_image_t *image,
169 - int xs, int ys, int xd, int yd, int w, int h)
170 -{
171 - pixman_image_composite(PIXMAN_OP_SRC,
172 - image, NULL, image,
173 - xs, ys, 0, 0, xd, yd, w, h);
174 -}
175 -
176 -static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch,
177 - TextAttributes *t_attrib)
178 -{
179 - static pixman_image_t *glyphs[256];
180 - pixman_color_t fgcol, bgcol;
181 -
182 - assert(vt->image);
183 - if (t_attrib->invers) {
184 - bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
185 - fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
186 - } else {
187 - fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
188 - bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
189 - }
190 -
191 - if (!glyphs[ch]) {
192 - glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
193 - }
194 - qemu_pixman_glyph_render(glyphs[ch], vt->image,
195 - &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
196 -}
197 -
198 -static void vt100_invalidate_xy(QemuVT100 *vt, int x, int y)
199 -{
200 - if (vt->update_x0 > x * FONT_WIDTH) {
201 - vt->update_x0 = x * FONT_WIDTH;
202 - }
203 - if (vt->update_y0 > y * FONT_HEIGHT) {
204 - vt->update_y0 = y * FONT_HEIGHT;
205 - }
206 - if (vt->update_x1 < (x + 1) * FONT_WIDTH) {
207 - vt->update_x1 = (x + 1) * FONT_WIDTH;
208 - }
209 - if (vt->update_y1 < (y + 1) * FONT_HEIGHT) {
210 - vt->update_y1 = (y + 1) * FONT_HEIGHT;
211 - }
212 -}
213 -
214 -static void vt100_show_cursor(QemuVT100 *vt, int show)
215 -{
216 - TextCell *c;
217 - int y, y1;
218 - int x = vt->x;
219 -
220 - vt->cursor_invalidate = 1;
221 -
222 - if (x >= vt->width) {
223 - x = vt->width - 1;
224 - }
225 - y1 = (vt->y_base + vt->y) % vt->total_height;
226 - y = y1 - vt->y_displayed;
227 - if (y < 0) {
228 - y += vt->total_height;
229 - }
230 - if (y < vt->height) {
231 - c = &vt->cells[y1 * vt->width + x];
232 - if (show && cursor_visible_phase) {
233 - TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
234 - t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
235 - vt100_putcharxy(vt, x, y, c->ch, &t_attrib);
236 - } else {
237 - vt100_putcharxy(vt, x, y, c->ch, &(c->t_attrib));
238 - }
239 - vt100_invalidate_xy(vt, x, y);
240 - }
241 -}
242 -
243 -static void vt100_image_update(QemuVT100 *vt, int x, int y, int width, int height)
244 -{
245 - vt->image_update(vt, x, y, width, height);
246 -}
247 -
248 -static void vt100_refresh(QemuVT100 *vt)
249 -{
250 - TextCell *c;
251 - int x, y, y1;
252 - int w = pixman_image_get_width(vt->image);
253 - int h = pixman_image_get_height(vt->image);
254 -
255 - vt->text_x[0] = 0;
256 - vt->text_y[0] = 0;
257 - vt->text_x[1] = vt->width - 1;
258 - vt->text_y[1] = vt->height - 1;
259 - vt->cursor_invalidate = 1;
260 -
261 - image_fill_rect(vt->image, 0, 0, w, h,
262 - color_table_rgb[0][QEMU_COLOR_BLACK]);
263 - y1 = vt->y_displayed;
264 - for (y = 0; y < vt->height; y++) {
265 - c = vt->cells + y1 * vt->width;
266 - for (x = 0; x < vt->width; x++) {
267 - vt100_putcharxy(vt, x, y, c->ch,
268 - &(c->t_attrib));
269 - c++;
270 - }
271 - if (++y1 == vt->total_height) {
272 - y1 = 0;
273 - }
274 - }
275 - vt100_show_cursor(vt, 1);
276 - vt100_image_update(vt, 0, 0, w, h);
277 -}
278 -
279 -static void vt100_scroll(QemuVT100 *vt, int ydelta)
280 -{
281 - int i, y1;
282 -
283 - if (ydelta > 0) {
284 - for(i = 0; i < ydelta; i++) {
285 - if (vt->y_displayed == vt->y_base)
286 - break;
287 - if (++vt->y_displayed == vt->total_height)
288 - vt->y_displayed = 0;
289 - }
290 - } else {
291 - ydelta = -ydelta;
292 - i = vt->backscroll_height;
293 - if (i > vt->total_height - vt->height)
294 - i = vt->total_height - vt->height;
295 - y1 = vt->y_base - i;
296 - if (y1 < 0)
297 - y1 += vt->total_height;
298 - for(i = 0; i < ydelta; i++) {
299 - if (vt->y_displayed == y1)
300 - break;
301 - if (--vt->y_displayed < 0)
302 - vt->y_displayed = vt->total_height - 1;
303 - }
304 - }
305 - vt100_refresh(vt);
306 -}
307 -
55 static void qemu_text_console_out_flush(QemuTextConsole *s)
56 {
57 uint32_t len, avail;
@@ -322,64 +69,6 @@ static void qemu_text_console_out_flush(QemuTextConsole *s)
69 }
70 }
71
325 -static void vt100_write(QemuVT100 *vt, const void *buf, size_t len)
326 -{
327 - uint32_t num_free;
328 -
329 - num_free = fifo8_num_free(&vt->out_fifo);
330 - fifo8_push_all(&vt->out_fifo, buf, MIN(num_free, len));
331 - vt->out_flush(vt);
332 -}
333 -
334 -static size_t vt100_input(QemuVT100 *vt, const uint8_t *buf, size_t len);
335 -
336 -static void vt100_keysym(QemuVT100 *vt, int keysym)
337 -{
338 - uint8_t buf[16], *q;
339 - int c;
340 -
341 - switch(keysym) {
342 - case QEMU_KEY_CTRL_UP:
343 - vt100_scroll(vt, -1);
344 - break;
345 - case QEMU_KEY_CTRL_DOWN:
346 - vt100_scroll(vt, 1);
347 - break;
348 - case QEMU_KEY_CTRL_PAGEUP:
349 - vt100_scroll(vt, -10);
350 - break;
351 - case QEMU_KEY_CTRL_PAGEDOWN:
352 - vt100_scroll(vt, 10);
353 - break;
354 - default:
355 - /* convert the QEMU keysym to VT100 key string */
356 - q = buf;
357 - if (keysym >= 0xe100 && keysym <= 0xe11f) {
358 - *q++ = '\033';
359 - *q++ = '[';
360 - c = keysym - 0xe100;
361 - if (c >= 10)
362 - *q++ = '0' + (c / 10);
363 - *q++ = '0' + (c % 10);
364 - *q++ = '~';
365 - } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
366 - *q++ = '\033';
367 - *q++ = '[';
368 - *q++ = keysym & 0xff;
369 - } else if (vt->echo && (keysym == '\r' || keysym == '\n')) {
370 - vt100_input(vt, (uint8_t *)"\r", 1);
371 - *q++ = '\n';
372 - } else {
373 - *q++ = keysym;
374 - }
375 - if (vt->echo) {
376 - vt100_input(vt, buf, q - buf);
377 - }
378 - vt100_write(vt, buf, q - buf);
379 - break;
380 - }
381 -
382 -}
72 /* called when an ascii key is pressed */
73 void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
74 {
@@ -414,678 +103,10 @@ static void text_console_update(void *opaque, uint32_t *chardata)
103 }
104 }
105
417 -static void vt100_set_image(QemuVT100 *vt, pixman_image_t *image)
418 -{
419 - TextCell *cells, *c, *c1;
420 - int w1, x, y, last_width, w, h;
421 -
422 - vt->image = image;
423 - w = pixman_image_get_width(image) / FONT_WIDTH;
424 - h = pixman_image_get_height(image) / FONT_HEIGHT;
425 - if (w == vt->width && h == vt->height) {
426 - return;
427 - }
428 -
429 - last_width = vt->width;
430 - vt->width = w;
431 - vt->height = h;
432 -
433 - w1 = MIN(vt->width, last_width);
434 -
435 - cells = g_new(TextCell, vt->width * vt->total_height + 1);
436 - for (y = 0; y < vt->total_height; y++) {
437 - c = &cells[y * vt->width];
438 - if (w1 > 0) {
439 - c1 = &vt->cells[y * last_width];
440 - for (x = 0; x < w1; x++) {
441 - *c++ = *c1++;
442 - }
443 - }
444 - for (x = w1; x < vt->width; x++) {
445 - c->ch = ' ';
446 - c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
447 - c++;
448 - }
449 - }
450 - g_free(vt->cells);
451 - vt->cells = cells;
452 -}
453 -
454 -static void vt100_put_lf(QemuVT100 *vt)
455 -{
456 - TextCell *c;
457 - int x, y1;
458 -
459 - vt->y++;
460 - if (vt->y >= vt->height) {
461 - vt->y = vt->height - 1;
462 -
463 - if (vt->y_displayed == vt->y_base) {
464 - if (++vt->y_displayed == vt->total_height)
465 - vt->y_displayed = 0;
466 - }
467 - if (++vt->y_base == vt->total_height)
468 - vt->y_base = 0;
469 - if (vt->backscroll_height < vt->total_height)
470 - vt->backscroll_height++;
471 - y1 = (vt->y_base + vt->height - 1) % vt->total_height;
472 - c = &vt->cells[y1 * vt->width];
473 - for(x = 0; x < vt->width; x++) {
474 - c->ch = ' ';
475 - c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
476 - c++;
477 - }
478 - if (vt->y_displayed == vt->y_base) {
479 - vt->text_x[0] = 0;
480 - vt->text_y[0] = 0;
481 - vt->text_x[1] = vt->width - 1;
482 - vt->text_y[1] = vt->height - 1;
483 -
484 - image_bitblt(vt->image, 0, FONT_HEIGHT, 0, 0,
485 - vt->width * FONT_WIDTH,
486 - (vt->height - 1) * FONT_HEIGHT);
487 - image_fill_rect(vt->image, 0, (vt->height - 1) * FONT_HEIGHT,
488 - vt->width * FONT_WIDTH, FONT_HEIGHT,
489 - color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
490 - vt->update_x0 = 0;
491 - vt->update_y0 = 0;
492 - vt->update_x1 = vt->width * FONT_WIDTH;
493 - vt->update_y1 = vt->height * FONT_HEIGHT;
494 - }
495 - }
496 -}
497 -
498 -/* Set console attributes depending on the current escape codes.
499 - * NOTE: I know this code is not very efficient (checking every color for it
500 - * self) but it is more readable and better maintainable.
501 - */
502 -static void vt100_handle_escape(QemuVT100 *vt)
503 -{
504 - int i;
505 -
506 - for (i = 0; i < vt->nb_esc_params; i++) {
507 - switch (vt->esc_params[i]) {
508 - case 0: /* reset all console attributes to default */
509 - vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
510 - break;
511 - case 1:
512 - vt->t_attrib.bold = 1;
513 - break;
514 - case 4:
515 - vt->t_attrib.uline = 1;
516 - break;
517 - case 5:
518 - vt->t_attrib.blink = 1;
519 - break;
520 - case 7:
521 - vt->t_attrib.invers = 1;
522 - break;
523 - case 8:
524 - vt->t_attrib.unvisible = 1;
525 - break;
526 - case 22:
527 - vt->t_attrib.bold = 0;
528 - break;
529 - case 24:
530 - vt->t_attrib.uline = 0;
531 - break;
532 - case 25:
533 - vt->t_attrib.blink = 0;
534 - break;
535 - case 27:
536 - vt->t_attrib.invers = 0;
537 - break;
538 - case 28:
539 - vt->t_attrib.unvisible = 0;
540 - break;
541 - /* set foreground color */
542 - case 30:
543 - vt->t_attrib.fgcol = QEMU_COLOR_BLACK;
544 - break;
545 - case 31:
546 - vt->t_attrib.fgcol = QEMU_COLOR_RED;
547 - break;
548 - case 32:
549 - vt->t_attrib.fgcol = QEMU_COLOR_GREEN;
550 - break;
551 - case 33:
552 - vt->t_attrib.fgcol = QEMU_COLOR_YELLOW;
553 - break;
554 - case 34:
555 - vt->t_attrib.fgcol = QEMU_COLOR_BLUE;
556 - break;
557 - case 35:
558 - vt->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
559 - break;
560 - case 36:
561 - vt->t_attrib.fgcol = QEMU_COLOR_CYAN;
562 - break;
563 - case 37:
564 - vt->t_attrib.fgcol = QEMU_COLOR_WHITE;
565 - break;
566 - /* set background color */
567 - case 40:
568 - vt->t_attrib.bgcol = QEMU_COLOR_BLACK;
569 - break;
570 - case 41:
571 - vt->t_attrib.bgcol = QEMU_COLOR_RED;
572 - break;
573 - case 42:
574 - vt->t_attrib.bgcol = QEMU_COLOR_GREEN;
575 - break;
576 - case 43:
577 - vt->t_attrib.bgcol = QEMU_COLOR_YELLOW;
578 - break;
579 - case 44:
580 - vt->t_attrib.bgcol = QEMU_COLOR_BLUE;
581 - break;
582 - case 45:
583 - vt->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
584 - break;
585 - case 46:
586 - vt->t_attrib.bgcol = QEMU_COLOR_CYAN;
587 - break;
588 - case 47:
589 - vt->t_attrib.bgcol = QEMU_COLOR_WHITE;
590 - break;
591 - }
592 - }
593 -}
594 -
595 -static void vt100_update_xy(QemuVT100 *vt, int x, int y)
596 -{
597 - TextCell *c;
598 - int y1, y2;
599 -
600 - vt->text_x[0] = MIN(vt->text_x[0], x);
601 - vt->text_x[1] = MAX(vt->text_x[1], x);
602 - vt->text_y[0] = MIN(vt->text_y[0], y);
603 - vt->text_y[1] = MAX(vt->text_y[1], y);
604 -
605 - y1 = (vt->y_base + y) % vt->total_height;
606 - y2 = y1 - vt->y_displayed;
607 - if (y2 < 0) {
608 - y2 += vt->total_height;
609 - }
610 - if (y2 < vt->height) {
611 - if (x >= vt->width) {
612 - x = vt->width - 1;
613 - }
614 - c = &vt->cells[y1 * vt->width + x];
615 - vt100_putcharxy(vt, x, y2, c->ch,
616 - &(c->t_attrib));
617 - vt100_invalidate_xy(vt, x, y2);
618 - }
619 -}
620 -
621 -static void vt100_clear_xy(QemuVT100 *vt, int x, int y)
622 -{
623 - int y1 = (vt->y_base + y) % vt->total_height;
624 - if (x >= vt->width) {
625 - x = vt->width - 1;
626 - }
627 - TextCell *c = &vt->cells[y1 * vt->width + x];
628 - c->ch = ' ';
629 - c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
630 - vt100_update_xy(vt, x, y);
631 -}
632 -
633 -/*
634 - * UTF-8 DFA decoder by Bjoern Hoehrmann.
635 - * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
636 - * See https://github.com/polijan/utf8_decode for details.
637 - *
638 - * SPDX-License-Identifier: MIT
639 - */
640 -#define BH_UTF8_ACCEPT 0
641 -#define BH_UTF8_REJECT 12
642 -
643 -static uint32_t bh_utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
644 -{
645 - static const uint8_t utf8d[] = {
646 - /* character class lookup */
647 - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
648 - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
649 - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
650 - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
651 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
652 - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
653 - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
654 - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
655 -
656 - /* state transition lookup */
657 - 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
658 - 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
659 - 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
660 - 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
661 - 12,36,12,12,12,12,12,12,12,12,12,12,
662 - };
663 - uint32_t type = utf8d[byte];
664 -
665 - *codep = (*state != BH_UTF8_ACCEPT) ?
666 - (byte & 0x3fu) | (*codep << 6) :
667 - (0xffu >> type) & (byte);
668 -
669 - *state = utf8d[256 + *state + type];
670 - return *state;
671 -}
672 -
673 -static void vt100_put_one(QemuVT100 *vt, int ch)
674 -{
675 - TextCell *c;
676 - int y1;
677 - if (vt->x >= vt->width) {
678 - /* line wrap */
679 - vt->x = 0;
680 - vt100_put_lf(vt);
681 - }
682 - y1 = (vt->y_base + vt->y) % vt->total_height;
683 - c = &vt->cells[y1 * vt->width + vt->x];
684 - c->ch = ch;
685 - c->t_attrib = vt->t_attrib;
686 - vt100_update_xy(vt, vt->x, vt->y);
687 - vt->x++;
688 -}
689 -
690 -/* set cursor, checking bounds */
691 -static void vt100_set_cursor(QemuVT100 *vt, int x, int y)
692 -{
693 - if (x < 0) {
694 - x = 0;
695 - }
696 - if (y < 0) {
697 - y = 0;
698 - }
699 - if (y >= vt->height) {
700 - y = vt->height - 1;
701 - }
702 - if (x >= vt->width) {
703 - x = vt->width - 1;
704 - }
705 -
706 - vt->x = x;
707 - vt->y = y;
708 -}
709 -
710 -/**
711 - * vc_csi_P() - (DCH) deletes one or more characters from the cursor
712 - * position to the right. As characters are deleted, the remaining
713 - * characters between the cursor and right margin move to the
714 - * left. Character attributes move with the characters.
715 - */
716 -static void vt100_csi_P(QemuVT100 *vt, unsigned int nr)
717 -{
718 - TextCell *c1, *c2;
719 - unsigned int x1, x2, y;
720 - unsigned int end, len;
721 -
722 - if (!nr) {
723 - nr = 1;
724 - }
725 - if (nr > vt->width - vt->x) {
726 - nr = vt->width - vt->x;
727 - if (!nr) {
728 - return;
729 - }
730 - }
731 -
732 - x1 = vt->x;
733 - x2 = vt->x + nr;
734 - len = vt->width - x2;
735 - if (len) {
736 - y = (vt->y_base + vt->y) % vt->total_height;
737 - c1 = &vt->cells[y * vt->width + x1];
738 - c2 = &vt->cells[y * vt->width + x2];
739 - memmove(c1, c2, len * sizeof(*c1));
740 - for (end = x1 + len; x1 < end; x1++) {
741 - vt100_update_xy(vt, x1, vt->y);
742 - }
743 - }
744 - /* Clear the rest */
745 - for (; x1 < vt->width; x1++) {
746 - vt100_clear_xy(vt, x1, vt->y);
747 - }
748 -}
749 -
750 -/**
751 - * vc_csi_at() - (ICH) inserts `nr` blank characters with the default
752 - * character attribute. The cursor remains at the beginning of the
753 - * blank characters. Text between the cursor and right margin moves to
754 - * the right. Characters scrolled past the right margin are lost.
755 - */
756 -static void vt100_csi_at(QemuVT100 *vt, unsigned int nr)
757 -{
758 - TextCell *c1, *c2;
759 - unsigned int x1, x2, y;
760 - unsigned int end, len;
761 -
762 - if (!nr) {
763 - nr = 1;
764 - }
765 - if (nr > vt->width - vt->x) {
766 - nr = vt->width - vt->x;
767 - if (!nr) {
768 - return;
769 - }
770 - }
771 -
772 - x1 = vt->x + nr;
773 - x2 = vt->x;
774 - len = vt->width - x1;
775 - if (len) {
776 - y = (vt->y_base + vt->y) % vt->total_height;
777 - c1 = &vt->cells[y * vt->width + x1];
778 - c2 = &vt->cells[y * vt->width + x2];
779 - memmove(c1, c2, len * sizeof(*c1));
780 - for (end = x1 + len; x1 < end; x1++) {
781 - vt100_update_xy(vt, x1, vt->y);
782 - }
783 - }
784 - /* Insert blanks */
785 - for (x1 = vt->x; x1 < vt->x + nr; x1++) {
786 - vt100_clear_xy(vt, x1, vt->y);
787 - }
788 -}
789 -
790 -/**
791 - * vt100_save_cursor() - saves cursor position and character attributes.
792 - */
793 -static void vt100_save_cursor(QemuVT100 *vt)
794 -{
795 - vt->x_saved = vt->x;
796 - vt->y_saved = vt->y;
797 - vt->t_attrib_saved = vt->t_attrib;
798 -}
799 -
800 -/**
801 - * vt100_restore_cursor() - restores cursor position and character
802 - * attributes from saved state.
803 - */
804 -static void vt100_restore_cursor(QemuVT100 *vt)
805 -{
806 - vt->x = vt->x_saved;
807 - vt->y = vt->y_saved;
808 - vt->t_attrib = vt->t_attrib_saved;
809 -}
810 -
811 -static void vt100_putchar(QemuVT100 *vt, int ch)
812 -{
813 - int i;
814 - int x, y;
815 - g_autofree char *response = NULL;
816 -
817 - switch (vt->state) {
818 - case TTY_STATE_NORM:
819 - if (ch >= 0x80 && vt->encoding == CHARDEV_VC_ENCODING_UTF8) {
820 - switch (bh_utf8_decode(&vt->utf8_state, &vt->utf8_codepoint, ch)) {
821 - case BH_UTF8_ACCEPT:
822 - vt100_put_one(vt, unicode_to_cp437(vt->utf8_codepoint));
823 - break;
824 - case BH_UTF8_REJECT:
825 - vt->utf8_state = BH_UTF8_ACCEPT;
826 - break;
827 - default:
828 - /* Need more bytes */
829 - break;
830 - }
831 - break;
832 - }
833 - vt->utf8_state = BH_UTF8_ACCEPT;
834 - switch(ch) {
835 - case '\r': /* carriage return */
836 - vt->x = 0;
837 - break;
838 - case '\n': /* newline */
839 - vt100_put_lf(vt);
840 - break;
841 - case '\b': /* backspace */
842 - if (vt->x > 0)
843 - vt->x--;
844 - break;
845 - case '\t': /* tabspace */
846 - if (vt->x + (8 - (vt->x % 8)) > vt->width) {
847 - vt->x = 0;
848 - vt100_put_lf(vt);
849 - } else {
850 - vt->x = vt->x + (8 - (vt->x % 8));
851 - }
852 - break;
853 - case '\a': /* alert aka. bell */
854 - /* TODO: has to be implemented */
855 - break;
856 - case 14:
857 - /* SO (shift out), character set 1 (ignored) */
858 - break;
859 - case 15:
860 - /* SI (shift in), character set 0 (ignored) */
861 - break;
862 - case 27: /* esc (introducing an escape sequence) */
863 - vt->state = TTY_STATE_ESC;
864 - break;
865 - default:
866 - vt100_put_one(vt, ch);
867 - break;
868 - }
869 - break;
870 - case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
871 - if (ch == '[') {
872 - for(i=0;i<MAX_ESC_PARAMS;i++)
873 - vt->esc_params[i] = 0;
874 - vt->nb_esc_params = 0;
875 - vt->state = TTY_STATE_CSI;
876 - } else if (ch == '(') {
877 - vt->state = TTY_STATE_G0;
878 - } else if (ch == ')') {
879 - vt->state = TTY_STATE_G1;
880 - } else if (ch == ']' || ch == 'P' || ch == 'X'
881 - || ch == '^' || ch == '_') {
882 - /* String sequences: OSC, DCS, SOS, PM, APC */
883 - vt->state = TTY_STATE_OSC;
884 - } else if (ch == '7') {
885 - vt100_save_cursor(vt);
886 - vt->state = TTY_STATE_NORM;
887 - } else if (ch == '8') {
888 - vt100_restore_cursor(vt);
889 - vt->state = TTY_STATE_NORM;
890 - } else {
891 - vt->state = TTY_STATE_NORM;
892 - }
893 - break;
894 - case TTY_STATE_CSI: /* handle escape sequence parameters */
895 - if (ch >= '0' && ch <= '9') {
896 - if (vt->nb_esc_params < MAX_ESC_PARAMS) {
897 - int *param = &vt->esc_params[vt->nb_esc_params];
898 - int digit = (ch - '0');
899 -
900 - *param = (*param <= (INT_MAX - digit) / 10) ?
901 - *param * 10 + digit : INT_MAX;
902 - }
903 - } else {
904 - if (vt->nb_esc_params < MAX_ESC_PARAMS)
905 - vt->nb_esc_params++;
906 - if (ch == ';' || ch == '?') {
907 - break;
908 - }
909 - trace_console_putchar_csi(vt->esc_params[0], vt->esc_params[1],
910 - ch, vt->nb_esc_params);
911 - vt->state = TTY_STATE_NORM;
912 - switch(ch) {
913 - case 'A':
914 - /* move cursor up */
915 - if (vt->esc_params[0] == 0) {
916 - vt->esc_params[0] = 1;
917 - }
918 - vt100_set_cursor(vt, vt->x, vt->y - vt->esc_params[0]);
919 - break;
920 - case 'B':
921 - /* move cursor down */
922 - if (vt->esc_params[0] == 0) {
923 - vt->esc_params[0] = 1;
924 - }
925 - vt100_set_cursor(vt, vt->x, vt->y + vt->esc_params[0]);
926 - break;
927 - case 'C':
928 - /* move cursor right */
929 - if (vt->esc_params[0] == 0) {
930 - vt->esc_params[0] = 1;
931 - }
932 - vt100_set_cursor(vt, vt->x + vt->esc_params[0], vt->y);
933 - break;
934 - case 'D':
935 - /* move cursor left */
936 - if (vt->esc_params[0] == 0) {
937 - vt->esc_params[0] = 1;
938 - }
939 - vt100_set_cursor(vt, vt->x - vt->esc_params[0], vt->y);
940 - break;
941 - case 'G':
942 - /* move cursor to column */
943 - vt100_set_cursor(vt, vt->esc_params[0] - 1, vt->y);
944 - break;
945 - case 'f':
946 - case 'H':
947 - /* move cursor to row, column */
948 - vt100_set_cursor(vt, vt->esc_params[1] - 1, vt->esc_params[0] - 1);
949 - break;
950 - case 'J':
951 - switch (vt->esc_params[0]) {
952 - case 0:
953 - /* clear to end of screen */
954 - for (y = vt->y; y < vt->height; y++) {
955 - for (x = 0; x < vt->width; x++) {
956 - if (y == vt->y && x < vt->x) {
957 - continue;
958 - }
959 - vt100_clear_xy(vt, x, y);
960 - }
961 - }
962 - break;
963 - case 1:
964 - /* clear from beginning of screen */
965 - for (y = 0; y <= vt->y; y++) {
966 - for (x = 0; x < vt->width; x++) {
967 - if (y == vt->y && x > vt->x) {
968 - break;
969 - }
970 - vt100_clear_xy(vt, x, y);
971 - }
972 - }
973 - break;
974 - case 2:
975 - /* clear entire screen */
976 - for (y = 0; y < vt->height; y++) {
977 - for (x = 0; x < vt->width; x++) {
978 - vt100_clear_xy(vt, x, y);
979 - }
980 - }
981 - break;
982 - }
983 - break;
984 - case 'K':
985 - switch (vt->esc_params[0]) {
986 - case 0:
987 - /* clear to eol */
988 - for(x = vt->x; x < vt->width; x++) {
989 - vt100_clear_xy(vt, x, vt->y);
990 - }
991 - break;
992 - case 1:
993 - /* clear from beginning of line */
994 - for (x = 0; x <= vt->x && x < vt->width; x++) {
995 - vt100_clear_xy(vt, x, vt->y);
996 - }
997 - break;
998 - case 2:
999 - /* clear entire line */
1000 - for(x = 0; x < vt->width; x++) {
1001 - vt100_clear_xy(vt, x, vt->y);
1002 - }
1003 - break;
1004 - }
1005 - break;
1006 - case 'P':
1007 - vt100_csi_P(vt, vt->esc_params[0]);
1008 - break;
1009 - case 'm':
1010 - vt100_handle_escape(vt);
1011 - break;
1012 - case 'n':
1013 - switch (vt->esc_params[0]) {
1014 - case 5:
1015 - /* report console status (always succeed)*/
1016 - vt100_write(vt, "\033[0n", 4);
1017 - break;
1018 - case 6:
1019 - /* report cursor position */
1020 - response = g_strdup_printf("\033[%d;%dR",
1021 - vt->y + 1, vt->x + 1);
1022 - vt100_write(vt, response, strlen(response));
1023 - break;
1024 - }
1025 - break;
1026 - case 's':
1027 - vt100_save_cursor(vt);
1028 - break;
1029 - case 'u':
1030 - vt100_restore_cursor(vt);
1031 - break;
1032 - case '@':
1033 - vt100_csi_at(vt, vt->esc_params[0]);
1034 - break;
1035 - default:
1036 - trace_console_putchar_unhandled(ch);
1037 - break;
1038 - }
1039 - break;
1040 - }
1041 - break;
1042 - case TTY_STATE_OSC: /* Operating System Command: ESC ] ... BEL/ST */
1043 - if (ch == '\a') {
1044 - /* BEL terminates OSC */
1045 - vt->state = TTY_STATE_NORM;
1046 - } else if (ch == 27) {
1047 - /* ESC might start ST (ESC \) */
1048 - vt->state = TTY_STATE_ESC;
1049 - }
1050 - /* All other bytes are silently consumed */
1051 - break;
1052 - case TTY_STATE_G0: /* set character sets */
1053 - case TTY_STATE_G1: /* set character sets */
1054 - switch (ch) {
1055 - case 'B':
1056 - /* Latin-1 map */
1057 - break;
1058 - }
1059 - vt->state = TTY_STATE_NORM;
1060 - break;
1061 - }
1062 -}
1063 -
106 #define TYPE_CHARDEV_VC "chardev-vc"
107 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
108 TYPE_CHARDEV_VC)
109
1068 -static size_t vt100_input(QemuVT100 *vt, const uint8_t *buf, size_t len)
1069 -{
1070 - int i;
1071 -
1072 - vt->update_x0 = vt->width * FONT_WIDTH;
1073 - vt->update_y0 = vt->height * FONT_HEIGHT;
1074 - vt->update_x1 = 0;
1075 - vt->update_y1 = 0;
1076 - vt100_show_cursor(vt, 0);
1077 - for(i = 0; i < len; i++) {
1078 - vt100_putchar(vt, buf[i]);
1079 - }
1080 - vt100_show_cursor(vt, 1);
1081 - if (vt->update_x0 < vt->update_x1) {
1082 - vt100_image_update(vt, vt->update_x0, vt->update_y0,
1083 - vt->update_x1 - vt->update_x0,
1084 - vt->update_y1 - vt->update_y0);
1085 - }
1086 - return len;
1087 -}
1088 -
110 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
111 {
112 VCChardev *drv = VC_CHARDEV(chr);
@@ -1094,30 +115,6 @@ static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
115 return vt100_input(&s->vt, buf, len);
116 }
117
1097 -void vt100_update_cursor(void)
1098 -{
1099 - QemuVT100 *vt;
1100 -
1101 - cursor_visible_phase = !cursor_visible_phase;
1102 -
1103 - if (QTAILQ_EMPTY(&vt100s)) {
1104 - return;
1105 - }
1106 -
1107 - QTAILQ_FOREACH(vt, &vt100s, list) {
1108 - vt100_refresh(vt);
1109 - }
1110 -
1111 - timer_mod(cursor_timer,
1112 - qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
1113 -}
1114 -
1115 -static void
1116 -cursor_timer_cb(void *opaque)
1117 -{
1118 - vt100_update_cursor();
1119 -}
1120 -
118 static void text_console_invalidate(void *opaque)
119 {
120 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
@@ -1128,13 +125,6 @@ static void text_console_invalidate(void *opaque)
125 vt100_refresh(&s->vt);
126 }
127
1131 -static void vt100_fini(QemuVT100 *vt)
1132 -{
1133 - QTAILQ_REMOVE(&vt100s, vt, list);
1134 - fifo8_destroy(&vt->out_fifo);
1135 - g_free(vt->cells);
1136 -}
1137 -
128 static void
129 qemu_text_console_finalize(Object *obj)
130 {
@@ -1213,27 +203,6 @@ static void text_console_out_flush(QemuVT100 *vt)
203 qemu_text_console_out_flush(console);
204 }
205
1216 -static void vt100_init(QemuVT100 *vt,
1217 - pixman_image_t *image,
1218 - ChardevVCEncoding encoding,
1219 - void (*image_update)(QemuVT100 *vt, int x, int y, int w, int h),
1220 - void (*out_flush)(QemuVT100 *vt))
1221 -{
1222 - if (!cursor_timer) {
1223 - cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);
1224 - }
1225 -
1226 - vt->encoding = encoding;
1227 - QTAILQ_INSERT_HEAD(&vt100s, vt, list);
1228 - fifo8_create(&vt->out_fifo, 16);
1229 - vt->total_height = DEFAULT_BACKSCROLL;
1230 - vt->image_update = image_update;
1231 - vt->out_flush = out_flush;
1232 - /* set current text attributes to default */
1233 - vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
1234 - vt100_set_image(vt, image);
1235 -}
1236 -
206 static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
207 {
208 ChardevVC *vc = backend->u.vc.data;
ui/console.c
+2
@@ -39,6 +39,8 @@
39 #include "system/memory.h"
40 #include "qom/object.h"
41 #include "qemu/memfd.h"
42 +#include "ui/vt100.h"
43 +#include "vgafont.h"
44
45 #include "console-priv.h"
46
ui/meson.build
+3 -1
@@ -3,6 +3,7 @@ system_ss.add(png)
3 system_ss.add(files(
4 'clipboard.c',
5 'console.c',
6 + 'cp437.c',
7 'cursor.c',
8 'display-surface.c',
9 'dmabuf.c',
@@ -17,8 +18,9 @@ system_ss.add(files(
18 'ui-qmp-cmds.c',
19 'util.c',
20 'vgafont.c',
21 + 'vt100.c',
22 ))
21 -system_ss.add(when: pixman, if_true: files('console-vc.c', 'cp437.c'), if_false: files('console-vc-stubs.c'))
23 +system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c'))
24 if dbus_display
25 system_ss.add(files('dbus-module.c'))
26 endif
ui/vt100.c new
+984
@@ -0,0 +1,984 @@
1 +/*
2 + * SPDX-License-Identifier: MIT
3 + * QEMU vt100
4 + */
5 +#include "qemu/osdep.h"
6 +#include "qemu/timer.h"
7 +#include "cp437.h"
8 +#include "vgafont.h"
9 +#include "vt100.h"
10 +
11 +#include "trace.h"
12 +
13 +#define DEFAULT_BACKSCROLL 512
14 +#define CONSOLE_CURSOR_PERIOD 500
15 +
16 +static const pixman_color_t color_table_rgb[2][8] = {
17 + { /* dark */
18 + [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
19 + [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa), /* blue */
20 + [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00), /* green */
21 + [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa), /* cyan */
22 + [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00), /* red */
23 + [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa), /* magenta */
24 + [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00), /* yellow */
25 + [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR_GRAY,
26 + },
27 + { /* bright */
28 + [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
29 + [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff), /* blue */
30 + [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00), /* green */
31 + [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff), /* cyan */
32 + [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00), /* red */
33 + [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff), /* magenta */
34 + [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00), /* yellow */
35 + [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff), /* white */
36 + }
37 +};
38 +
39 +static bool cursor_visible_phase;
40 +static QEMUTimer *cursor_timer;
41 +static QTAILQ_HEAD(QemuVT100Head, QemuVT100) vt100s =
42 + QTAILQ_HEAD_INITIALIZER(vt100s);
43 +
44 +static void image_fill_rect(pixman_image_t *image, int posx, int posy,
45 + int width, int height, pixman_color_t color)
46 +{
47 + pixman_rectangle16_t rect = {
48 + .x = posx, .y = posy, .width = width, .height = height
49 + };
50 +
51 + pixman_image_fill_rectangles(PIXMAN_OP_SRC, image,
52 + &color, 1, &rect);
53 +}
54 +
55 +/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
56 +static void image_bitblt(pixman_image_t *image,
57 + int xs, int ys, int xd, int yd, int w, int h)
58 +{
59 + pixman_image_composite(PIXMAN_OP_SRC,
60 + image, NULL, image,
61 + xs, ys, 0, 0, xd, yd, w, h);
62 +}
63 +
64 +static void vt100_putcharxy(QemuVT100 *vt, int x, int y, int ch,
65 + TextAttributes *t_attrib)
66 +{
67 + static pixman_image_t *glyphs[256];
68 + pixman_color_t fgcol, bgcol;
69 +
70 + assert(vt->image);
71 + if (t_attrib->invers) {
72 + bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
73 + fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
74 + } else {
75 + fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
76 + bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
77 + }
78 +
79 + if (!glyphs[ch]) {
80 + glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
81 + }
82 + qemu_pixman_glyph_render(glyphs[ch], vt->image,
83 + &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
84 +}
85 +
86 +static void vt100_invalidate_xy(QemuVT100 *vt, int x, int y)
87 +{
88 + if (vt->update_x0 > x * FONT_WIDTH) {
89 + vt->update_x0 = x * FONT_WIDTH;
90 + }
91 + if (vt->update_y0 > y * FONT_HEIGHT) {
92 + vt->update_y0 = y * FONT_HEIGHT;
93 + }
94 + if (vt->update_x1 < (x + 1) * FONT_WIDTH) {
95 + vt->update_x1 = (x + 1) * FONT_WIDTH;
96 + }
97 + if (vt->update_y1 < (y + 1) * FONT_HEIGHT) {
98 + vt->update_y1 = (y + 1) * FONT_HEIGHT;
99 + }
100 +}
101 +
102 +static void vt100_show_cursor(QemuVT100 *vt, int show)
103 +{
104 + TextCell *c;
105 + int y, y1;
106 + int x = vt->x;
107 +
108 + vt->cursor_invalidate = 1;
109 +
110 + if (x >= vt->width) {
111 + x = vt->width - 1;
112 + }
113 + y1 = (vt->y_base + vt->y) % vt->total_height;
114 + y = y1 - vt->y_displayed;
115 + if (y < 0) {
116 + y += vt->total_height;
117 + }
118 + if (y < vt->height) {
119 + c = &vt->cells[y1 * vt->width + x];
120 + if (show && cursor_visible_phase) {
121 + TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
122 + t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
123 + vt100_putcharxy(vt, x, y, c->ch, &t_attrib);
124 + } else {
125 + vt100_putcharxy(vt, x, y, c->ch, &(c->t_attrib));
126 + }
127 + vt100_invalidate_xy(vt, x, y);
128 + }
129 +}
130 +
131 +static void vt100_image_update(QemuVT100 *vt, int x, int y, int width, int height)
132 +{
133 + vt->image_update(vt, x, y, width, height);
134 +}
135 +
136 +void vt100_refresh(QemuVT100 *vt)
137 +{
138 + TextCell *c;
139 + int x, y, y1;
140 + int w = pixman_image_get_width(vt->image);
141 + int h = pixman_image_get_height(vt->image);
142 +
143 + vt->text_x[0] = 0;
144 + vt->text_y[0] = 0;
145 + vt->text_x[1] = vt->width - 1;
146 + vt->text_y[1] = vt->height - 1;
147 + vt->cursor_invalidate = 1;
148 +
149 + image_fill_rect(vt->image, 0, 0, w, h,
150 + color_table_rgb[0][QEMU_COLOR_BLACK]);
151 + y1 = vt->y_displayed;
152 + for (y = 0; y < vt->height; y++) {
153 + c = vt->cells + y1 * vt->width;
154 + for (x = 0; x < vt->width; x++) {
155 + vt100_putcharxy(vt, x, y, c->ch,
156 + &(c->t_attrib));
157 + c++;
158 + }
159 + if (++y1 == vt->total_height) {
160 + y1 = 0;
161 + }
162 + }
163 + vt100_show_cursor(vt, 1);
164 + vt100_image_update(vt, 0, 0, w, h);
165 +}
166 +
167 +static void vt100_scroll(QemuVT100 *vt, int ydelta)
168 +{
169 + int i, y1;
170 +
171 + if (ydelta > 0) {
172 + for (i = 0; i < ydelta; i++) {
173 + if (vt->y_displayed == vt->y_base) {
174 + break;
175 + }
176 + if (++vt->y_displayed == vt->total_height) {
177 + vt->y_displayed = 0;
178 + }
179 + }
180 + } else {
181 + ydelta = -ydelta;
182 + i = vt->backscroll_height;
183 + if (i > vt->total_height - vt->height) {
184 + i = vt->total_height - vt->height;
185 + }
186 + y1 = vt->y_base - i;
187 + if (y1 < 0) {
188 + y1 += vt->total_height;
189 + }
190 + for (i = 0; i < ydelta; i++) {
191 + if (vt->y_displayed == y1) {
192 + break;
193 + }
194 + if (--vt->y_displayed < 0) {
195 + vt->y_displayed = vt->total_height - 1;
196 + }
197 + }
198 + }
199 + vt100_refresh(vt);
200 +}
201 +
202 +static void vt100_write(QemuVT100 *vt, const void *buf, size_t len)
203 +{
204 + uint32_t num_free;
205 +
206 + num_free = fifo8_num_free(&vt->out_fifo);
207 + fifo8_push_all(&vt->out_fifo, buf, MIN(num_free, len));
208 + vt->out_flush(vt);
209 +}
210 +
211 +void vt100_set_image(QemuVT100 *vt, pixman_image_t *image)
212 +{
213 + TextCell *cells, *c, *c1;
214 + int w1, x, y, last_width, w, h;
215 +
216 + vt->image = image;
217 + w = pixman_image_get_width(vt->image) / FONT_WIDTH;
218 + h = pixman_image_get_height(vt->image) / FONT_HEIGHT;
219 + if (w == vt->width && h == vt->height) {
220 + return;
221 + }
222 +
223 + last_width = vt->width;
224 + vt->width = w;
225 + vt->height = h;
226 +
227 + w1 = MIN(vt->width, last_width);
228 +
229 + cells = g_new(TextCell, vt->width * vt->total_height + 1);
230 + for (y = 0; y < vt->total_height; y++) {
231 + c = &cells[y * vt->width];
232 + if (w1 > 0) {
233 + c1 = &vt->cells[y * last_width];
234 + for (x = 0; x < w1; x++) {
235 + *c++ = *c1++;
236 + }
237 + }
238 + for (x = w1; x < vt->width; x++) {
239 + c->ch = ' ';
240 + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
241 + c++;
242 + }
243 + }
244 + g_free(vt->cells);
245 + vt->cells = cells;
246 +}
247 +
248 +static void vt100_put_lf(QemuVT100 *vt)
249 +{
250 + TextCell *c;
251 + int x, y1;
252 +
253 + vt->y++;
254 + if (vt->y >= vt->height) {
255 + vt->y = vt->height - 1;
256 +
257 + if (vt->y_displayed == vt->y_base) {
258 + if (++vt->y_displayed == vt->total_height) {
259 + vt->y_displayed = 0;
260 + }
261 + }
262 + if (++vt->y_base == vt->total_height) {
263 + vt->y_base = 0;
264 + }
265 + if (vt->backscroll_height < vt->total_height) {
266 + vt->backscroll_height++;
267 + }
268 + y1 = (vt->y_base + vt->height - 1) % vt->total_height;
269 + c = &vt->cells[y1 * vt->width];
270 + for (x = 0; x < vt->width; x++) {
271 + c->ch = ' ';
272 + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
273 + c++;
274 + }
275 + if (vt->y_displayed == vt->y_base) {
276 + vt->text_x[0] = 0;
277 + vt->text_y[0] = 0;
278 + vt->text_x[1] = vt->width - 1;
279 + vt->text_y[1] = vt->height - 1;
280 +
281 + image_bitblt(vt->image, 0, FONT_HEIGHT, 0, 0,
282 + vt->width * FONT_WIDTH,
283 + (vt->height - 1) * FONT_HEIGHT);
284 + image_fill_rect(vt->image, 0, (vt->height - 1) * FONT_HEIGHT,
285 + vt->width * FONT_WIDTH, FONT_HEIGHT,
286 + color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
287 + vt->update_x0 = 0;
288 + vt->update_y0 = 0;
289 + vt->update_x1 = vt->width * FONT_WIDTH;
290 + vt->update_y1 = vt->height * FONT_HEIGHT;
291 + }
292 + }
293 +}
294 +
295 +/*
296 + * Set console attributes depending on the current escape codes.
297 + * NOTE: I know this code is not very efficient (checking every color for it
298 + * self) but it is more readable and better maintainable.
299 + */
300 +static void vt100_handle_escape(QemuVT100 *vt)
301 +{
302 + int i;
303 +
304 + for (i = 0; i < vt->nb_esc_params; i++) {
305 + switch (vt->esc_params[i]) {
306 + case 0: /* reset all console attributes to default */
307 + vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
308 + break;
309 + case 1:
310 + vt->t_attrib.bold = 1;
311 + break;
312 + case 4:
313 + vt->t_attrib.uline = 1;
314 + break;
315 + case 5:
316 + vt->t_attrib.blink = 1;
317 + break;
318 + case 7:
319 + vt->t_attrib.invers = 1;
320 + break;
321 + case 8:
322 + vt->t_attrib.unvisible = 1;
323 + break;
324 + case 22:
325 + vt->t_attrib.bold = 0;
326 + break;
327 + case 24:
328 + vt->t_attrib.uline = 0;
329 + break;
330 + case 25:
331 + vt->t_attrib.blink = 0;
332 + break;
333 + case 27:
334 + vt->t_attrib.invers = 0;
335 + break;
336 + case 28:
337 + vt->t_attrib.unvisible = 0;
338 + break;
339 + /* set foreground color */
340 + case 30:
341 + vt->t_attrib.fgcol = QEMU_COLOR_BLACK;
342 + break;
343 + case 31:
344 + vt->t_attrib.fgcol = QEMU_COLOR_RED;
345 + break;
346 + case 32:
347 + vt->t_attrib.fgcol = QEMU_COLOR_GREEN;
348 + break;
349 + case 33:
350 + vt->t_attrib.fgcol = QEMU_COLOR_YELLOW;
351 + break;
352 + case 34:
353 + vt->t_attrib.fgcol = QEMU_COLOR_BLUE;
354 + break;
355 + case 35:
356 + vt->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
357 + break;
358 + case 36:
359 + vt->t_attrib.fgcol = QEMU_COLOR_CYAN;
360 + break;
361 + case 37:
362 + vt->t_attrib.fgcol = QEMU_COLOR_WHITE;
363 + break;
364 + /* set background color */
365 + case 40:
366 + vt->t_attrib.bgcol = QEMU_COLOR_BLACK;
367 + break;
368 + case 41:
369 + vt->t_attrib.bgcol = QEMU_COLOR_RED;
370 + break;
371 + case 42:
372 + vt->t_attrib.bgcol = QEMU_COLOR_GREEN;
373 + break;
374 + case 43:
375 + vt->t_attrib.bgcol = QEMU_COLOR_YELLOW;
376 + break;
377 + case 44:
378 + vt->t_attrib.bgcol = QEMU_COLOR_BLUE;
379 + break;
380 + case 45:
381 + vt->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
382 + break;
383 + case 46:
384 + vt->t_attrib.bgcol = QEMU_COLOR_CYAN;
385 + break;
386 + case 47:
387 + vt->t_attrib.bgcol = QEMU_COLOR_WHITE;
388 + break;
389 + }
390 + }
391 +}
392 +
393 +static void vt100_update_xy(QemuVT100 *vt, int x, int y)
394 +{
395 + TextCell *c;
396 + int y1, y2;
397 +
398 + vt->text_x[0] = MIN(vt->text_x[0], x);
399 + vt->text_x[1] = MAX(vt->text_x[1], x);
400 + vt->text_y[0] = MIN(vt->text_y[0], y);
401 + vt->text_y[1] = MAX(vt->text_y[1], y);
402 +
403 + y1 = (vt->y_base + y) % vt->total_height;
404 + y2 = y1 - vt->y_displayed;
405 + if (y2 < 0) {
406 + y2 += vt->total_height;
407 + }
408 + if (y2 < vt->height) {
409 + if (x >= vt->width) {
410 + x = vt->width - 1;
411 + }
412 + c = &vt->cells[y1 * vt->width + x];
413 + vt100_putcharxy(vt, x, y2, c->ch,
414 + &(c->t_attrib));
415 + vt100_invalidate_xy(vt, x, y2);
416 + }
417 +}
418 +
419 +static void vt100_clear_xy(QemuVT100 *vt, int x, int y)
420 +{
421 + int y1 = (vt->y_base + y) % vt->total_height;
422 + if (x >= vt->width) {
423 + x = vt->width - 1;
424 + }
425 + TextCell *c = &vt->cells[y1 * vt->width + x];
426 + c->ch = ' ';
427 + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
428 + vt100_update_xy(vt, x, y);
429 +}
430 +
431 +/*
432 + * UTF-8 DFA decoder by Bjoern Hoehrmann.
433 + * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
434 + * See https://github.com/polijan/utf8_decode for details.
435 + *
436 + * SPDX-License-Identifier: MIT
437 + */
438 +#define BH_UTF8_ACCEPT 0
439 +#define BH_UTF8_REJECT 12
440 +
441 +static uint32_t bh_utf8_decode(uint32_t *state, uint32_t *codep, uint32_t byte)
442 +{
443 + static const uint8_t utf8d[] = {
444 + /* character class lookup */
445 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
446 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
447 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
449 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
450 + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
451 + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
452 + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
453 +
454 + /* state transition lookup */
455 + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
456 + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
457 + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
458 + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
459 + 12,36,12,12,12,12,12,12,12,12,12,12,
460 + };
461 + uint32_t type = utf8d[byte];
462 +
463 + *codep = (*state != BH_UTF8_ACCEPT) ?
464 + (byte & 0x3fu) | (*codep << 6) :
465 + (0xffu >> type) & (byte);
466 +
467 + *state = utf8d[256 + *state + type];
468 + return *state;
469 +}
470 +
471 +static void vt100_put_one(QemuVT100 *vt, int ch)
472 +{
473 + TextCell *c;
474 + int y1;
475 + if (vt->x >= vt->width) {
476 + /* line wrap */
477 + vt->x = 0;
478 + vt100_put_lf(vt);
479 + }
480 + y1 = (vt->y_base + vt->y) % vt->total_height;
481 + c = &vt->cells[y1 * vt->width + vt->x];
482 + c->ch = ch;
483 + c->t_attrib = vt->t_attrib;
484 + vt100_update_xy(vt, vt->x, vt->y);
485 + vt->x++;
486 +}
487 +
488 +/* set cursor, checking bounds */
489 +static void vt100_set_cursor(QemuVT100 *vt, int x, int y)
490 +{
491 + if (x < 0) {
492 + x = 0;
493 + }
494 + if (y < 0) {
495 + y = 0;
496 + }
497 + if (y >= vt->height) {
498 + y = vt->height - 1;
499 + }
500 + if (x >= vt->width) {
501 + x = vt->width - 1;
502 + }
503 +
504 + vt->x = x;
505 + vt->y = y;
506 +}
507 +
508 +/**
509 + * vt100_csi_P() - (DCH) deletes one or more characters from the cursor
510 + * position to the right. As characters are deleted, the remaining
511 + * characters between the cursor and right margin move to the
512 + * left. Character attributes move with the characters.
513 + */
514 +static void vt100_csi_P(QemuVT100 *vt, unsigned int nr)
515 +{
516 + TextCell *c1, *c2;
517 + unsigned int x1, x2, y;
518 + unsigned int end, len;
519 +
520 + if (!nr) {
521 + nr = 1;
522 + }
523 + if (nr > vt->width - vt->x) {
524 + nr = vt->width - vt->x;
525 + if (!nr) {
526 + return;
527 + }
528 + }
529 +
530 + x1 = vt->x;
531 + x2 = vt->x + nr;
532 + len = vt->width - x2;
533 + if (len) {
534 + y = (vt->y_base + vt->y) % vt->total_height;
535 + c1 = &vt->cells[y * vt->width + x1];
536 + c2 = &vt->cells[y * vt->width + x2];
537 + memmove(c1, c2, len * sizeof(*c1));
538 + for (end = x1 + len; x1 < end; x1++) {
539 + vt100_update_xy(vt, x1, vt->y);
540 + }
541 + }
542 + /* Clear the rest */
543 + for (; x1 < vt->width; x1++) {
544 + vt100_clear_xy(vt, x1, vt->y);
545 + }
546 +}
547 +
548 +/**
549 + * vt100_csi_at() - (ICH) inserts `nr` blank characters with the default
550 + * character attribute. The cursor remains at the beginning of the
551 + * blank characters. Text between the cursor and right margin moves to
552 + * the right. Characters scrolled past the right margin are lost.
553 + */
554 +static void vt100_csi_at(QemuVT100 *vt, unsigned int nr)
555 +{
556 + TextCell *c1, *c2;
557 + unsigned int x1, x2, y;
558 + unsigned int end, len;
559 +
560 + if (!nr) {
561 + nr = 1;
562 + }
563 + if (nr > vt->width - vt->x) {
564 + nr = vt->width - vt->x;
565 + if (!nr) {
566 + return;
567 + }
568 + }
569 +
570 + x1 = vt->x + nr;
571 + x2 = vt->x;
572 + len = vt->width - x1;
573 + if (len) {
574 + y = (vt->y_base + vt->y) % vt->total_height;
575 + c1 = &vt->cells[y * vt->width + x1];
576 + c2 = &vt->cells[y * vt->width + x2];
577 + memmove(c1, c2, len * sizeof(*c1));
578 + for (end = x1 + len; x1 < end; x1++) {
579 + vt100_update_xy(vt, x1, vt->y);
580 + }
581 + }
582 + /* Insert blanks */
583 + for (x1 = vt->x; x1 < vt->x + nr; x1++) {
584 + vt100_clear_xy(vt, x1, vt->y);
585 + }
586 +}
587 +
588 +/**
589 + * vt100_save_cursor() - saves cursor position and character attributes.
590 + */
591 +static void vt100_save_cursor(QemuVT100 *vt)
592 +{
593 + vt->x_saved = vt->x;
594 + vt->y_saved = vt->y;
595 + vt->t_attrib_saved = vt->t_attrib;
596 +}
597 +
598 +/**
599 + * vt100_restore_cursor() - restores cursor position and character
600 + * attributes from saved state.
601 + */
602 +static void vt100_restore_cursor(QemuVT100 *vt)
603 +{
604 + vt->x = vt->x_saved;
605 + vt->y = vt->y_saved;
606 + vt->t_attrib = vt->t_attrib_saved;
607 +}
608 +
609 +static void vt100_putchar(QemuVT100 *vt, int ch)
610 +{
611 + int i;
612 + int x, y;
613 + g_autofree char *response = NULL;
614 +
615 + switch (vt->state) {
616 + case TTY_STATE_NORM:
617 + if (ch >= 0x80 && vt->encoding == CHARDEV_VC_ENCODING_UTF8) {
618 + switch (bh_utf8_decode(&vt->utf8_state, &vt->utf8_codepoint, ch)) {
619 + case BH_UTF8_ACCEPT:
620 + vt100_put_one(vt, unicode_to_cp437(vt->utf8_codepoint));
621 + break;
622 + case BH_UTF8_REJECT:
623 + vt->utf8_state = BH_UTF8_ACCEPT;
624 + break;
625 + default:
626 + break;
627 + }
628 + break;
629 + }
630 + vt->utf8_state = BH_UTF8_ACCEPT;
631 + switch (ch) {
632 + case '\r': /* carriage return */
633 + vt->x = 0;
634 + break;
635 + case '\n': /* newline */
636 + vt100_put_lf(vt);
637 + break;
638 + case '\b': /* backspace */
639 + if (vt->x > 0) {
640 + vt->x--;
641 + }
642 + break;
643 + case '\t': /* tabspace */
644 + if (vt->x + (8 - (vt->x % 8)) > vt->width) {
645 + vt->x = 0;
646 + vt100_put_lf(vt);
647 + } else {
648 + vt->x = vt->x + (8 - (vt->x % 8));
649 + }
650 + break;
651 + case '\a': /* alert aka. bell */
652 + /* TODO: has to be implemented */
653 + break;
654 + case 14:
655 + /* SO (shift out), character set 1 (ignored) */
656 + break;
657 + case 15:
658 + /* SI (shift in), character set 0 (ignored) */
659 + break;
660 + case 27: /* esc (introducing an escape sequence) */
661 + vt->state = TTY_STATE_ESC;
662 + break;
663 + default:
664 + vt100_put_one(vt, ch);
665 + break;
666 + }
667 + break;
668 + case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
669 + if (ch == '[') {
670 + for (i = 0; i < MAX_ESC_PARAMS; i++) {
671 + vt->esc_params[i] = 0;
672 + }
673 + vt->nb_esc_params = 0;
674 + vt->state = TTY_STATE_CSI;
675 + } else if (ch == '(') {
676 + vt->state = TTY_STATE_G0;
677 + } else if (ch == ')') {
678 + vt->state = TTY_STATE_G1;
679 + } else if (ch == ']' || ch == 'P' || ch == 'X'
680 + || ch == '^' || ch == '_') {
681 + /* String sequences: OSC, DCS, SOS, PM, APC */
682 + vt->state = TTY_STATE_OSC;
683 + } else if (ch == '7') {
684 + vt100_save_cursor(vt);
685 + vt->state = TTY_STATE_NORM;
686 + } else if (ch == '8') {
687 + vt100_restore_cursor(vt);
688 + vt->state = TTY_STATE_NORM;
689 + } else {
690 + vt->state = TTY_STATE_NORM;
691 + }
692 + break;
693 + case TTY_STATE_CSI: /* handle escape sequence parameters */
694 + if (ch >= '0' && ch <= '9') {
695 + if (vt->nb_esc_params < MAX_ESC_PARAMS) {
696 + int *param = &vt->esc_params[vt->nb_esc_params];
697 + int digit = (ch - '0');
698 +
699 + *param = (*param <= (INT_MAX - digit) / 10) ?
700 + *param * 10 + digit : INT_MAX;
701 + }
702 + } else {
703 + if (vt->nb_esc_params < MAX_ESC_PARAMS) {
704 + vt->nb_esc_params++;
705 + }
706 + if (ch == ';' || ch == '?') {
707 + break;
708 + }
709 + trace_console_putchar_csi(vt->esc_params[0], vt->esc_params[1],
710 + ch, vt->nb_esc_params);
711 + vt->state = TTY_STATE_NORM;
712 + switch (ch) {
713 + case 'A':
714 + /* move cursor up */
715 + if (vt->esc_params[0] == 0) {
716 + vt->esc_params[0] = 1;
717 + }
718 + vt100_set_cursor(vt, vt->x, vt->y - vt->esc_params[0]);
719 + break;
720 + case 'B':
721 + /* move cursor down */
722 + if (vt->esc_params[0] == 0) {
723 + vt->esc_params[0] = 1;
724 + }
725 + vt100_set_cursor(vt, vt->x, vt->y + vt->esc_params[0]);
726 + break;
727 + case 'C':
728 + /* move cursor right */
729 + if (vt->esc_params[0] == 0) {
730 + vt->esc_params[0] = 1;
731 + }
732 + vt100_set_cursor(vt, vt->x + vt->esc_params[0], vt->y);
733 + break;
734 + case 'D':
735 + /* move cursor left */
736 + if (vt->esc_params[0] == 0) {
737 + vt->esc_params[0] = 1;
738 + }
739 + vt100_set_cursor(vt, vt->x - vt->esc_params[0], vt->y);
740 + break;
741 + case 'G':
742 + /* move cursor to column */
743 + vt100_set_cursor(vt, vt->esc_params[0] - 1, vt->y);
744 + break;
745 + case 'f':
746 + case 'H':
747 + /* move cursor to row, column */
748 + vt100_set_cursor(vt, vt->esc_params[1] - 1, vt->esc_params[0] - 1);
749 + break;
750 + case 'J':
751 + switch (vt->esc_params[0]) {
752 + case 0:
753 + /* clear to end of screen */
754 + for (y = vt->y; y < vt->height; y++) {
755 + for (x = 0; x < vt->width; x++) {
756 + if (y == vt->y && x < vt->x) {
757 + continue;
758 + }
759 + vt100_clear_xy(vt, x, y);
760 + }
761 + }
762 + break;
763 + case 1:
764 + /* clear from beginning of screen */
765 + for (y = 0; y <= vt->y; y++) {
766 + for (x = 0; x < vt->width; x++) {
767 + if (y == vt->y && x > vt->x) {
768 + break;
769 + }
770 + vt100_clear_xy(vt, x, y);
771 + }
772 + }
773 + break;
774 + case 2:
775 + /* clear entire screen */
776 + for (y = 0; y < vt->height; y++) {
777 + for (x = 0; x < vt->width; x++) {
778 + vt100_clear_xy(vt, x, y);
779 + }
780 + }
781 + break;
782 + }
783 + break;
784 + case 'K':
785 + switch (vt->esc_params[0]) {
786 + case 0:
787 + /* clear to eol */
788 + for (x = vt->x; x < vt->width; x++) {
789 + vt100_clear_xy(vt, x, vt->y);
790 + }
791 + break;
792 + case 1:
793 + /* clear from beginning of line */
794 + for (x = 0; x <= vt->x && x < vt->width; x++) {
795 + vt100_clear_xy(vt, x, vt->y);
796 + }
797 + break;
798 + case 2:
799 + /* clear entire line */
800 + for (x = 0; x < vt->width; x++) {
801 + vt100_clear_xy(vt, x, vt->y);
802 + }
803 + break;
804 + }
805 + break;
806 + case 'P':
807 + vt100_csi_P(vt, vt->esc_params[0]);
808 + break;
809 + case 'm':
810 + vt100_handle_escape(vt);
811 + break;
812 + case 'n':
813 + switch (vt->esc_params[0]) {
814 + case 5:
815 + /* report console status (always succeed)*/
816 + vt100_write(vt, "\033[0n", 4);
817 + break;
818 + case 6:
819 + /* report cursor position */
820 + response = g_strdup_printf("\033[%d;%dR",
821 + vt->y + 1, vt->x + 1);
822 + vt100_write(vt, response, strlen(response));
823 + break;
824 + }
825 + break;
826 + case 's':
827 + vt100_save_cursor(vt);
828 + break;
829 + case 'u':
830 + vt100_restore_cursor(vt);
831 + break;
832 + case '@':
833 + vt100_csi_at(vt, vt->esc_params[0]);
834 + break;
835 + default:
836 + trace_console_putchar_unhandled(ch);
837 + break;
838 + }
839 + break;
840 + }
841 + break;
842 + case TTY_STATE_OSC: /* Operating System Command: ESC ] ... BEL/ST */
843 + if (ch == '\a') {
844 + /* BEL terminates OSC */
845 + vt->state = TTY_STATE_NORM;
846 + } else if (ch == 27) {
847 + /* ESC might start ST (ESC \) */
848 + vt->state = TTY_STATE_ESC;
849 + }
850 + /* All other bytes are silently consumed */
851 + break;
852 + case TTY_STATE_G0: /* set character sets */
853 + case TTY_STATE_G1: /* set character sets */
854 + switch (ch) {
855 + case 'B':
856 + /* Latin-1 map */
857 + break;
858 + }
859 + vt->state = TTY_STATE_NORM;
860 + break;
861 + }
862 +
863 +}
864 +
865 +size_t vt100_input(QemuVT100 *vt, const uint8_t *buf, size_t len)
866 +{
867 + int i;
868 +
869 + vt->update_x0 = vt->width * FONT_WIDTH;
870 + vt->update_y0 = vt->height * FONT_HEIGHT;
871 + vt->update_x1 = 0;
872 + vt->update_y1 = 0;
873 + vt100_show_cursor(vt, 0);
874 + for (i = 0; i < len; i++) {
875 + vt100_putchar(vt, buf[i]);
876 + }
877 + vt100_show_cursor(vt, 1);
878 + if (vt->update_x0 < vt->update_x1) {
879 + vt100_image_update(vt, vt->update_x0, vt->update_y0,
880 + vt->update_x1 - vt->update_x0,
881 + vt->update_y1 - vt->update_y0);
882 + }
883 + return len;
884 +}
885 +
886 +void vt100_keysym(QemuVT100 *vt, int keysym)
887 +{
888 + uint8_t buf[16], *q;
889 + int c;
890 +
891 + switch (keysym) {
892 + case QEMU_KEY_CTRL_UP:
893 + vt100_scroll(vt, -1);
894 + break;
895 + case QEMU_KEY_CTRL_DOWN:
896 + vt100_scroll(vt, 1);
897 + break;
898 + case QEMU_KEY_CTRL_PAGEUP:
899 + vt100_scroll(vt, -10);
900 + break;
901 + case QEMU_KEY_CTRL_PAGEDOWN:
902 + vt100_scroll(vt, 10);
903 + break;
904 + default:
905 + /* convert the QEMU keysym to VT100 key string */
906 + q = buf;
907 + if (keysym >= 0xe100 && keysym <= 0xe11f) {
908 + *q++ = '\033';
909 + *q++ = '[';
910 + c = keysym - 0xe100;
911 + if (c >= 10) {
912 + *q++ = '0' + (c / 10);
913 + }
914 + *q++ = '0' + (c % 10);
915 + *q++ = '~';
916 + } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
917 + *q++ = '\033';
918 + *q++ = '[';
919 + *q++ = keysym & 0xff;
920 + } else if (vt->echo && (keysym == '\r' || keysym == '\n')) {
921 + vt100_input(vt, (uint8_t *)"\r", 1);
922 + *q++ = '\n';
923 + } else {
924 + *q++ = keysym;
925 + }
926 + if (vt->echo) {
927 + vt100_input(vt, buf, q - buf);
928 + }
929 + vt100_write(vt, buf, q - buf);
930 + break;
931 + }
932 +}
933 +
934 +void vt100_update_cursor(void)
935 +{
936 + QemuVT100 *vt;
937 +
938 + cursor_visible_phase = !cursor_visible_phase;
939 +
940 + if (QTAILQ_EMPTY(&vt100s)) {
941 + return;
942 + }
943 +
944 + QTAILQ_FOREACH(vt, &vt100s, list) {
945 + vt100_refresh(vt);
946 + }
947 +
948 + timer_mod(cursor_timer,
949 + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
950 +}
951 +
952 +static void
953 +cursor_timer_cb(void *opaque)
954 +{
955 + vt100_update_cursor();
956 +}
957 +
958 +void vt100_init(QemuVT100 *vt,
959 + pixman_image_t *image,
960 + ChardevVCEncoding encoding,
961 + void (*image_update)(QemuVT100 *vt, int x, int y, int w, int h),
962 + void (*out_flush)(QemuVT100 *vt))
963 +{
964 + if (!cursor_timer) {
965 + cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL);
966 + }
967 +
968 + vt->encoding = encoding;
969 + QTAILQ_INSERT_HEAD(&vt100s, vt, list);
970 + fifo8_create(&vt->out_fifo, 16);
971 + vt->total_height = DEFAULT_BACKSCROLL;
972 + vt->image_update = image_update;
973 + vt->out_flush = out_flush;
974 + /* set current text attributes to default */
975 + vt->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
976 + vt100_set_image(vt, image);
977 +}
978 +
979 +void vt100_fini(QemuVT100 *vt)
980 +{
981 + QTAILQ_REMOVE(&vt100s, vt, list);
982 + fifo8_destroy(&vt->out_fifo);
983 + g_free(vt->cells);
984 +}
ui/vt100.h new
+95
@@ -0,0 +1,95 @@
1 +/*
2 + * SPDX-License-Identifier: MIT
3 + * QEMU vt100
4 + */
5 +#ifndef VT100_H
6 +#define VT100_H
7 +
8 +#include "chardev/char.h"
9 +#include "ui/console.h"
10 +#include "qemu/fifo8.h"
11 +#include "qemu/queue.h"
12 +
13 +typedef struct TextAttributes {
14 + uint8_t fgcol:4;
15 + uint8_t bgcol:4;
16 + uint8_t bold:1;
17 + uint8_t uline:1;
18 + uint8_t blink:1;
19 + uint8_t invers:1;
20 + uint8_t unvisible:1;
21 +} TextAttributes;
22 +
23 +#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
24 + .fgcol = QEMU_COLOR_WHITE, \
25 + .bgcol = QEMU_COLOR_BLACK \
26 +})
27 +
28 +typedef struct TextCell {
29 + uint8_t ch;
30 + TextAttributes t_attrib;
31 +} TextCell;
32 +
33 +#define MAX_ESC_PARAMS 3
34 +
35 +enum TTYState {
36 + TTY_STATE_NORM,
37 + TTY_STATE_ESC,
38 + TTY_STATE_CSI,
39 + TTY_STATE_G0,
40 + TTY_STATE_G1,
41 + TTY_STATE_OSC,
42 +};
43 +
44 +typedef struct QemuVT100 QemuVT100;
45 +
46 +struct QemuVT100 {
47 + pixman_image_t *image;
48 + void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height);
49 +
50 + ChardevVCEncoding encoding;
51 + int width;
52 + int height;
53 + int total_height;
54 + int backscroll_height;
55 + int x, y;
56 + int y_displayed;
57 + int y_base;
58 + TextCell *cells;
59 + int text_x[2], text_y[2], cursor_invalidate;
60 + int echo;
61 +
62 + int update_x0;
63 + int update_y0;
64 + int update_x1;
65 + int update_y1;
66 +
67 + enum TTYState state;
68 + int esc_params[MAX_ESC_PARAMS];
69 + int nb_esc_params;
70 + uint32_t utf8_state; /* UTF-8 DFA decoder state */
71 + uint32_t utf8_codepoint; /* accumulated UTF-8 code point */
72 + TextAttributes t_attrib; /* currently active text attributes */
73 + TextAttributes t_attrib_saved;
74 + int x_saved, y_saved;
75 + /* fifo for key pressed */
76 + Fifo8 out_fifo;
77 + void (*out_flush)(QemuVT100 *vt);
78 +
79 + QTAILQ_ENTRY(QemuVT100) list;
80 +};
81 +
82 +void vt100_init(QemuVT100 *vt,
83 + pixman_image_t *image,
84 + ChardevVCEncoding encoding,
85 + void (*image_update)(QemuVT100 *vt, int x, int y, int width, int height),
86 + void (*out_flush)(QemuVT100 *vt));
87 +void vt100_fini(QemuVT100 *vt);
88 +
89 +void vt100_update_cursor(void);
90 +size_t vt100_input(QemuVT100 *vt, const uint8_t *buf, size_t len);
91 +void vt100_keysym(QemuVT100 *vt, int keysym);
92 +void vt100_set_image(QemuVT100 *vt, pixman_image_t *image);
93 +void vt100_refresh(QemuVT100 *vt);
94 +
95 +#endif