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