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