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