Use memory globals for G registers
[qemu/malc.git] / console.c
blob880ac831a5940ac76b2b6749ba8b9419ff10ecba
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 vga_hw_text_update_ptr hw_text_update;
125 void *hw;
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;
141 enum TTYState state;
142 int esc_params[MAX_ESC_PARAMS];
143 int nb_esc_params;
145 CharDriverState *chr;
146 /* fifo for key pressed */
147 QEMUFIFO out_fifo;
148 uint8_t out_fifo_buf[16];
149 QEMUTimer *kbd_timer;
152 static TextConsole *active_console;
153 static TextConsole *consoles[MAX_CONSOLES];
154 static int nb_consoles = 0;
156 void vga_hw_update(void)
158 if (active_console && active_console->hw_update)
159 active_console->hw_update(active_console->hw);
162 void vga_hw_invalidate(void)
164 if (active_console->hw_invalidate)
165 active_console->hw_invalidate(active_console->hw);
168 void vga_hw_screen_dump(const char *filename)
170 /* There is currently no was of specifying which screen we want to dump,
171 so always dump the dirst one. */
172 if (consoles[0]->hw_screen_dump)
173 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
176 void vga_hw_text_update(console_ch_t *chardata)
178 if (active_console && active_console->hw_text_update)
179 active_console->hw_text_update(active_console->hw, chardata);
182 /* convert a RGBA color to a color index usable in graphic primitives */
183 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
185 unsigned int r, g, b, color;
187 switch(ds->depth) {
188 #if 0
189 case 8:
190 r = (rgba >> 16) & 0xff;
191 g = (rgba >> 8) & 0xff;
192 b = (rgba) & 0xff;
193 color = (rgb_to_index[r] * 6 * 6) +
194 (rgb_to_index[g] * 6) +
195 (rgb_to_index[b]);
196 break;
197 #endif
198 case 15:
199 r = (rgba >> 16) & 0xff;
200 g = (rgba >> 8) & 0xff;
201 b = (rgba) & 0xff;
202 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
203 break;
204 case 16:
205 r = (rgba >> 16) & 0xff;
206 g = (rgba >> 8) & 0xff;
207 b = (rgba) & 0xff;
208 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
209 break;
210 case 32:
211 default:
212 color = rgba;
213 break;
215 return color;
218 static void vga_fill_rect (DisplayState *ds,
219 int posx, int posy, int width, int height, uint32_t color)
221 uint8_t *d, *d1;
222 int x, y, bpp;
224 bpp = (ds->depth + 7) >> 3;
225 d1 = ds->data +
226 ds->linesize * posy + bpp * posx;
227 for (y = 0; y < height; y++) {
228 d = d1;
229 switch(bpp) {
230 case 1:
231 for (x = 0; x < width; x++) {
232 *((uint8_t *)d) = color;
233 d++;
235 break;
236 case 2:
237 for (x = 0; x < width; x++) {
238 *((uint16_t *)d) = color;
239 d += 2;
241 break;
242 case 4:
243 for (x = 0; x < width; x++) {
244 *((uint32_t *)d) = color;
245 d += 4;
247 break;
249 d1 += ds->linesize;
253 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
254 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
256 const uint8_t *s;
257 uint8_t *d;
258 int wb, y, bpp;
260 bpp = (ds->depth + 7) >> 3;
261 wb = w * bpp;
262 if (yd <= ys) {
263 s = ds->data +
264 ds->linesize * ys + bpp * xs;
265 d = ds->data +
266 ds->linesize * yd + bpp * xd;
267 for (y = 0; y < h; y++) {
268 memmove(d, s, wb);
269 d += ds->linesize;
270 s += ds->linesize;
272 } else {
273 s = ds->data +
274 ds->linesize * (ys + h - 1) + bpp * xs;
275 d = ds->data +
276 ds->linesize * (yd + h - 1) + bpp * xd;
277 for (y = 0; y < h; y++) {
278 memmove(d, s, wb);
279 d -= ds->linesize;
280 s -= ds->linesize;
285 /***********************************************************/
286 /* basic char display */
288 #define FONT_HEIGHT 16
289 #define FONT_WIDTH 8
291 #include "vgafont.h"
293 #define cbswap_32(__x) \
294 ((uint32_t)( \
295 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
296 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
297 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
298 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
300 #ifdef WORDS_BIGENDIAN
301 #define PAT(x) x
302 #else
303 #define PAT(x) cbswap_32(x)
304 #endif
306 static const uint32_t dmask16[16] = {
307 PAT(0x00000000),
308 PAT(0x000000ff),
309 PAT(0x0000ff00),
310 PAT(0x0000ffff),
311 PAT(0x00ff0000),
312 PAT(0x00ff00ff),
313 PAT(0x00ffff00),
314 PAT(0x00ffffff),
315 PAT(0xff000000),
316 PAT(0xff0000ff),
317 PAT(0xff00ff00),
318 PAT(0xff00ffff),
319 PAT(0xffff0000),
320 PAT(0xffff00ff),
321 PAT(0xffffff00),
322 PAT(0xffffffff),
325 static const uint32_t dmask4[4] = {
326 PAT(0x00000000),
327 PAT(0x0000ffff),
328 PAT(0xffff0000),
329 PAT(0xffffffff),
332 static uint32_t color_table[2][8];
334 enum color_names {
335 COLOR_BLACK = 0,
336 COLOR_RED = 1,
337 COLOR_GREEN = 2,
338 COLOR_YELLOW = 3,
339 COLOR_BLUE = 4,
340 COLOR_MAGENTA = 5,
341 COLOR_CYAN = 6,
342 COLOR_WHITE = 7
345 static const uint32_t color_table_rgb[2][8] = {
346 { /* dark */
347 QEMU_RGB(0x00, 0x00, 0x00), /* black */
348 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
349 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
350 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
351 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
352 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
353 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
354 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
356 { /* bright */
357 QEMU_RGB(0x00, 0x00, 0x00), /* black */
358 QEMU_RGB(0xff, 0x00, 0x00), /* red */
359 QEMU_RGB(0x00, 0xff, 0x00), /* green */
360 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
361 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
362 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
363 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
364 QEMU_RGB(0xff, 0xff, 0xff), /* white */
368 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
370 switch(ds->depth) {
371 case 8:
372 col |= col << 8;
373 col |= col << 16;
374 break;
375 case 15:
376 case 16:
377 col |= col << 16;
378 break;
379 default:
380 break;
383 return col;
385 #ifdef DEBUG_CONSOLE
386 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
388 if (t_attrib->bold) {
389 printf("b");
390 } else {
391 printf(" ");
393 if (t_attrib->uline) {
394 printf("u");
395 } else {
396 printf(" ");
398 if (t_attrib->blink) {
399 printf("l");
400 } else {
401 printf(" ");
403 if (t_attrib->invers) {
404 printf("i");
405 } else {
406 printf(" ");
408 if (t_attrib->unvisible) {
409 printf("n");
410 } else {
411 printf(" ");
414 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
416 #endif
418 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
419 TextAttributes *t_attrib)
421 uint8_t *d;
422 const uint8_t *font_ptr;
423 unsigned int font_data, linesize, xorcol, bpp;
424 int i;
425 unsigned int fgcol, bgcol;
427 #ifdef DEBUG_CONSOLE
428 printf("x: %2i y: %2i", x, y);
429 console_print_text_attributes(t_attrib, ch);
430 #endif
432 if (t_attrib->invers) {
433 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
434 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
435 } else {
436 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
437 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
440 bpp = (ds->depth + 7) >> 3;
441 d = ds->data +
442 ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
443 linesize = ds->linesize;
444 font_ptr = vgafont16 + FONT_HEIGHT * ch;
445 xorcol = bgcol ^ fgcol;
446 switch(ds->depth) {
447 case 8:
448 for(i = 0; i < FONT_HEIGHT; i++) {
449 font_data = *font_ptr++;
450 if (t_attrib->uline
451 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
452 font_data = 0xFFFF;
454 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
455 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
456 d += linesize;
458 break;
459 case 16:
460 case 15:
461 for(i = 0; i < FONT_HEIGHT; i++) {
462 font_data = *font_ptr++;
463 if (t_attrib->uline
464 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
465 font_data = 0xFFFF;
467 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
468 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
469 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
470 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
471 d += linesize;
473 break;
474 case 32:
475 for(i = 0; i < FONT_HEIGHT; i++) {
476 font_data = *font_ptr++;
477 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
478 font_data = 0xFFFF;
480 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
481 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
482 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
483 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
484 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
485 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
486 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
487 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
488 d += linesize;
490 break;
494 static void text_console_resize(TextConsole *s)
496 TextCell *cells, *c, *c1;
497 int w1, x, y, last_width;
499 last_width = s->width;
500 s->width = s->g_width / FONT_WIDTH;
501 s->height = s->g_height / FONT_HEIGHT;
503 w1 = last_width;
504 if (s->width < w1)
505 w1 = s->width;
507 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
508 for(y = 0; y < s->total_height; y++) {
509 c = &cells[y * s->width];
510 if (w1 > 0) {
511 c1 = &s->cells[y * last_width];
512 for(x = 0; x < w1; x++) {
513 *c++ = *c1++;
516 for(x = w1; x < s->width; x++) {
517 c->ch = ' ';
518 c->t_attrib = s->t_attrib_default;
519 c++;
522 qemu_free(s->cells);
523 s->cells = cells;
526 static inline void text_update_xy(TextConsole *s, int x, int y)
528 s->text_x[0] = MIN(s->text_x[0], x);
529 s->text_x[1] = MAX(s->text_x[1], x);
530 s->text_y[0] = MIN(s->text_y[0], y);
531 s->text_y[1] = MAX(s->text_y[1], y);
534 static void update_xy(TextConsole *s, int x, int y)
536 TextCell *c;
537 int y1, y2;
539 if (s == active_console) {
540 if (!s->ds->depth) {
541 text_update_xy(s, x, y);
542 return;
545 y1 = (s->y_base + y) % s->total_height;
546 y2 = y1 - s->y_displayed;
547 if (y2 < 0)
548 y2 += s->total_height;
549 if (y2 < s->height) {
550 c = &s->cells[y1 * s->width + x];
551 vga_putcharxy(s->ds, x, y2, c->ch,
552 &(c->t_attrib));
553 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
554 FONT_WIDTH, FONT_HEIGHT);
559 static void console_show_cursor(TextConsole *s, int show)
561 TextCell *c;
562 int y, y1;
564 if (s == active_console) {
565 int x = s->x;
567 if (!s->ds->depth) {
568 s->cursor_invalidate = 1;
569 return;
572 if (x >= s->width) {
573 x = s->width - 1;
575 y1 = (s->y_base + s->y) % s->total_height;
576 y = y1 - s->y_displayed;
577 if (y < 0)
578 y += s->total_height;
579 if (y < s->height) {
580 c = &s->cells[y1 * s->width + x];
581 if (show) {
582 TextAttributes t_attrib = s->t_attrib_default;
583 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
584 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
585 } else {
586 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
588 dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
589 FONT_WIDTH, FONT_HEIGHT);
594 static void console_refresh(TextConsole *s)
596 TextCell *c;
597 int x, y, y1;
599 if (s != active_console)
600 return;
601 if (!s->ds->depth) {
602 s->text_x[0] = 0;
603 s->text_y[0] = 0;
604 s->text_x[1] = s->width - 1;
605 s->text_y[1] = s->height - 1;
606 s->cursor_invalidate = 1;
607 return;
610 vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
611 color_table[0][COLOR_BLACK]);
612 y1 = s->y_displayed;
613 for(y = 0; y < s->height; y++) {
614 c = s->cells + y1 * s->width;
615 for(x = 0; x < s->width; x++) {
616 vga_putcharxy(s->ds, x, y, c->ch,
617 &(c->t_attrib));
618 c++;
620 if (++y1 == s->total_height)
621 y1 = 0;
623 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
624 console_show_cursor(s, 1);
627 static void console_scroll(int ydelta)
629 TextConsole *s;
630 int i, y1;
632 s = active_console;
633 if (!s || (s->console_type == GRAPHIC_CONSOLE))
634 return;
636 if (ydelta > 0) {
637 for(i = 0; i < ydelta; i++) {
638 if (s->y_displayed == s->y_base)
639 break;
640 if (++s->y_displayed == s->total_height)
641 s->y_displayed = 0;
643 } else {
644 ydelta = -ydelta;
645 i = s->backscroll_height;
646 if (i > s->total_height - s->height)
647 i = s->total_height - s->height;
648 y1 = s->y_base - i;
649 if (y1 < 0)
650 y1 += s->total_height;
651 for(i = 0; i < ydelta; i++) {
652 if (s->y_displayed == y1)
653 break;
654 if (--s->y_displayed < 0)
655 s->y_displayed = s->total_height - 1;
658 console_refresh(s);
661 static void console_put_lf(TextConsole *s)
663 TextCell *c;
664 int x, y1;
666 s->y++;
667 if (s->y >= s->height) {
668 s->y = s->height - 1;
670 if (s->y_displayed == s->y_base) {
671 if (++s->y_displayed == s->total_height)
672 s->y_displayed = 0;
674 if (++s->y_base == s->total_height)
675 s->y_base = 0;
676 if (s->backscroll_height < s->total_height)
677 s->backscroll_height++;
678 y1 = (s->y_base + s->height - 1) % s->total_height;
679 c = &s->cells[y1 * s->width];
680 for(x = 0; x < s->width; x++) {
681 c->ch = ' ';
682 c->t_attrib = s->t_attrib_default;
683 c++;
685 if (s == active_console && s->y_displayed == s->y_base) {
686 if (!s->ds->depth) {
687 s->text_x[0] = 0;
688 s->text_y[0] = 0;
689 s->text_x[1] = s->width - 1;
690 s->text_y[1] = s->height - 1;
691 return;
694 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
695 s->width * FONT_WIDTH,
696 (s->height - 1) * FONT_HEIGHT);
697 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
698 s->width * FONT_WIDTH, FONT_HEIGHT,
699 color_table[0][s->t_attrib_default.bgcol]);
700 dpy_update(s->ds, 0, 0,
701 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
706 /* Set console attributes depending on the current escape codes.
707 * NOTE: I know this code is not very efficient (checking every color for it
708 * self) but it is more readable and better maintainable.
710 static void console_handle_escape(TextConsole *s)
712 int i;
714 for (i=0; i<s->nb_esc_params; i++) {
715 switch (s->esc_params[i]) {
716 case 0: /* reset all console attributes to default */
717 s->t_attrib = s->t_attrib_default;
718 break;
719 case 1:
720 s->t_attrib.bold = 1;
721 break;
722 case 4:
723 s->t_attrib.uline = 1;
724 break;
725 case 5:
726 s->t_attrib.blink = 1;
727 break;
728 case 7:
729 s->t_attrib.invers = 1;
730 break;
731 case 8:
732 s->t_attrib.unvisible = 1;
733 break;
734 case 22:
735 s->t_attrib.bold = 0;
736 break;
737 case 24:
738 s->t_attrib.uline = 0;
739 break;
740 case 25:
741 s->t_attrib.blink = 0;
742 break;
743 case 27:
744 s->t_attrib.invers = 0;
745 break;
746 case 28:
747 s->t_attrib.unvisible = 0;
748 break;
749 /* set foreground color */
750 case 30:
751 s->t_attrib.fgcol=COLOR_BLACK;
752 break;
753 case 31:
754 s->t_attrib.fgcol=COLOR_RED;
755 break;
756 case 32:
757 s->t_attrib.fgcol=COLOR_GREEN;
758 break;
759 case 33:
760 s->t_attrib.fgcol=COLOR_YELLOW;
761 break;
762 case 34:
763 s->t_attrib.fgcol=COLOR_BLUE;
764 break;
765 case 35:
766 s->t_attrib.fgcol=COLOR_MAGENTA;
767 break;
768 case 36:
769 s->t_attrib.fgcol=COLOR_CYAN;
770 break;
771 case 37:
772 s->t_attrib.fgcol=COLOR_WHITE;
773 break;
774 /* set background color */
775 case 40:
776 s->t_attrib.bgcol=COLOR_BLACK;
777 break;
778 case 41:
779 s->t_attrib.bgcol=COLOR_RED;
780 break;
781 case 42:
782 s->t_attrib.bgcol=COLOR_GREEN;
783 break;
784 case 43:
785 s->t_attrib.bgcol=COLOR_YELLOW;
786 break;
787 case 44:
788 s->t_attrib.bgcol=COLOR_BLUE;
789 break;
790 case 45:
791 s->t_attrib.bgcol=COLOR_MAGENTA;
792 break;
793 case 46:
794 s->t_attrib.bgcol=COLOR_CYAN;
795 break;
796 case 47:
797 s->t_attrib.bgcol=COLOR_WHITE;
798 break;
803 static void console_clear_xy(TextConsole *s, int x, int y)
805 int y1 = (s->y_base + y) % s->total_height;
806 TextCell *c = &s->cells[y1 * s->width + x];
807 c->ch = ' ';
808 c->t_attrib = s->t_attrib_default;
809 c++;
810 update_xy(s, x, y);
813 static void console_putchar(TextConsole *s, int ch)
815 TextCell *c;
816 int y1, i;
817 int x, y;
819 switch(s->state) {
820 case TTY_STATE_NORM:
821 switch(ch) {
822 case '\r': /* carriage return */
823 s->x = 0;
824 break;
825 case '\n': /* newline */
826 console_put_lf(s);
827 break;
828 case '\b': /* backspace */
829 if (s->x > 0)
830 s->x--;
831 break;
832 case '\t': /* tabspace */
833 if (s->x + (8 - (s->x % 8)) > s->width) {
834 s->x = 0;
835 console_put_lf(s);
836 } else {
837 s->x = s->x + (8 - (s->x % 8));
839 break;
840 case '\a': /* alert aka. bell */
841 /* TODO: has to be implemented */
842 break;
843 case 14:
844 /* SI (shift in), character set 0 (ignored) */
845 break;
846 case 15:
847 /* SO (shift out), character set 1 (ignored) */
848 break;
849 case 27: /* esc (introducing an escape sequence) */
850 s->state = TTY_STATE_ESC;
851 break;
852 default:
853 if (s->x >= s->width) {
854 /* line wrap */
855 s->x = 0;
856 console_put_lf(s);
858 y1 = (s->y_base + s->y) % s->total_height;
859 c = &s->cells[y1 * s->width + s->x];
860 c->ch = ch;
861 c->t_attrib = s->t_attrib;
862 update_xy(s, s->x, s->y);
863 s->x++;
864 break;
866 break;
867 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
868 if (ch == '[') {
869 for(i=0;i<MAX_ESC_PARAMS;i++)
870 s->esc_params[i] = 0;
871 s->nb_esc_params = 0;
872 s->state = TTY_STATE_CSI;
873 } else {
874 s->state = TTY_STATE_NORM;
876 break;
877 case TTY_STATE_CSI: /* handle escape sequence parameters */
878 if (ch >= '0' && ch <= '9') {
879 if (s->nb_esc_params < MAX_ESC_PARAMS) {
880 s->esc_params[s->nb_esc_params] =
881 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
883 } else {
884 s->nb_esc_params++;
885 if (ch == ';')
886 break;
887 #ifdef DEBUG_CONSOLE
888 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
889 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
890 #endif
891 s->state = TTY_STATE_NORM;
892 switch(ch) {
893 case 'A':
894 /* move cursor up */
895 if (s->esc_params[0] == 0) {
896 s->esc_params[0] = 1;
898 s->y -= s->esc_params[0];
899 if (s->y < 0) {
900 s->y = 0;
902 break;
903 case 'B':
904 /* move cursor down */
905 if (s->esc_params[0] == 0) {
906 s->esc_params[0] = 1;
908 s->y += s->esc_params[0];
909 if (s->y >= s->height) {
910 s->y = s->height - 1;
912 break;
913 case 'C':
914 /* move cursor right */
915 if (s->esc_params[0] == 0) {
916 s->esc_params[0] = 1;
918 s->x += s->esc_params[0];
919 if (s->x >= s->width) {
920 s->x = s->width - 1;
922 break;
923 case 'D':
924 /* move cursor left */
925 if (s->esc_params[0] == 0) {
926 s->esc_params[0] = 1;
928 s->x -= s->esc_params[0];
929 if (s->x < 0) {
930 s->x = 0;
932 break;
933 case 'G':
934 /* move cursor to column */
935 s->x = s->esc_params[0] - 1;
936 if (s->x < 0) {
937 s->x = 0;
939 break;
940 case 'f':
941 case 'H':
942 /* move cursor to row, column */
943 s->x = s->esc_params[1] - 1;
944 if (s->x < 0) {
945 s->x = 0;
947 s->y = s->esc_params[0] - 1;
948 if (s->y < 0) {
949 s->y = 0;
951 break;
952 case 'J':
953 switch (s->esc_params[0]) {
954 case 0:
955 /* clear to end of screen */
956 for (y = s->y; y < s->height; y++) {
957 for (x = 0; x < s->width; x++) {
958 if (y == s->y && x < s->x) {
959 continue;
961 console_clear_xy(s, x, y);
964 break;
965 case 1:
966 /* clear from beginning of screen */
967 for (y = 0; y <= s->y; y++) {
968 for (x = 0; x < s->width; x++) {
969 if (y == s->y && x > s->x) {
970 break;
972 console_clear_xy(s, x, y);
975 break;
976 case 2:
977 /* clear entire screen */
978 for (y = 0; y <= s->height; y++) {
979 for (x = 0; x < s->width; x++) {
980 console_clear_xy(s, x, y);
983 break;
985 case 'K':
986 switch (s->esc_params[0]) {
987 case 0:
988 /* clear to eol */
989 for(x = s->x; x < s->width; x++) {
990 console_clear_xy(s, x, s->y);
992 break;
993 case 1:
994 /* clear from beginning of line */
995 for (x = 0; x <= s->x; x++) {
996 console_clear_xy(s, x, s->y);
998 break;
999 case 2:
1000 /* clear entire line */
1001 for(x = 0; x < s->width; x++) {
1002 console_clear_xy(s, x, s->y);
1004 break;
1006 break;
1007 case 'm':
1008 console_handle_escape(s);
1009 break;
1010 case 'n':
1011 /* report cursor position */
1012 /* TODO: send ESC[row;colR */
1013 break;
1014 case 's':
1015 /* save cursor position */
1016 s->x_saved = s->x;
1017 s->y_saved = s->y;
1018 break;
1019 case 'u':
1020 /* restore cursor position */
1021 s->x = s->x_saved;
1022 s->y = s->y_saved;
1023 break;
1024 default:
1025 #ifdef DEBUG_CONSOLE
1026 fprintf(stderr, "unhandled escape character '%c'\n", ch);
1027 #endif
1028 break;
1030 break;
1035 void console_select(unsigned int index)
1037 TextConsole *s;
1039 if (index >= MAX_CONSOLES)
1040 return;
1041 s = consoles[index];
1042 if (s) {
1043 active_console = s;
1044 vga_hw_invalidate();
1048 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1050 TextConsole *s = chr->opaque;
1051 int i;
1053 console_show_cursor(s, 0);
1054 for(i = 0; i < len; i++) {
1055 console_putchar(s, buf[i]);
1057 console_show_cursor(s, 1);
1058 return len;
1061 static void console_send_event(CharDriverState *chr, int event)
1063 TextConsole *s = chr->opaque;
1064 int i;
1066 if (event == CHR_EVENT_FOCUS) {
1067 for(i = 0; i < nb_consoles; i++) {
1068 if (consoles[i] == s) {
1069 console_select(i);
1070 break;
1076 static void kbd_send_chars(void *opaque)
1078 TextConsole *s = opaque;
1079 int len;
1080 uint8_t buf[16];
1082 len = qemu_chr_can_read(s->chr);
1083 if (len > s->out_fifo.count)
1084 len = s->out_fifo.count;
1085 if (len > 0) {
1086 if (len > sizeof(buf))
1087 len = sizeof(buf);
1088 qemu_fifo_read(&s->out_fifo, buf, len);
1089 qemu_chr_read(s->chr, buf, len);
1091 /* characters are pending: we send them a bit later (XXX:
1092 horrible, should change char device API) */
1093 if (s->out_fifo.count > 0) {
1094 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1098 /* called when an ascii key is pressed */
1099 void kbd_put_keysym(int keysym)
1101 TextConsole *s;
1102 uint8_t buf[16], *q;
1103 int c;
1105 s = active_console;
1106 if (!s || (s->console_type == GRAPHIC_CONSOLE))
1107 return;
1109 switch(keysym) {
1110 case QEMU_KEY_CTRL_UP:
1111 console_scroll(-1);
1112 break;
1113 case QEMU_KEY_CTRL_DOWN:
1114 console_scroll(1);
1115 break;
1116 case QEMU_KEY_CTRL_PAGEUP:
1117 console_scroll(-10);
1118 break;
1119 case QEMU_KEY_CTRL_PAGEDOWN:
1120 console_scroll(10);
1121 break;
1122 default:
1123 /* convert the QEMU keysym to VT100 key string */
1124 q = buf;
1125 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1126 *q++ = '\033';
1127 *q++ = '[';
1128 c = keysym - 0xe100;
1129 if (c >= 10)
1130 *q++ = '0' + (c / 10);
1131 *q++ = '0' + (c % 10);
1132 *q++ = '~';
1133 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1134 *q++ = '\033';
1135 *q++ = '[';
1136 *q++ = keysym & 0xff;
1137 } else {
1138 *q++ = keysym;
1140 if (s->chr->chr_read) {
1141 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1142 kbd_send_chars(s);
1144 break;
1148 static void text_console_invalidate(void *opaque)
1150 TextConsole *s = (TextConsole *) opaque;
1152 if (s->console_type != GRAPHIC_CONSOLE) {
1153 if (s->g_width != s->ds->width ||
1154 s->g_height != s->ds->height) {
1155 if (s->console_type == TEXT_CONSOLE_FIXED_SIZE)
1156 dpy_resize(s->ds, s->g_width, s->g_height);
1157 else {
1158 s->g_width = s->ds->width;
1159 s->g_height = s->ds->height;
1160 text_console_resize(s);
1164 console_refresh(s);
1167 static void text_console_update(void *opaque, console_ch_t *chardata)
1169 TextConsole *s = (TextConsole *) opaque;
1170 int i, j, src;
1172 if (s->text_x[0] <= s->text_x[1]) {
1173 src = (s->y_base + s->text_y[0]) * s->width;
1174 chardata += s->text_y[0] * s->width;
1175 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1176 for (j = 0; j < s->width; j ++, src ++)
1177 console_write_ch(chardata ++, s->cells[src].ch |
1178 (s->cells[src].t_attrib.fgcol << 12) |
1179 (s->cells[src].t_attrib.bgcol << 8) |
1180 (s->cells[src].t_attrib.bold << 21));
1181 dpy_update(s->ds, s->text_x[0], s->text_y[0],
1182 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1183 s->text_x[0] = s->width;
1184 s->text_y[0] = s->height;
1185 s->text_x[1] = 0;
1186 s->text_y[1] = 0;
1188 if (s->cursor_invalidate) {
1189 dpy_cursor(s->ds, s->x, s->y);
1190 s->cursor_invalidate = 0;
1194 static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1196 TextConsole *s;
1197 int i;
1199 if (nb_consoles >= MAX_CONSOLES)
1200 return NULL;
1201 s = qemu_mallocz(sizeof(TextConsole));
1202 if (!s) {
1203 return NULL;
1205 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1206 (console_type == GRAPHIC_CONSOLE))) {
1207 active_console = s;
1209 s->ds = ds;
1210 s->console_type = console_type;
1211 if (console_type != GRAPHIC_CONSOLE) {
1212 consoles[nb_consoles++] = s;
1213 } else {
1214 /* HACK: Put graphical consoles before text consoles. */
1215 for (i = nb_consoles; i > 0; i--) {
1216 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1217 break;
1218 consoles[i] = consoles[i - 1];
1220 consoles[i] = s;
1222 return s;
1225 TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1226 vga_hw_invalidate_ptr invalidate,
1227 vga_hw_screen_dump_ptr screen_dump,
1228 vga_hw_text_update_ptr text_update,
1229 void *opaque)
1231 TextConsole *s;
1233 s = new_console(ds, GRAPHIC_CONSOLE);
1234 if (!s)
1235 return NULL;
1236 s->hw_update = update;
1237 s->hw_invalidate = invalidate;
1238 s->hw_screen_dump = screen_dump;
1239 s->hw_text_update = text_update;
1240 s->hw = opaque;
1241 return s;
1244 int is_graphic_console(void)
1246 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1249 void console_color_init(DisplayState *ds)
1251 int i, j;
1252 for (j = 0; j < 2; j++) {
1253 for (i = 0; i < 8; i++) {
1254 color_table[j][i] = col_expand(ds,
1255 vga_get_color(ds, color_table_rgb[j][i]));
1260 CharDriverState *text_console_init(DisplayState *ds, const char *p)
1262 CharDriverState *chr;
1263 TextConsole *s;
1264 unsigned width;
1265 unsigned height;
1266 static int color_inited;
1268 chr = qemu_mallocz(sizeof(CharDriverState));
1269 if (!chr)
1270 return NULL;
1271 s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
1272 if (!s) {
1273 free(chr);
1274 return NULL;
1276 chr->opaque = s;
1277 chr->chr_write = console_puts;
1278 chr->chr_send_event = console_send_event;
1280 s->chr = chr;
1281 s->out_fifo.buf = s->out_fifo_buf;
1282 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1283 s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1285 if (!color_inited) {
1286 color_inited = 1;
1287 console_color_init(s->ds);
1289 s->y_displayed = 0;
1290 s->y_base = 0;
1291 s->total_height = DEFAULT_BACKSCROLL;
1292 s->x = 0;
1293 s->y = 0;
1294 width = s->ds->width;
1295 height = s->ds->height;
1296 if (p != 0) {
1297 width = strtoul(p, (char **)&p, 10);
1298 if (*p == 'C') {
1299 p++;
1300 width *= FONT_WIDTH;
1302 if (*p == 'x') {
1303 p++;
1304 height = strtoul(p, (char **)&p, 10);
1305 if (*p == 'C') {
1306 p++;
1307 height *= FONT_HEIGHT;
1311 s->g_width = width;
1312 s->g_height = height;
1314 s->hw_invalidate = text_console_invalidate;
1315 s->hw_text_update = text_console_update;
1316 s->hw = s;
1318 /* Set text attribute defaults */
1319 s->t_attrib_default.bold = 0;
1320 s->t_attrib_default.uline = 0;
1321 s->t_attrib_default.blink = 0;
1322 s->t_attrib_default.invers = 0;
1323 s->t_attrib_default.unvisible = 0;
1324 s->t_attrib_default.fgcol = COLOR_WHITE;
1325 s->t_attrib_default.bgcol = COLOR_BLACK;
1327 /* set current text attributes to default */
1328 s->t_attrib = s->t_attrib_default;
1329 text_console_resize(s);
1331 qemu_chr_reset(chr);
1333 return chr;