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