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