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