master
c 987 lines 28.8 KB
Raw
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, uint8_t 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, uint8_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, uint8_t 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, uint8_t 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 if (!QTAILQ_IN_USE(vt, list)) {
982 return;
983 }
984 QTAILQ_REMOVE(&vt100s, vt, list);
985 fifo8_destroy(&vt->out_fifo);
986 g_free(vt->cells);
987 }