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