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