Merge branch 'qemu-cvs'
[kvm-userspace.git] / qemu / console.c
blobe7c00eccb3afea0dde6e9fdd8f239b92853a7092
1 /*
2 * QEMU graphical console
4 * Copyright (c) 2004 Fabrice Bellard
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
24 #include "qemu-common.h"
25 #include "console.h"
26 #include "qemu-timer.h"
28 //#define DEBUG_CONSOLE
29 #define DEFAULT_BACKSCROLL 512
30 #define MAX_CONSOLES 12
32 #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
33 #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
35 typedef struct TextAttributes {
36 uint8_t fgcol:4;
37 uint8_t bgcol:4;
38 uint8_t bold:1;
39 uint8_t uline:1;
40 uint8_t blink:1;
41 uint8_t invers:1;
42 uint8_t unvisible:1;
43 } TextAttributes;
45 typedef struct TextCell {
46 uint8_t ch;
47 TextAttributes t_attrib;
48 } TextCell;
50 #define MAX_ESC_PARAMS 3
52 enum TTYState {
53 TTY_STATE_NORM,
54 TTY_STATE_ESC,
55 TTY_STATE_CSI,
58 typedef struct QEMUFIFO {
59 uint8_t *buf;
60 int buf_size;
61 int count, wptr, rptr;
62 } QEMUFIFO;
64 static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
66 int l, len;
68 l = f->buf_size - f->count;
69 if (len1 > l)
70 len1 = l;
71 len = len1;
72 while (len > 0) {
73 l = f->buf_size - f->wptr;
74 if (l > len)
75 l = len;
76 memcpy(f->buf + f->wptr, buf, l);
77 f->wptr += l;
78 if (f->wptr >= f->buf_size)
79 f->wptr = 0;
80 buf += l;
81 len -= l;
83 f->count += len1;
84 return len1;
87 static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
89 int l, len;
91 if (len1 > f->count)
92 len1 = f->count;
93 len = len1;
94 while (len > 0) {
95 l = f->buf_size - f->rptr;
96 if (l > len)
97 l = len;
98 memcpy(buf, f->buf + f->rptr, l);
99 f->rptr += l;
100 if (f->rptr >= f->buf_size)
101 f->rptr = 0;
102 buf += l;
103 len -= l;
105 f->count -= len1;
106 return len1;
109 typedef enum {
110 GRAPHIC_CONSOLE,
111 TEXT_CONSOLE,
112 TEXT_CONSOLE_FIXED_SIZE
113 } console_type_t;
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 void *hw;
126 int g_width, g_height;
127 int width;
128 int height;
129 int total_height;
130 int backscroll_height;
131 int x, y;
132 int x_saved, y_saved;
133 int y_displayed;
134 int y_base;
135 TextAttributes t_attrib_default; /* default text attributes */
136 TextAttributes t_attrib; /* currently active text attributes */
137 TextCell *cells;
139 enum TTYState state;
140 int esc_params[MAX_ESC_PARAMS];
141 int nb_esc_params;
143 CharDriverState *chr;
144 /* fifo for key pressed */
145 QEMUFIFO out_fifo;
146 uint8_t out_fifo_buf[16];
147 QEMUTimer *kbd_timer;
150 static TextConsole *active_console;
151 static TextConsole *consoles[MAX_CONSOLES];
152 static int nb_consoles = 0;
154 void vga_hw_update(void)
156 if (active_console && active_console->hw_update)
157 active_console->hw_update(active_console->hw);
160 void vga_hw_invalidate(void)
162 if (active_console->hw_invalidate)
163 active_console->hw_invalidate(active_console->hw);
166 void vga_hw_screen_dump(const char *filename)
168 /* There is currently no was of specifying which screen we want to dump,
169 so always dump the dirst one. */
170 if (consoles[0]->hw_screen_dump)
171 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
174 /* convert a RGBA color to a color index usable in graphic primitives */
175 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
177 unsigned int r, g, b, color;
179 switch(ds->depth) {
180 #if 0
181 case 8:
182 r = (rgba >> 16) & 0xff;
183 g = (rgba >> 8) & 0xff;
184 b = (rgba) & 0xff;
185 color = (rgb_to_index[r] * 6 * 6) +
186 (rgb_to_index[g] * 6) +
187 (rgb_to_index[b]);
188 break;
189 #endif
190 case 15:
191 r = (rgba >> 16) & 0xff;
192 g = (rgba >> 8) & 0xff;
193 b = (rgba) & 0xff;
194 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
195 break;
196 case 16:
197 r = (rgba >> 16) & 0xff;
198 g = (rgba >> 8) & 0xff;
199 b = (rgba) & 0xff;
200 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
201 break;
202 case 32:
203 default:
204 color = rgba;
205 break;
207 return color;
210 static void vga_fill_rect (DisplayState *ds,
211 int posx, int posy, int width, int height, uint32_t color)
213 uint8_t *d, *d1;
214 int x, y, bpp;
216 bpp = (ds->depth + 7) >> 3;
217 d1 = ds->data +
218 ds->linesize * posy + bpp * posx;
219 for (y = 0; y < height; y++) {
220 d = d1;
221 switch(bpp) {
222 case 1:
223 for (x = 0; x < width; x++) {
224 *((uint8_t *)d) = color;
225 d++;
227 break;
228 case 2:
229 for (x = 0; x < width; x++) {
230 *((uint16_t *)d) = color;
231 d += 2;
233 break;
234 case 4:
235 for (x = 0; x < width; x++) {
236 *((uint32_t *)d) = color;
237 d += 4;
239 break;
241 d1 += ds->linesize;
245 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
246 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
248 const uint8_t *s;
249 uint8_t *d;
250 int wb, y, bpp;
252 bpp = (ds->depth + 7) >> 3;
253 wb = w * bpp;
254 if (yd <= ys) {
255 s = ds->data +
256 ds->linesize * ys + bpp * xs;
257 d = ds->data +
258 ds->linesize * yd + bpp * xd;
259 for (y = 0; y < h; y++) {
260 memmove(d, s, wb);
261 d += ds->linesize;
262 s += ds->linesize;
264 } else {
265 s = ds->data +
266 ds->linesize * (ys + h - 1) + bpp * xs;
267 d = ds->data +
268 ds->linesize * (yd + h - 1) + bpp * xd;
269 for (y = 0; y < h; y++) {
270 memmove(d, s, wb);
271 d -= ds->linesize;
272 s -= ds->linesize;
277 /***********************************************************/
278 /* basic char display */
280 #define FONT_HEIGHT 16
281 #define FONT_WIDTH 8
283 #include "vgafont.h"
285 #define cbswap_32(__x) \
286 ((uint32_t)( \
287 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
288 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
289 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
290 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
292 #ifdef WORDS_BIGENDIAN
293 #define PAT(x) x
294 #else
295 #define PAT(x) cbswap_32(x)
296 #endif
298 static const uint32_t dmask16[16] = {
299 PAT(0x00000000),
300 PAT(0x000000ff),
301 PAT(0x0000ff00),
302 PAT(0x0000ffff),
303 PAT(0x00ff0000),
304 PAT(0x00ff00ff),
305 PAT(0x00ffff00),
306 PAT(0x00ffffff),
307 PAT(0xff000000),
308 PAT(0xff0000ff),
309 PAT(0xff00ff00),
310 PAT(0xff00ffff),
311 PAT(0xffff0000),
312 PAT(0xffff00ff),
313 PAT(0xffffff00),
314 PAT(0xffffffff),
317 static const uint32_t dmask4[4] = {
318 PAT(0x00000000),
319 PAT(0x0000ffff),
320 PAT(0xffff0000),
321 PAT(0xffffffff),
324 static uint32_t color_table[2][8];
326 enum color_names {
327 COLOR_BLACK = 0,
328 COLOR_RED = 1,
329 COLOR_GREEN = 2,
330 COLOR_YELLOW = 3,
331 COLOR_BLUE = 4,
332 COLOR_MAGENTA = 5,
333 COLOR_CYAN = 6,
334 COLOR_WHITE = 7
337 static const uint32_t color_table_rgb[2][8] = {
338 { /* dark */
339 QEMU_RGB(0x00, 0x00, 0x00), /* black */
340 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
341 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
342 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
343 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
344 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
345 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
346 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
348 { /* bright */
349 QEMU_RGB(0x00, 0x00, 0x00), /* black */
350 QEMU_RGB(0xff, 0x00, 0x00), /* red */
351 QEMU_RGB(0x00, 0xff, 0x00), /* green */
352 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
353 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
354 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
355 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
356 QEMU_RGB(0xff, 0xff, 0xff), /* white */
360 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
362 switch(ds->depth) {
363 case 8:
364 col |= col << 8;
365 col |= col << 16;
366 break;
367 case 15:
368 case 16:
369 col |= col << 16;
370 break;
371 default:
372 break;
375 return col;
377 #ifdef DEBUG_CONSOLE
378 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
380 if (t_attrib->bold) {
381 printf("b");
382 } else {
383 printf(" ");
385 if (t_attrib->uline) {
386 printf("u");
387 } else {
388 printf(" ");
390 if (t_attrib->blink) {
391 printf("l");
392 } else {
393 printf(" ");
395 if (t_attrib->invers) {
396 printf("i");
397 } else {
398 printf(" ");
400 if (t_attrib->unvisible) {
401 printf("n");
402 } else {
403 printf(" ");
406 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
408 #endif
410 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
411 TextAttributes *t_attrib)
413 uint8_t *d;
414 const uint8_t *font_ptr;
415 unsigned int font_data, linesize, xorcol, bpp;
416 int i;
417 unsigned int fgcol, bgcol;
419 #ifdef DEBUG_CONSOLE
420 printf("x: %2i y: %2i", x, y);
421 console_print_text_attributes(t_attrib, ch);
422 #endif
424 if (t_attrib->invers) {
425 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
426 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
427 } else {
428 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
429 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
432 bpp = (ds->depth + 7) >> 3;
433 d = ds->data +
434 ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
435 linesize = ds->linesize;
436 font_ptr = vgafont16 + FONT_HEIGHT * ch;
437 xorcol = bgcol ^ fgcol;
438 switch(ds->depth) {
439 case 8:
440 for(i = 0; i < FONT_HEIGHT; i++) {
441 font_data = *font_ptr++;
442 if (t_attrib->uline
443 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
444 font_data = 0xFFFF;
446 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
447 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
448 d += linesize;
450 break;
451 case 16:
452 case 15:
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;
459 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
460 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
461 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
462 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
463 d += linesize;
465 break;
466 case 32:
467 for(i = 0; i < FONT_HEIGHT; i++) {
468 font_data = *font_ptr++;
469 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
470 font_data = 0xFFFF;
472 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
473 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
474 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
475 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
476 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
477 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
478 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
479 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
480 d += linesize;
482 break;
486 static void text_console_resize(TextConsole *s)
488 TextCell *cells, *c, *c1;
489 int w1, x, y, last_width;
491 last_width = s->width;
492 s->width = s->g_width / FONT_WIDTH;
493 s->height = s->g_height / FONT_HEIGHT;
495 w1 = last_width;
496 if (s->width < w1)
497 w1 = s->width;
499 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
500 for(y = 0; y < s->total_height; y++) {
501 c = &cells[y * s->width];
502 if (w1 > 0) {
503 c1 = &s->cells[y * last_width];
504 for(x = 0; x < w1; x++) {
505 *c++ = *c1++;
508 for(x = w1; x < s->width; x++) {
509 c->ch = ' ';
510 c->t_attrib = s->t_attrib_default;
511 c++;
514 qemu_free(s->cells);
515 s->cells = cells;
518 static void update_xy(TextConsole *s, int x, int y)
520 TextCell *c;
521 int y1, y2;
523 if (s == active_console) {
524 y1 = (s->y_base + y) % s->total_height;
525 y2 = y1 - s->y_displayed;
526 if (y2 < 0)
527 y2 += s->total_height;
528 if (y2 < s->height) {
529 c = &s->cells[y1 * s->width + x];
530 vga_putcharxy(s->ds, x, y2, c->ch,
531 &(c->t_attrib));
532 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
533 FONT_WIDTH, FONT_HEIGHT);
538 static void console_show_cursor(TextConsole *s, int show)
540 TextCell *c;
541 int y, y1;
543 if (s == active_console) {
544 int x = s->x;
545 if (x >= s->width) {
546 x = s->width - 1;
548 y1 = (s->y_base + s->y) % s->total_height;
549 y = y1 - s->y_displayed;
550 if (y < 0)
551 y += s->total_height;
552 if (y < s->height) {
553 c = &s->cells[y1 * s->width + x];
554 if (show) {
555 TextAttributes t_attrib = s->t_attrib_default;
556 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
557 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
558 } else {
559 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
561 dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
562 FONT_WIDTH, FONT_HEIGHT);
567 static void console_refresh(TextConsole *s)
569 TextCell *c;
570 int x, y, y1;
572 if (s != active_console)
573 return;
575 vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
576 color_table[0][COLOR_BLACK]);
577 y1 = s->y_displayed;
578 for(y = 0; y < s->height; y++) {
579 c = s->cells + y1 * s->width;
580 for(x = 0; x < s->width; x++) {
581 vga_putcharxy(s->ds, x, y, c->ch,
582 &(c->t_attrib));
583 c++;
585 if (++y1 == s->total_height)
586 y1 = 0;
588 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
589 console_show_cursor(s, 1);
592 static void console_scroll(int ydelta)
594 TextConsole *s;
595 int i, y1;
597 s = active_console;
598 if (!s || (s->console_type == GRAPHIC_CONSOLE))
599 return;
601 if (ydelta > 0) {
602 for(i = 0; i < ydelta; i++) {
603 if (s->y_displayed == s->y_base)
604 break;
605 if (++s->y_displayed == s->total_height)
606 s->y_displayed = 0;
608 } else {
609 ydelta = -ydelta;
610 i = s->backscroll_height;
611 if (i > s->total_height - s->height)
612 i = s->total_height - s->height;
613 y1 = s->y_base - i;
614 if (y1 < 0)
615 y1 += s->total_height;
616 for(i = 0; i < ydelta; i++) {
617 if (s->y_displayed == y1)
618 break;
619 if (--s->y_displayed < 0)
620 s->y_displayed = s->total_height - 1;
623 console_refresh(s);
626 static void console_put_lf(TextConsole *s)
628 TextCell *c;
629 int x, y1;
631 s->y++;
632 if (s->y >= s->height) {
633 s->y = s->height - 1;
635 if (s->y_displayed == s->y_base) {
636 if (++s->y_displayed == s->total_height)
637 s->y_displayed = 0;
639 if (++s->y_base == s->total_height)
640 s->y_base = 0;
641 if (s->backscroll_height < s->total_height)
642 s->backscroll_height++;
643 y1 = (s->y_base + s->height - 1) % s->total_height;
644 c = &s->cells[y1 * s->width];
645 for(x = 0; x < s->width; x++) {
646 c->ch = ' ';
647 c->t_attrib = s->t_attrib_default;
648 c++;
650 if (s == active_console && s->y_displayed == s->y_base) {
651 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
652 s->width * FONT_WIDTH,
653 (s->height - 1) * FONT_HEIGHT);
654 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
655 s->width * FONT_WIDTH, FONT_HEIGHT,
656 color_table[0][s->t_attrib_default.bgcol]);
657 dpy_update(s->ds, 0, 0,
658 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
663 /* Set console attributes depending on the current escape codes.
664 * NOTE: I know this code is not very efficient (checking every color for it
665 * self) but it is more readable and better maintainable.
667 static void console_handle_escape(TextConsole *s)
669 int i;
671 for (i=0; i<s->nb_esc_params; i++) {
672 switch (s->esc_params[i]) {
673 case 0: /* reset all console attributes to default */
674 s->t_attrib = s->t_attrib_default;
675 break;
676 case 1:
677 s->t_attrib.bold = 1;
678 break;
679 case 4:
680 s->t_attrib.uline = 1;
681 break;
682 case 5:
683 s->t_attrib.blink = 1;
684 break;
685 case 7:
686 s->t_attrib.invers = 1;
687 break;
688 case 8:
689 s->t_attrib.unvisible = 1;
690 break;
691 case 22:
692 s->t_attrib.bold = 0;
693 break;
694 case 24:
695 s->t_attrib.uline = 0;
696 break;
697 case 25:
698 s->t_attrib.blink = 0;
699 break;
700 case 27:
701 s->t_attrib.invers = 0;
702 break;
703 case 28:
704 s->t_attrib.unvisible = 0;
705 break;
706 /* set foreground color */
707 case 30:
708 s->t_attrib.fgcol=COLOR_BLACK;
709 break;
710 case 31:
711 s->t_attrib.fgcol=COLOR_RED;
712 break;
713 case 32:
714 s->t_attrib.fgcol=COLOR_GREEN;
715 break;
716 case 33:
717 s->t_attrib.fgcol=COLOR_YELLOW;
718 break;
719 case 34:
720 s->t_attrib.fgcol=COLOR_BLUE;
721 break;
722 case 35:
723 s->t_attrib.fgcol=COLOR_MAGENTA;
724 break;
725 case 36:
726 s->t_attrib.fgcol=COLOR_CYAN;
727 break;
728 case 37:
729 s->t_attrib.fgcol=COLOR_WHITE;
730 break;
731 /* set background color */
732 case 40:
733 s->t_attrib.bgcol=COLOR_BLACK;
734 break;
735 case 41:
736 s->t_attrib.bgcol=COLOR_RED;
737 break;
738 case 42:
739 s->t_attrib.bgcol=COLOR_GREEN;
740 break;
741 case 43:
742 s->t_attrib.bgcol=COLOR_YELLOW;
743 break;
744 case 44:
745 s->t_attrib.bgcol=COLOR_BLUE;
746 break;
747 case 45:
748 s->t_attrib.bgcol=COLOR_MAGENTA;
749 break;
750 case 46:
751 s->t_attrib.bgcol=COLOR_CYAN;
752 break;
753 case 47:
754 s->t_attrib.bgcol=COLOR_WHITE;
755 break;
760 static void console_clear_xy(TextConsole *s, int x, int y)
762 int y1 = (s->y_base + y) % s->total_height;
763 TextCell *c = &s->cells[y1 * s->width + x];
764 c->ch = ' ';
765 c->t_attrib = s->t_attrib_default;
766 c++;
767 update_xy(s, x, y);
770 static void console_putchar(TextConsole *s, int ch)
772 TextCell *c;
773 int y1, i;
774 int x, y;
776 switch(s->state) {
777 case TTY_STATE_NORM:
778 switch(ch) {
779 case '\r': /* carriage return */
780 s->x = 0;
781 break;
782 case '\n': /* newline */
783 console_put_lf(s);
784 break;
785 case '\b': /* backspace */
786 if (s->x > 0)
787 s->x--;
788 break;
789 case '\t': /* tabspace */
790 if (s->x + (8 - (s->x % 8)) > s->width) {
791 s->x = 0;
792 console_put_lf(s);
793 } else {
794 s->x = s->x + (8 - (s->x % 8));
796 break;
797 case '\a': /* alert aka. bell */
798 /* TODO: has to be implemented */
799 break;
800 case 14:
801 /* SI (shift in), character set 0 (ignored) */
802 break;
803 case 15:
804 /* SO (shift out), character set 1 (ignored) */
805 break;
806 case 27: /* esc (introducing an escape sequence) */
807 s->state = TTY_STATE_ESC;
808 break;
809 default:
810 if (s->x >= s->width) {
811 /* line wrap */
812 s->x = 0;
813 console_put_lf(s);
815 y1 = (s->y_base + s->y) % s->total_height;
816 c = &s->cells[y1 * s->width + s->x];
817 c->ch = ch;
818 c->t_attrib = s->t_attrib;
819 update_xy(s, s->x, s->y);
820 s->x++;
821 break;
823 break;
824 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
825 if (ch == '[') {
826 for(i=0;i<MAX_ESC_PARAMS;i++)
827 s->esc_params[i] = 0;
828 s->nb_esc_params = 0;
829 s->state = TTY_STATE_CSI;
830 } else {
831 s->state = TTY_STATE_NORM;
833 break;
834 case TTY_STATE_CSI: /* handle escape sequence parameters */
835 if (ch >= '0' && ch <= '9') {
836 if (s->nb_esc_params < MAX_ESC_PARAMS) {
837 s->esc_params[s->nb_esc_params] =
838 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
840 } else {
841 s->nb_esc_params++;
842 if (ch == ';')
843 break;
844 #ifdef DEBUG_CONSOLE
845 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
846 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
847 #endif
848 s->state = TTY_STATE_NORM;
849 switch(ch) {
850 case 'A':
851 /* move cursor up */
852 if (s->esc_params[0] == 0) {
853 s->esc_params[0] = 1;
855 s->y -= s->esc_params[0];
856 if (s->y < 0) {
857 s->y = 0;
859 break;
860 case 'B':
861 /* move cursor down */
862 if (s->esc_params[0] == 0) {
863 s->esc_params[0] = 1;
865 s->y += s->esc_params[0];
866 if (s->y >= s->height) {
867 s->y = s->height - 1;
869 break;
870 case 'C':
871 /* move cursor right */
872 if (s->esc_params[0] == 0) {
873 s->esc_params[0] = 1;
875 s->x += s->esc_params[0];
876 if (s->x >= s->width) {
877 s->x = s->width - 1;
879 break;
880 case 'D':
881 /* move cursor left */
882 if (s->esc_params[0] == 0) {
883 s->esc_params[0] = 1;
885 s->x -= s->esc_params[0];
886 if (s->x < 0) {
887 s->x = 0;
889 break;
890 case 'G':
891 /* move cursor to column */
892 s->x = s->esc_params[0] - 1;
893 if (s->x < 0) {
894 s->x = 0;
896 break;
897 case 'f':
898 case 'H':
899 /* move cursor to row, column */
900 s->x = s->esc_params[1] - 1;
901 if (s->x < 0) {
902 s->x = 0;
904 s->y = s->esc_params[0] - 1;
905 if (s->y < 0) {
906 s->y = 0;
908 break;
909 case 'J':
910 switch (s->esc_params[0]) {
911 case 0:
912 /* clear to end of screen */
913 for (y = s->y; y < s->height; y++) {
914 for (x = 0; x < s->width; x++) {
915 if (y == s->y && x < s->x) {
916 continue;
918 console_clear_xy(s, x, y);
921 break;
922 case 1:
923 /* clear from beginning of screen */
924 for (y = 0; y <= s->y; y++) {
925 for (x = 0; x < s->width; x++) {
926 if (y == s->y && x > s->x) {
927 break;
929 console_clear_xy(s, x, y);
932 break;
933 case 2:
934 /* clear entire screen */
935 for (y = 0; y <= s->height; y++) {
936 for (x = 0; x < s->width; x++) {
937 console_clear_xy(s, x, y);
940 break;
942 case 'K':
943 switch (s->esc_params[0]) {
944 case 0:
945 /* clear to eol */
946 for(x = s->x; x < s->width; x++) {
947 console_clear_xy(s, x, s->y);
949 break;
950 case 1:
951 /* clear from beginning of line */
952 for (x = 0; x <= s->x; x++) {
953 console_clear_xy(s, x, s->y);
955 break;
956 case 2:
957 /* clear entire line */
958 for(x = 0; x < s->width; x++) {
959 console_clear_xy(s, x, s->y);
961 break;
963 break;
964 case 'm':
965 console_handle_escape(s);
966 break;
967 case 'n':
968 /* report cursor position */
969 /* TODO: send ESC[row;colR */
970 break;
971 case 's':
972 /* save cursor position */
973 s->x_saved = s->x;
974 s->y_saved = s->y;
975 break;
976 case 'u':
977 /* restore cursor position */
978 s->x = s->x_saved;
979 s->y = s->y_saved;
980 break;
981 default:
982 #ifdef DEBUG_CONSOLE
983 fprintf(stderr, "unhandled escape character '%c'\n", ch);
984 #endif
985 break;
987 break;
992 void console_select(unsigned int index)
994 TextConsole *s;
996 if (index >= MAX_CONSOLES)
997 return;
998 s = consoles[index];
999 if (s) {
1000 active_console = s;
1001 if (s->console_type != GRAPHIC_CONSOLE) {
1002 if (s->g_width != s->ds->width ||
1003 s->g_height != s->ds->height) {
1004 if (s->console_type == TEXT_CONSOLE_FIXED_SIZE) {
1005 dpy_resize(s->ds, s->g_width, s->g_height);
1006 } else {
1007 s->g_width = s->ds->width;
1008 s->g_height = s->ds->height;
1009 text_console_resize(s);
1012 console_refresh(s);
1013 } else {
1014 vga_hw_invalidate();
1019 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1021 TextConsole *s = chr->opaque;
1022 int i;
1024 console_show_cursor(s, 0);
1025 for(i = 0; i < len; i++) {
1026 console_putchar(s, buf[i]);
1028 console_show_cursor(s, 1);
1029 return len;
1032 static void console_send_event(CharDriverState *chr, int event)
1034 TextConsole *s = chr->opaque;
1035 int i;
1037 if (event == CHR_EVENT_FOCUS) {
1038 for(i = 0; i < nb_consoles; i++) {
1039 if (consoles[i] == s) {
1040 console_select(i);
1041 break;
1047 static void kbd_send_chars(void *opaque)
1049 TextConsole *s = opaque;
1050 int len;
1051 uint8_t buf[16];
1053 len = qemu_chr_can_read(s->chr);
1054 if (len > s->out_fifo.count)
1055 len = s->out_fifo.count;
1056 if (len > 0) {
1057 if (len > sizeof(buf))
1058 len = sizeof(buf);
1059 qemu_fifo_read(&s->out_fifo, buf, len);
1060 qemu_chr_read(s->chr, buf, len);
1062 /* characters are pending: we send them a bit later (XXX:
1063 horrible, should change char device API) */
1064 if (s->out_fifo.count > 0) {
1065 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1069 /* called when an ascii key is pressed */
1070 void kbd_put_keysym(int keysym)
1072 TextConsole *s;
1073 uint8_t buf[16], *q;
1074 int c;
1076 s = active_console;
1077 if (!s || (s->console_type == GRAPHIC_CONSOLE))
1078 return;
1080 switch(keysym) {
1081 case QEMU_KEY_CTRL_UP:
1082 console_scroll(-1);
1083 break;
1084 case QEMU_KEY_CTRL_DOWN:
1085 console_scroll(1);
1086 break;
1087 case QEMU_KEY_CTRL_PAGEUP:
1088 console_scroll(-10);
1089 break;
1090 case QEMU_KEY_CTRL_PAGEDOWN:
1091 console_scroll(10);
1092 break;
1093 default:
1094 /* convert the QEMU keysym to VT100 key string */
1095 q = buf;
1096 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1097 *q++ = '\033';
1098 *q++ = '[';
1099 c = keysym - 0xe100;
1100 if (c >= 10)
1101 *q++ = '0' + (c / 10);
1102 *q++ = '0' + (c % 10);
1103 *q++ = '~';
1104 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1105 *q++ = '\033';
1106 *q++ = '[';
1107 *q++ = keysym & 0xff;
1108 } else {
1109 *q++ = keysym;
1111 if (s->chr->chr_read) {
1112 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1113 kbd_send_chars(s);
1115 break;
1119 static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1121 TextConsole *s;
1122 int i;
1124 if (nb_consoles >= MAX_CONSOLES)
1125 return NULL;
1126 s = qemu_mallocz(sizeof(TextConsole));
1127 if (!s) {
1128 return NULL;
1130 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1131 (console_type == GRAPHIC_CONSOLE))) {
1132 active_console = s;
1134 s->ds = ds;
1135 s->console_type = console_type;
1136 if (console_type != GRAPHIC_CONSOLE) {
1137 consoles[nb_consoles++] = s;
1138 } else {
1139 /* HACK: Put graphical consoles before text consoles. */
1140 for (i = nb_consoles; i > 0; i--) {
1141 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1142 break;
1143 consoles[i] = consoles[i - 1];
1145 consoles[i] = s;
1147 return s;
1150 TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1151 vga_hw_invalidate_ptr invalidate,
1152 vga_hw_screen_dump_ptr screen_dump,
1153 void *opaque)
1155 TextConsole *s;
1157 s = new_console(ds, GRAPHIC_CONSOLE);
1158 if (!s)
1159 return NULL;
1160 s->hw_update = update;
1161 s->hw_invalidate = invalidate;
1162 s->hw_screen_dump = screen_dump;
1163 s->hw = opaque;
1164 return s;
1167 int is_graphic_console(void)
1169 return active_console->console_type == GRAPHIC_CONSOLE;
1172 void console_color_init(DisplayState *ds)
1174 int i, j;
1175 for (j = 0; j < 2; j++) {
1176 for (i = 0; i < 8; i++) {
1177 color_table[j][i] = col_expand(ds,
1178 vga_get_color(ds, color_table_rgb[j][i]));
1183 CharDriverState *text_console_init(DisplayState *ds, const char *p)
1185 CharDriverState *chr;
1186 TextConsole *s;
1187 unsigned width;
1188 unsigned height;
1189 static int color_inited;
1191 chr = qemu_mallocz(sizeof(CharDriverState));
1192 if (!chr)
1193 return NULL;
1194 s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
1195 if (!s) {
1196 free(chr);
1197 return NULL;
1199 chr->opaque = s;
1200 chr->chr_write = console_puts;
1201 chr->chr_send_event = console_send_event;
1203 s->chr = chr;
1204 s->out_fifo.buf = s->out_fifo_buf;
1205 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1206 s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1208 if (!color_inited) {
1209 color_inited = 1;
1210 console_color_init(s->ds);
1212 s->y_displayed = 0;
1213 s->y_base = 0;
1214 s->total_height = DEFAULT_BACKSCROLL;
1215 s->x = 0;
1216 s->y = 0;
1217 width = s->ds->width;
1218 height = s->ds->height;
1219 if (p != 0) {
1220 width = strtoul(p, (char **)&p, 10);
1221 if (*p == 'C') {
1222 p++;
1223 width *= FONT_WIDTH;
1225 if (*p == 'x') {
1226 p++;
1227 height = strtoul(p, (char **)&p, 10);
1228 if (*p == 'C') {
1229 p++;
1230 height *= FONT_HEIGHT;
1234 s->g_width = width;
1235 s->g_height = height;
1237 /* Set text attribute defaults */
1238 s->t_attrib_default.bold = 0;
1239 s->t_attrib_default.uline = 0;
1240 s->t_attrib_default.blink = 0;
1241 s->t_attrib_default.invers = 0;
1242 s->t_attrib_default.unvisible = 0;
1243 s->t_attrib_default.fgcol = COLOR_WHITE;
1244 s->t_attrib_default.bgcol = COLOR_BLACK;
1246 /* set current text attributes to default */
1247 s->t_attrib = s->t_attrib_default;
1248 text_console_resize(s);
1250 qemu_chr_reset(chr);
1252 return chr;