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