1 /*
2 * QEMU graphical console
3 *
4 * Copyright (c) 2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include "qemu-common.h"
25 #include "console.h"
26 #include "qemu-timer.h"
27
28 //#define DEBUG_CONSOLE
29 #define DEFAULT_BACKSCROLL 512
30 #define MAX_CONSOLES 12
31 #define DEFAULT_MONITOR_SIZE "800x600"
32
33 #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
34 #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
35
36 typedef struct TextAttributes {
37 uint8_t fgcol:4;
38 uint8_t bgcol:4;
39 uint8_t bold:1;
40 uint8_t uline:1;
41 uint8_t blink:1;
42 uint8_t invers:1;
43 uint8_t unvisible:1;
44 } TextAttributes;
45
46 typedef struct TextCell {
47 uint8_t ch;
48 TextAttributes t_attrib;
49 } TextCell;
50
51 #define MAX_ESC_PARAMS 3
52
53 enum TTYState {
54 TTY_STATE_NORM,
55 TTY_STATE_ESC,
56 TTY_STATE_CSI,
57 };
58
59 typedef struct QEMUFIFO {
60 uint8_t *buf;
61 int buf_size;
62 int count, wptr, rptr;
63 } QEMUFIFO;
64
qemu_fifo_write(QEMUFIFO * f,const uint8_t * buf,int len1)65 static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
66 {
67 int l, len;
68
69 l = f->buf_size - f->count;
70 if (len1 > l)
71 len1 = l;
72 len = len1;
73 while (len > 0) {
74 l = f->buf_size - f->wptr;
75 if (l > len)
76 l = len;
77 memcpy(f->buf + f->wptr, buf, l);
78 f->wptr += l;
79 if (f->wptr >= f->buf_size)
80 f->wptr = 0;
81 buf += l;
82 len -= l;
83 }
84 f->count += len1;
85 return len1;
86 }
87
qemu_fifo_read(QEMUFIFO * f,uint8_t * buf,int len1)88 static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
89 {
90 int l, len;
91
92 if (len1 > f->count)
93 len1 = f->count;
94 len = len1;
95 while (len > 0) {
96 l = f->buf_size - f->rptr;
97 if (l > len)
98 l = len;
99 memcpy(buf, f->buf + f->rptr, l);
100 f->rptr += l;
101 if (f->rptr >= f->buf_size)
102 f->rptr = 0;
103 buf += l;
104 len -= l;
105 }
106 f->count -= len1;
107 return len1;
108 }
109
110 typedef enum {
111 GRAPHIC_CONSOLE,
112 TEXT_CONSOLE
113 } console_type_t;
114
115 /* ??? This is mis-named.
116 It is used for both text and graphical consoles. */
117 struct TextConsole {
118 console_type_t console_type;
119 DisplayState *ds;
120 /* Graphic console state. */
121 vga_hw_update_ptr hw_update;
122 vga_hw_invalidate_ptr hw_invalidate;
123 vga_hw_screen_dump_ptr hw_screen_dump;
124 vga_hw_text_update_ptr hw_text_update;
125 void *hw;
126
127 int g_width, g_height;
128 int width;
129 int height;
130 int total_height;
131 int backscroll_height;
132 int x, y;
133 int x_saved, y_saved;
134 int y_displayed;
135 int y_base;
136 TextAttributes t_attrib_default; /* default text attributes */
137 TextAttributes t_attrib; /* currently active text attributes */
138 TextCell *cells;
139 int text_x[2], text_y[2], cursor_invalidate;
140
141 enum TTYState state;
142 int esc_params[MAX_ESC_PARAMS];
143 int nb_esc_params;
144
145 CharDriverState *chr;
146 /* fifo for key pressed */
147 QEMUFIFO out_fifo;
148 uint8_t out_fifo_buf[16];
149 QEMUTimer *kbd_timer;
150 };
151
152 static TextConsole *active_console;
153 static TextConsole *consoles[MAX_CONSOLES];
154 static int nb_consoles = 0;
155
vga_hw_update(void)156 void vga_hw_update(void)
157 {
158 if (active_console && active_console->hw_update)
159 active_console->hw_update(active_console->hw);
160 }
161
vga_hw_invalidate(void)162 void vga_hw_invalidate(void)
163 {
164 if (active_console->hw_invalidate)
165 active_console->hw_invalidate(active_console->hw);
166 }
167
vga_hw_screen_dump(const char * filename)168 void vga_hw_screen_dump(const char *filename)
169 {
170 TextConsole *previous_active_console;
171
172 previous_active_console = active_console;
173 active_console = consoles[0];
174 /* There is currently no way of specifying which screen we want to dump,
175 so always dump the first one. */
176 if (consoles[0]->hw_screen_dump)
177 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
178 active_console = previous_active_console;
179 }
180
vga_hw_text_update(console_ch_t * chardata)181 void vga_hw_text_update(console_ch_t *chardata)
182 {
183 if (active_console && active_console->hw_text_update)
184 active_console->hw_text_update(active_console->hw, chardata);
185 }
186
187 /* convert a RGBA color to a color index usable in graphic primitives */
vga_get_color(DisplayState * ds,unsigned int rgba)188 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
189 {
190 unsigned int r, g, b, color;
191
192 switch(ds->depth) {
193 #if 0
194 case 8:
195 r = (rgba >> 16) & 0xff;
196 g = (rgba >> 8) & 0xff;
197 b = (rgba) & 0xff;
198 color = (rgb_to_index[r] * 6 * 6) +
199 (rgb_to_index[g] * 6) +
200 (rgb_to_index[b]);
201 break;
202 #endif
203 case 15:
204 r = (rgba >> 16) & 0xff;
205 g = (rgba >> 8) & 0xff;
206 b = (rgba) & 0xff;
207 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
208 break;
209 case 16:
210 r = (rgba >> 16) & 0xff;
211 g = (rgba >> 8) & 0xff;
212 b = (rgba) & 0xff;
213 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
214 break;
215 case 32:
216 default:
217 color = rgba;
218 break;
219 }
220 return color;
221 }
222
vga_fill_rect(DisplayState * ds,int posx,int posy,int width,int height,uint32_t color)223 static void vga_fill_rect (DisplayState *ds,
224 int posx, int posy, int width, int height, uint32_t color)
225 {
226 uint8_t *d, *d1;
227 int x, y, bpp;
228
229 bpp = (ds->depth + 7) >> 3;
230 d1 = ds->data +
231 ds->linesize * posy + bpp * posx;
232 for (y = 0; y < height; y++) {
233 d = d1;
234 switch(bpp) {
235 case 1:
236 for (x = 0; x < width; x++) {
237 *((uint8_t *)d) = color;
238 d++;
239 }
240 break;
241 case 2:
242 for (x = 0; x < width; x++) {
243 *((uint16_t *)d) = color;
244 d += 2;
245 }
246 break;
247 case 4:
248 for (x = 0; x < width; x++) {
249 *((uint32_t *)d) = color;
250 d += 4;
251 }
252 break;
253 }
254 d1 += ds->linesize;
255 }
256 }
257
258 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
vga_bitblt(DisplayState * ds,int xs,int ys,int xd,int yd,int w,int h)259 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
260 {
261 const uint8_t *s;
262 uint8_t *d;
263 int wb, y, bpp;
264
265 bpp = (ds->depth + 7) >> 3;
266 wb = w * bpp;
267 if (yd <= ys) {
268 s = ds->data +
269 ds->linesize * ys + bpp * xs;
270 d = ds->data +
271 ds->linesize * yd + bpp * xd;
272 for (y = 0; y < h; y++) {
273 memmove(d, s, wb);
274 d += ds->linesize;
275 s += ds->linesize;
276 }
277 } else {
278 s = ds->data +
279 ds->linesize * (ys + h - 1) + bpp * xs;
280 d = ds->data +
281 ds->linesize * (yd + h - 1) + bpp * xd;
282 for (y = 0; y < h; y++) {
283 memmove(d, s, wb);
284 d -= ds->linesize;
285 s -= ds->linesize;
286 }
287 }
288 }
289
290 /***********************************************************/
291 /* basic char display */
292
293 #define FONT_HEIGHT 16
294 #define FONT_WIDTH 8
295
296 #include "vgafont.h"
297
298 #define cbswap_32(__x) \
299 ((uint32_t)( \
300 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
301 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
302 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
303 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
304
305 #ifdef WORDS_BIGENDIAN
306 #define PAT(x) x
307 #else
308 #define PAT(x) cbswap_32(x)
309 #endif
310
311 static const uint32_t dmask16[16] = {
312 PAT(0x00000000),
313 PAT(0x000000ff),
314 PAT(0x0000ff00),
315 PAT(0x0000ffff),
316 PAT(0x00ff0000),
317 PAT(0x00ff00ff),
318 PAT(0x00ffff00),
319 PAT(0x00ffffff),
320 PAT(0xff000000),
321 PAT(0xff0000ff),
322 PAT(0xff00ff00),
323 PAT(0xff00ffff),
324 PAT(0xffff0000),
325 PAT(0xffff00ff),
326 PAT(0xffffff00),
327 PAT(0xffffffff),
328 };
329
330 static const uint32_t dmask4[4] = {
331 PAT(0x00000000),
332 PAT(0x0000ffff),
333 PAT(0xffff0000),
334 PAT(0xffffffff),
335 };
336
337 static uint32_t color_table[2][8];
338
339 enum color_names {
340 COLOR_BLACK = 0,
341 COLOR_RED = 1,
342 COLOR_GREEN = 2,
343 COLOR_YELLOW = 3,
344 COLOR_BLUE = 4,
345 COLOR_MAGENTA = 5,
346 COLOR_CYAN = 6,
347 COLOR_WHITE = 7
348 };
349
350 static const uint32_t color_table_rgb[2][8] = {
351 { /* dark */
352 QEMU_RGB(0x00, 0x00, 0x00), /* black */
353 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
354 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
355 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
356 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
357 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
358 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
359 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
360 },
361 { /* bright */
362 QEMU_RGB(0x00, 0x00, 0x00), /* black */
363 QEMU_RGB(0xff, 0x00, 0x00), /* red */
364 QEMU_RGB(0x00, 0xff, 0x00), /* green */
365 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
366 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
367 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
368 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
369 QEMU_RGB(0xff, 0xff, 0xff), /* white */
370 }
371 };
372
col_expand(DisplayState * ds,unsigned int col)373 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
374 {
375 switch(ds->depth) {
376 case 8:
377 col |= col << 8;
378 col |= col << 16;
379 break;
380 case 15:
381 case 16:
382 col |= col << 16;
383 break;
384 default:
385 break;
386 }
387
388 return col;
389 }
390 #ifdef DEBUG_CONSOLE
console_print_text_attributes(TextAttributes * t_attrib,char ch)391 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
392 {
393 if (t_attrib->bold) {
394 printf("b");
395 } else {
396 printf(" ");
397 }
398 if (t_attrib->uline) {
399 printf("u");
400 } else {
401 printf(" ");
402 }
403 if (t_attrib->blink) {
404 printf("l");
405 } else {
406 printf(" ");
407 }
408 if (t_attrib->invers) {
409 printf("i");
410 } else {
411 printf(" ");
412 }
413 if (t_attrib->unvisible) {
414 printf("n");
415 } else {
416 printf(" ");
417 }
418
419 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
420 }
421 #endif
422
vga_putcharxy(DisplayState * ds,int x,int y,int ch,TextAttributes * t_attrib)423 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
424 TextAttributes *t_attrib)
425 {
426 uint8_t *d;
427 const uint8_t *font_ptr;
428 unsigned int font_data, linesize, xorcol, bpp;
429 int i;
430 unsigned int fgcol, bgcol;
431
432 #ifdef DEBUG_CONSOLE
433 printf("x: %2i y: %2i", x, y);
434 console_print_text_attributes(t_attrib, ch);
435 #endif
436
437 if (t_attrib->invers) {
438 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
439 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
440 } else {
441 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
442 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
443 }
444
445 bpp = (ds->depth + 7) >> 3;
446 d = ds->data +
447 ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
448 linesize = ds->linesize;
449 font_ptr = vgafont16 + FONT_HEIGHT * ch;
450 xorcol = bgcol ^ fgcol;
451 switch(ds->depth) {
452 case 8:
453 for(i = 0; i < FONT_HEIGHT; i++) {
454 font_data = *font_ptr++;
455 if (t_attrib->uline
456 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
457 font_data = 0xFFFF;
458 }
459 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
460 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
461 d += linesize;
462 }
463 break;
464 case 16:
465 case 15:
466 for(i = 0; i < FONT_HEIGHT; i++) {
467 font_data = *font_ptr++;
468 if (t_attrib->uline
469 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
470 font_data = 0xFFFF;
471 }
472 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
473 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
474 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
475 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
476 d += linesize;
477 }
478 break;
479 case 32:
480 for(i = 0; i < FONT_HEIGHT; i++) {
481 font_data = *font_ptr++;
482 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
483 font_data = 0xFFFF;
484 }
485 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
486 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
487 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
488 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
489 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
490 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
491 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
492 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
493 d += linesize;
494 }
495 break;
496 }
497 }
498
text_console_resize(TextConsole * s)499 static void text_console_resize(TextConsole *s)
500 {
501 TextCell *cells, *c, *c1;
502 int w1, x, y, last_width;
503
504 last_width = s->width;
505 s->width = s->g_width / FONT_WIDTH;
506 s->height = s->g_height / FONT_HEIGHT;
507
508 w1 = last_width;
509 if (s->width < w1)
510 w1 = s->width;
511
512 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
513 for(y = 0; y < s->total_height; y++) {
514 c = &cells[y * s->width];
515 if (w1 > 0) {
516 c1 = &s->cells[y * last_width];
517 for(x = 0; x < w1; x++) {
518 *c++ = *c1++;
519 }
520 }
521 for(x = w1; x < s->width; x++) {
522 c->ch = ' ';
523 c->t_attrib = s->t_attrib_default;
524 c++;
525 }
526 }
527 qemu_free(s->cells);
528 s->cells = cells;
529 }
530
text_update_xy(TextConsole * s,int x,int y)531 static inline void text_update_xy(TextConsole *s, int x, int y)
532 {
533 s->text_x[0] = MIN(s->text_x[0], x);
534 s->text_x[1] = MAX(s->text_x[1], x);
535 s->text_y[0] = MIN(s->text_y[0], y);
536 s->text_y[1] = MAX(s->text_y[1], y);
537 }
538
update_xy(TextConsole * s,int x,int y)539 static void update_xy(TextConsole *s, int x, int y)
540 {
541 TextCell *c;
542 int y1, y2;
543
544 if (s == active_console) {
545 if (!s->ds->depth) {
546 text_update_xy(s, x, y);
547 return;
548 }
549
550 y1 = (s->y_base + y) % s->total_height;
551 y2 = y1 - s->y_displayed;
552 if (y2 < 0)
553 y2 += s->total_height;
554 if (y2 < s->height) {
555 c = &s->cells[y1 * s->width + x];
556 vga_putcharxy(s->ds, x, y2, c->ch,
557 &(c->t_attrib));
558 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
559 FONT_WIDTH, FONT_HEIGHT);
560 }
561 }
562 }
563
console_show_cursor(TextConsole * s,int show)564 static void console_show_cursor(TextConsole *s, int show)
565 {
566 TextCell *c;
567 int y, y1;
568
569 if (s == active_console) {
570 int x = s->x;
571
572 if (!s->ds->depth) {
573 s->cursor_invalidate = 1;
574 return;
575 }
576
577 if (x >= s->width) {
578 x = s->width - 1;
579 }
580 y1 = (s->y_base + s->y) % s->total_height;
581 y = y1 - s->y_displayed;
582 if (y < 0)
583 y += s->total_height;
584 if (y < s->height) {
585 c = &s->cells[y1 * s->width + x];
586 if (show) {
587 TextAttributes t_attrib = s->t_attrib_default;
588 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
589 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
590 } else {
591 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
592 }
593 dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
594 FONT_WIDTH, FONT_HEIGHT);
595 }
596 }
597 }
598
console_refresh(TextConsole * s)599 static void console_refresh(TextConsole *s)
600 {
601 TextCell *c;
602 int x, y, y1;
603
604 if (s != active_console)
605 return;
606 if (!s->ds->depth) {
607 s->text_x[0] = 0;
608 s->text_y[0] = 0;
609 s->text_x[1] = s->width - 1;
610 s->text_y[1] = s->height - 1;
611 s->cursor_invalidate = 1;
612 return;
613 }
614
615 vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
616 color_table[0][COLOR_BLACK]);
617 y1 = s->y_displayed;
618 for(y = 0; y < s->height; y++) {
619 c = s->cells + y1 * s->width;
620 for(x = 0; x < s->width; x++) {
621 vga_putcharxy(s->ds, x, y, c->ch,
622 &(c->t_attrib));
623 c++;
624 }
625 if (++y1 == s->total_height)
626 y1 = 0;
627 }
628 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
629 console_show_cursor(s, 1);
630 }
631
console_scroll(int ydelta)632 static void console_scroll(int ydelta)
633 {
634 TextConsole *s;
635 int i, y1;
636
637 s = active_console;
638 if (!s || (s->console_type == GRAPHIC_CONSOLE))
639 return;
640
641 if (ydelta > 0) {
642 for(i = 0; i < ydelta; i++) {
643 if (s->y_displayed == s->y_base)
644 break;
645 if (++s->y_displayed == s->total_height)
646 s->y_displayed = 0;
647 }
648 } else {
649 ydelta = -ydelta;
650 i = s->backscroll_height;
651 if (i > s->total_height - s->height)
652 i = s->total_height - s->height;
653 y1 = s->y_base - i;
654 if (y1 < 0)
655 y1 += s->total_height;
656 for(i = 0; i < ydelta; i++) {
657 if (s->y_displayed == y1)
658 break;
659 if (--s->y_displayed < 0)
660 s->y_displayed = s->total_height - 1;
661 }
662 }
663 console_refresh(s);
664 }
665
console_put_lf(TextConsole * s)666 static void console_put_lf(TextConsole *s)
667 {
668 TextCell *c;
669 int x, y1;
670
671 s->y++;
672 if (s->y >= s->height) {
673 s->y = s->height - 1;
674
675 if (s->y_displayed == s->y_base) {
676 if (++s->y_displayed == s->total_height)
677 s->y_displayed = 0;
678 }
679 if (++s->y_base == s->total_height)
680 s->y_base = 0;
681 if (s->backscroll_height < s->total_height)
682 s->backscroll_height++;
683 y1 = (s->y_base + s->height - 1) % s->total_height;
684 c = &s->cells[y1 * s->width];
685 for(x = 0; x < s->width; x++) {
686 c->ch = ' ';
687 c->t_attrib = s->t_attrib_default;
688 c++;
689 }
690 if (s == active_console && s->y_displayed == s->y_base) {
691 if (!s->ds->depth) {
692 s->text_x[0] = 0;
693 s->text_y[0] = 0;
694 s->text_x[1] = s->width - 1;
695 s->text_y[1] = s->height - 1;
696 return;
697 }
698
699 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
700 s->width * FONT_WIDTH,
701 (s->height - 1) * FONT_HEIGHT);
702 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
703 s->width * FONT_WIDTH, FONT_HEIGHT,
704 color_table[0][s->t_attrib_default.bgcol]);
705 dpy_update(s->ds, 0, 0,
706 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
707 }
708 }
709 }
710
711 /* Set console attributes depending on the current escape codes.
712 * NOTE: I know this code is not very efficient (checking every color for it
713 * self) but it is more readable and better maintainable.
714 */
console_handle_escape(TextConsole * s)715 static void console_handle_escape(TextConsole *s)
716 {
717 int i;
718
719 for (i=0; i<s->nb_esc_params; i++) {
720 switch (s->esc_params[i]) {
721 case 0: /* reset all console attributes to default */
722 s->t_attrib = s->t_attrib_default;
723 break;
724 case 1:
725 s->t_attrib.bold = 1;
726 break;
727 case 4:
728 s->t_attrib.uline = 1;
729 break;
730 case 5:
731 s->t_attrib.blink = 1;
732 break;
733 case 7:
734 s->t_attrib.invers = 1;
735 break;
736 case 8:
737 s->t_attrib.unvisible = 1;
738 break;
739 case 22:
740 s->t_attrib.bold = 0;
741 break;
742 case 24:
743 s->t_attrib.uline = 0;
744 break;
745 case 25:
746 s->t_attrib.blink = 0;
747 break;
748 case 27:
749 s->t_attrib.invers = 0;
750 break;
751 case 28:
752 s->t_attrib.unvisible = 0;
753 break;
754 /* set foreground color */
755 case 30:
756 s->t_attrib.fgcol=COLOR_BLACK;
757 break;
758 case 31:
759 s->t_attrib.fgcol=COLOR_RED;
760 break;
761 case 32:
762 s->t_attrib.fgcol=COLOR_GREEN;
763 break;
764 case 33:
765 s->t_attrib.fgcol=COLOR_YELLOW;
766 break;
767 case 34:
768 s->t_attrib.fgcol=COLOR_BLUE;
769 break;
770 case 35:
771 s->t_attrib.fgcol=COLOR_MAGENTA;
772 break;
773 case 36:
774 s->t_attrib.fgcol=COLOR_CYAN;
775 break;
776 case 37:
777 s->t_attrib.fgcol=COLOR_WHITE;
778 break;
779 /* set background color */
780 case 40:
781 s->t_attrib.bgcol=COLOR_BLACK;
782 break;
783 case 41:
784 s->t_attrib.bgcol=COLOR_RED;
785 break;
786 case 42:
787 s->t_attrib.bgcol=COLOR_GREEN;
788 break;
789 case 43:
790 s->t_attrib.bgcol=COLOR_YELLOW;
791 break;
792 case 44:
793 s->t_attrib.bgcol=COLOR_BLUE;
794 break;
795 case 45:
796 s->t_attrib.bgcol=COLOR_MAGENTA;
797 break;
798 case 46:
799 s->t_attrib.bgcol=COLOR_CYAN;
800 break;
801 case 47:
802 s->t_attrib.bgcol=COLOR_WHITE;
803 break;
804 }
805 }
806 }
807
console_clear_xy(TextConsole * s,int x,int y)808 static void console_clear_xy(TextConsole *s, int x, int y)
809 {
810 int y1 = (s->y_base + y) % s->total_height;
811 TextCell *c = &s->cells[y1 * s->width + x];
812 c->ch = ' ';
813 c->t_attrib = s->t_attrib_default;
814 c++;
815 update_xy(s, x, y);
816 }
817
console_putchar(TextConsole * s,int ch)818 static void console_putchar(TextConsole *s, int ch)
819 {
820 TextCell *c;
821 int y1, i;
822 int x, y;
823
824 switch(s->state) {
825 case TTY_STATE_NORM:
826 switch(ch) {
827 case '\r': /* carriage return */
828 s->x = 0;
829 break;
830 case '\n': /* newline */
831 console_put_lf(s);
832 break;
833 case '\b': /* backspace */
834 if (s->x > 0)
835 s->x--;
836 break;
837 case '\t': /* tabspace */
838 if (s->x + (8 - (s->x % 8)) > s->width) {
839 s->x = 0;
840 console_put_lf(s);
841 } else {
842 s->x = s->x + (8 - (s->x % 8));
843 }
844 break;
845 case '\a': /* alert aka. bell */
846 /* TODO: has to be implemented */
847 break;
848 case 14:
849 /* SI (shift in), character set 0 (ignored) */
850 break;
851 case 15:
852 /* SO (shift out), character set 1 (ignored) */
853 break;
854 case 27: /* esc (introducing an escape sequence) */
855 s->state = TTY_STATE_ESC;
856 break;
857 default:
858 if (s->x >= s->width) {
859 /* line wrap */
860 s->x = 0;
861 console_put_lf(s);
862 }
863 y1 = (s->y_base + s->y) % s->total_height;
864 c = &s->cells[y1 * s->width + s->x];
865 c->ch = ch;
866 c->t_attrib = s->t_attrib;
867 update_xy(s, s->x, s->y);
868 s->x++;
869 break;
870 }
871 break;
872 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
873 if (ch == '[') {
874 for(i=0;i<MAX_ESC_PARAMS;i++)
875 s->esc_params[i] = 0;
876 s->nb_esc_params = 0;
877 s->state = TTY_STATE_CSI;
878 } else {
879 s->state = TTY_STATE_NORM;
880 }
881 break;
882 case TTY_STATE_CSI: /* handle escape sequence parameters */
883 if (ch >= '0' && ch <= '9') {
884 if (s->nb_esc_params < MAX_ESC_PARAMS) {
885 s->esc_params[s->nb_esc_params] =
886 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
887 }
888 } else {
889 s->nb_esc_params++;
890 if (ch == ';')
891 break;
892 #ifdef DEBUG_CONSOLE
893 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
894 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
895 #endif
896 s->state = TTY_STATE_NORM;
897 switch(ch) {
898 case 'A':
899 /* move cursor up */
900 if (s->esc_params[0] == 0) {
901 s->esc_params[0] = 1;
902 }
903 s->y -= s->esc_params[0];
904 if (s->y < 0) {
905 s->y = 0;
906 }
907 break;
908 case 'B':
909 /* move cursor down */
910 if (s->esc_params[0] == 0) {
911 s->esc_params[0] = 1;
912 }
913 s->y += s->esc_params[0];
914 if (s->y >= s->height) {
915 s->y = s->height - 1;
916 }
917 break;
918 case 'C':
919 /* move cursor right */
920 if (s->esc_params[0] == 0) {
921 s->esc_params[0] = 1;
922 }
923 s->x += s->esc_params[0];
924 if (s->x >= s->width) {
925 s->x = s->width - 1;
926 }
927 break;
928 case 'D':
929 /* move cursor left */
930 if (s->esc_params[0] == 0) {
931 s->esc_params[0] = 1;
932 }
933 s->x -= s->esc_params[0];
934 if (s->x < 0) {
935 s->x = 0;
936 }
937 break;
938 case 'G':
939 /* move cursor to column */
940 s->x = s->esc_params[0] - 1;
941 if (s->x < 0) {
942 s->x = 0;
943 }
944 break;
945 case 'f':
946 case 'H':
947 /* move cursor to row, column */
948 s->x = s->esc_params[1] - 1;
949 if (s->x < 0) {
950 s->x = 0;
951 }
952 s->y = s->esc_params[0] - 1;
953 if (s->y < 0) {
954 s->y = 0;
955 }
956 break;
957 case 'J':
958 switch (s->esc_params[0]) {
959 case 0:
960 /* clear to end of screen */
961 for (y = s->y; y < s->height; y++) {
962 for (x = 0; x < s->width; x++) {
963 if (y == s->y && x < s->x) {
964 continue;
965 }
966 console_clear_xy(s, x, y);
967 }
968 }
969 break;
970 case 1:
971 /* clear from beginning of screen */
972 for (y = 0; y <= s->y; y++) {
973 for (x = 0; x < s->width; x++) {
974 if (y == s->y && x > s->x) {
975 break;
976 }
977 console_clear_xy(s, x, y);
978 }
979 }
980 break;
981 case 2:
982 /* clear entire screen */
983 for (y = 0; y <= s->height; y++) {
984 for (x = 0; x < s->width; x++) {
985 console_clear_xy(s, x, y);
986 }
987 }
988 break;
989 }
990 case 'K':
991 switch (s->esc_params[0]) {
992 case 0:
993 /* clear to eol */
994 for(x = s->x; x < s->width; x++) {
995 console_clear_xy(s, x, s->y);
996 }
997 break;
998 case 1:
999 /* clear from beginning of line */
1000 for (x = 0; x <= s->x; x++) {
1001 console_clear_xy(s, x, s->y);
1002 }
1003 break;
1004 case 2:
1005 /* clear entire line */
1006 for(x = 0; x < s->width; x++) {
1007 console_clear_xy(s, x, s->y);
1008 }
1009 break;
1010 }
1011 break;
1012 case 'm':
1013 console_handle_escape(s);
1014 break;
1015 case 'n':
1016 /* report cursor position */
1017 /* TODO: send ESC[row;colR */
1018 break;
1019 case 's':
1020 /* save cursor position */
1021 s->x_saved = s->x;
1022 s->y_saved = s->y;
1023 break;
1024 case 'u':
1025 /* restore cursor position */
1026 s->x = s->x_saved;
1027 s->y = s->y_saved;
1028 break;
1029 default:
1030 #ifdef DEBUG_CONSOLE
1031 fprintf(stderr, "unhandled escape character '%c'\n", ch);
1032 #endif
1033 break;
1034 }
1035 break;
1036 }
1037 }
1038 }
1039
console_select(unsigned int index)1040 void console_select(unsigned int index)
1041 {
1042 TextConsole *s;
1043
1044 if (index >= MAX_CONSOLES)
1045 return;
1046 s = consoles[index];
1047 if (s) {
1048 active_console = s;
1049 if (s->g_width && s->g_height
1050 && (s->g_width != s->ds->width || s->g_height != s->ds->height))
1051 dpy_resize(s->ds, s->g_width, s->g_height);
1052 vga_hw_invalidate();
1053 }
1054 }
1055
console_puts(CharDriverState * chr,const uint8_t * buf,int len)1056 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1057 {
1058 TextConsole *s = chr->opaque;
1059 int i;
1060
1061 console_show_cursor(s, 0);
1062 for(i = 0; i < len; i++) {
1063 console_putchar(s, buf[i]);
1064 }
1065 console_show_cursor(s, 1);
1066 return len;
1067 }
1068
console_send_event(CharDriverState * chr,int event)1069 static void console_send_event(CharDriverState *chr, int event)
1070 {
1071 TextConsole *s = chr->opaque;
1072 int i;
1073
1074 if (event == CHR_EVENT_FOCUS) {
1075 for(i = 0; i < nb_consoles; i++) {
1076 if (consoles[i] == s) {
1077 console_select(i);
1078 break;
1079 }
1080 }
1081 }
1082 }
1083
kbd_send_chars(void * opaque)1084 static void kbd_send_chars(void *opaque)
1085 {
1086 TextConsole *s = opaque;
1087 int len;
1088 uint8_t buf[16];
1089
1090 len = qemu_chr_can_read(s->chr);
1091 if (len > s->out_fifo.count)
1092 len = s->out_fifo.count;
1093 if (len > 0) {
1094 if (len > sizeof(buf))
1095 len = sizeof(buf);
1096 qemu_fifo_read(&s->out_fifo, buf, len);
1097 qemu_chr_read(s->chr, buf, len);
1098 }
1099 /* characters are pending: we send them a bit later (XXX:
1100 horrible, should change char device API) */
1101 if (s->out_fifo.count > 0) {
1102 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1103 }
1104 }
1105
1106 /* called when an ascii key is pressed */
kbd_put_keysym(int keysym)1107 void kbd_put_keysym(int keysym)
1108 {
1109 TextConsole *s;
1110 uint8_t buf[16], *q;
1111 int c;
1112
1113 s = active_console;
1114 if (!s || (s->console_type == GRAPHIC_CONSOLE))
1115 return;
1116
1117 switch(keysym) {
1118 case QEMU_KEY_CTRL_UP:
1119 console_scroll(-1);
1120 break;
1121 case QEMU_KEY_CTRL_DOWN:
1122 console_scroll(1);
1123 break;
1124 case QEMU_KEY_CTRL_PAGEUP:
1125 console_scroll(-10);
1126 break;
1127 case QEMU_KEY_CTRL_PAGEDOWN:
1128 console_scroll(10);
1129 break;
1130 default:
1131 /* convert the QEMU keysym to VT100 key string */
1132 q = buf;
1133 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1134 *q++ = '\033';
1135 *q++ = '[';
1136 c = keysym - 0xe100;
1137 if (c >= 10)
1138 *q++ = '0' + (c / 10);
1139 *q++ = '0' + (c % 10);
1140 *q++ = '~';
1141 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1142 *q++ = '\033';
1143 *q++ = '[';
1144 *q++ = keysym & 0xff;
1145 } else {
1146 *q++ = keysym;
1147 }
1148 if (s->chr->chr_read) {
1149 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1150 kbd_send_chars(s);
1151 }
1152 break;
1153 }
1154 }
1155
text_console_invalidate(void * opaque)1156 static void text_console_invalidate(void *opaque)
1157 {
1158 TextConsole *s = (TextConsole *) opaque;
1159
1160 console_refresh(s);
1161 }
1162
text_console_update(void * opaque,console_ch_t * chardata)1163 static void text_console_update(void *opaque, console_ch_t *chardata)
1164 {
1165 TextConsole *s = (TextConsole *) opaque;
1166 int i, j, src;
1167
1168 if (s->text_x[0] <= s->text_x[1]) {
1169 src = (s->y_base + s->text_y[0]) * s->width;
1170 chardata += s->text_y[0] * s->width;
1171 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1172 for (j = 0; j < s->width; j ++, src ++)
1173 console_write_ch(chardata ++, s->cells[src].ch |
1174 (s->cells[src].t_attrib.fgcol << 12) |
1175 (s->cells[src].t_attrib.bgcol << 8) |
1176 (s->cells[src].t_attrib.bold << 21));
1177 dpy_update(s->ds, s->text_x[0], s->text_y[0],
1178 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1179 s->text_x[0] = s->width;
1180 s->text_y[0] = s->height;
1181 s->text_x[1] = 0;
1182 s->text_y[1] = 0;
1183 }
1184 if (s->cursor_invalidate) {
1185 dpy_cursor(s->ds, s->x, s->y);
1186 s->cursor_invalidate = 0;
1187 }
1188 }
1189
new_console(DisplayState * ds,console_type_t console_type)1190 static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1191 {
1192 TextConsole *s;
1193 int i;
1194
1195 if (nb_consoles >= MAX_CONSOLES)
1196 return NULL;
1197 s = qemu_mallocz(sizeof(TextConsole));
1198 if (!s) {
1199 return NULL;
1200 }
1201 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1202 (console_type == GRAPHIC_CONSOLE))) {
1203 active_console = s;
1204 }
1205 s->ds = ds;
1206 s->console_type = console_type;
1207 if (console_type != GRAPHIC_CONSOLE) {
1208 consoles[nb_consoles++] = s;
1209 } else {
1210 /* HACK: Put graphical consoles before text consoles. */
1211 for (i = nb_consoles; i > 0; i--) {
1212 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1213 break;
1214 consoles[i] = consoles[i - 1];
1215 }
1216 consoles[i] = s;
1217 }
1218 return s;
1219 }
1220
graphic_console_init(DisplayState * ds,vga_hw_update_ptr update,vga_hw_invalidate_ptr invalidate,vga_hw_screen_dump_ptr screen_dump,vga_hw_text_update_ptr text_update,void * opaque)1221 TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1222 vga_hw_invalidate_ptr invalidate,
1223 vga_hw_screen_dump_ptr screen_dump,
1224 vga_hw_text_update_ptr text_update,
1225 void *opaque)
1226 {
1227 TextConsole *s;
1228
1229 s = new_console(ds, GRAPHIC_CONSOLE);
1230 if (!s)
1231 return NULL;
1232 s->hw_update = update;
1233 s->hw_invalidate = invalidate;
1234 s->hw_screen_dump = screen_dump;
1235 s->hw_text_update = text_update;
1236 s->hw = opaque;
1237 return s;
1238 }
1239
is_graphic_console(void)1240 int is_graphic_console(void)
1241 {
1242 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1243 }
1244
console_color_init(DisplayState * ds)1245 void console_color_init(DisplayState *ds)
1246 {
1247 int i, j;
1248 for (j = 0; j < 2; j++) {
1249 for (i = 0; i < 8; i++) {
1250 color_table[j][i] = col_expand(ds,
1251 vga_get_color(ds, color_table_rgb[j][i]));
1252 }
1253 }
1254 }
1255
text_console_init(DisplayState * ds,const char * p)1256 CharDriverState *text_console_init(DisplayState *ds, const char *p)
1257 {
1258 CharDriverState *chr;
1259 TextConsole *s;
1260 unsigned width;
1261 unsigned height;
1262 static int color_inited;
1263
1264 chr = qemu_mallocz(sizeof(CharDriverState));
1265 if (!chr)
1266 return NULL;
1267 s = new_console(ds, TEXT_CONSOLE);
1268 if (!s) {
1269 free(chr);
1270 return NULL;
1271 }
1272 if (!p)
1273 p = DEFAULT_MONITOR_SIZE;
1274
1275 chr->opaque = s;
1276 chr->chr_write = console_puts;
1277 chr->chr_send_event = console_send_event;
1278
1279 s->chr = chr;
1280 s->out_fifo.buf = s->out_fifo_buf;
1281 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1282 s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1283
1284 if (!color_inited) {
1285 color_inited = 1;
1286 console_color_init(s->ds);
1287 }
1288 s->y_displayed = 0;
1289 s->y_base = 0;
1290 s->total_height = DEFAULT_BACKSCROLL;
1291 s->x = 0;
1292 s->y = 0;
1293 width = s->ds->width;
1294 height = s->ds->height;
1295 if (p != 0) {
1296 width = strtoul(p, (char **)&p, 10);
1297 if (*p == 'C') {
1298 p++;
1299 width *= FONT_WIDTH;
1300 }
1301 if (*p == 'x') {
1302 p++;
1303 height = strtoul(p, (char **)&p, 10);
1304 if (*p == 'C') {
1305 p++;
1306 height *= FONT_HEIGHT;
1307 }
1308 }
1309 }
1310 s->g_width = width;
1311 s->g_height = height;
1312
1313 s->hw_invalidate = text_console_invalidate;
1314 s->hw_text_update = text_console_update;
1315 s->hw = s;
1316
1317 /* Set text attribute defaults */
1318 s->t_attrib_default.bold = 0;
1319 s->t_attrib_default.uline = 0;
1320 s->t_attrib_default.blink = 0;
1321 s->t_attrib_default.invers = 0;
1322 s->t_attrib_default.unvisible = 0;
1323 s->t_attrib_default.fgcol = COLOR_WHITE;
1324 s->t_attrib_default.bgcol = COLOR_BLACK;
1325
1326 /* set current text attributes to default */
1327 s->t_attrib = s->t_attrib_default;
1328 text_console_resize(s);
1329
1330 qemu_chr_reset(chr);
1331
1332 return chr;
1333 }
1334
qemu_console_resize(QEMUConsole * console,int width,int height)1335 void qemu_console_resize(QEMUConsole *console, int width, int height)
1336 {
1337 if (console->g_width != width || console->g_height != height
1338 || !console->ds->data) {
1339 console->g_width = width;
1340 console->g_height = height;
1341 if (active_console == console) {
1342 dpy_resize(console->ds, width, height);
1343 }
1344 }
1345 }
1346