Improved terminal emulation (Piotr Esden-Tempski).
[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 RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
31 #define RGB(r, g, b) 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
57 struct TextConsole {
58     int text_console; /* true if text console */
59     DisplayState *ds;
60     int g_width, g_height;
61     int width;
62     int height;
63     int total_height;
64     int backscroll_height;
65     int x, y;
66     int y_displayed;
67     int y_base;
68     TextAttributes t_attrib_default; /* default text attributes */
69     TextAttributes t_attrib; /* currently active text attributes */
70     TextCell *cells;
71
72     enum TTYState state;
73     int esc_params[MAX_ESC_PARAMS];
74     int nb_esc_params;
75
76     /* kbd read handler */
77     IOReadHandler *fd_read;
78     void *fd_opaque;
79 };
80
81 static TextConsole *active_console;
82 static TextConsole *consoles[MAX_CONSOLES];
83 static int nb_consoles = 0;
84
85 /* convert a RGBA color to a color index usable in graphic primitives */
86 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
87 {
88     unsigned int r, g, b, color;
89
90     switch(ds->depth) {
91 #if 0
92     case 8:
93         r = (rgba >> 16) & 0xff;
94         g = (rgba >> 8) & 0xff;
95         b = (rgba) & 0xff;
96         color = (rgb_to_index[r] * 6 * 6) + 
97             (rgb_to_index[g] * 6) + 
98             (rgb_to_index[b]);
99         break;
100 #endif
101     case 15:
102         r = (rgba >> 16) & 0xff;
103         g = (rgba >> 8) & 0xff;
104         b = (rgba) & 0xff;
105         color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
106         break;
107     case 16:
108         r = (rgba >> 16) & 0xff;
109         g = (rgba >> 8) & 0xff;
110         b = (rgba) & 0xff;
111         color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
112         break;
113     case 32:
114     default:
115         color = rgba;
116         break;
117     }
118     return color;
119 }
120
121 static void vga_fill_rect (DisplayState *ds, 
122                            int posx, int posy, int width, int height, uint32_t color)
123 {
124     uint8_t *d, *d1;
125     int x, y, bpp;
126     
127     bpp = (ds->depth + 7) >> 3;
128     d1 = ds->data + 
129         ds->linesize * posy + bpp * posx;
130     for (y = 0; y < height; y++) {
131         d = d1;
132         switch(bpp) {
133         case 1:
134             for (x = 0; x < width; x++) {
135                 *((uint8_t *)d) = color;
136                 d++;
137             }
138             break;
139         case 2:
140             for (x = 0; x < width; x++) {
141                 *((uint16_t *)d) = color;
142                 d += 2;
143             }
144             break;
145         case 4:
146             for (x = 0; x < width; x++) {
147                 *((uint32_t *)d) = color;
148                 d += 4;
149             }
150             break;
151         }
152         d1 += ds->linesize;
153     }
154 }
155
156 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
157 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
158 {
159     const uint8_t *s;
160     uint8_t *d;
161     int wb, y, bpp;
162
163     bpp = (ds->depth + 7) >> 3;
164     wb = w * bpp;
165     if (yd <= ys) {
166         s = ds->data + 
167             ds->linesize * ys + bpp * xs;
168         d = ds->data + 
169             ds->linesize * yd + bpp * xd;
170         for (y = 0; y < h; y++) {
171             memmove(d, s, wb);
172             d += ds->linesize;
173             s += ds->linesize;
174         }
175     } else {
176         s = ds->data + 
177             ds->linesize * (ys + h - 1) + bpp * xs;
178         d = ds->data + 
179             ds->linesize * (yd + h - 1) + bpp * xd;
180        for (y = 0; y < h; y++) {
181             memmove(d, s, wb);
182             d -= ds->linesize;
183             s -= ds->linesize;
184         }
185     }
186 }
187
188 /***********************************************************/
189 /* basic char display */
190
191 #define FONT_HEIGHT 16
192 #define FONT_WIDTH 8
193
194 #include "vgafont.h"
195
196 #define cbswap_32(__x) \
197 ((uint32_t)( \
198                 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
199                 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
200                 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
201                 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
202
203 #ifdef WORDS_BIGENDIAN
204 #define PAT(x) x
205 #else
206 #define PAT(x) cbswap_32(x)
207 #endif
208
209 static const uint32_t dmask16[16] = {
210     PAT(0x00000000),
211     PAT(0x000000ff),
212     PAT(0x0000ff00),
213     PAT(0x0000ffff),
214     PAT(0x00ff0000),
215     PAT(0x00ff00ff),
216     PAT(0x00ffff00),
217     PAT(0x00ffffff),
218     PAT(0xff000000),
219     PAT(0xff0000ff),
220     PAT(0xff00ff00),
221     PAT(0xff00ffff),
222     PAT(0xffff0000),
223     PAT(0xffff00ff),
224     PAT(0xffffff00),
225     PAT(0xffffffff),
226 };
227
228 static const uint32_t dmask4[4] = {
229     PAT(0x00000000),
230     PAT(0x0000ffff),
231     PAT(0xffff0000),
232     PAT(0xffffffff),
233 };
234
235 static uint32_t color_table[2][8];
236
237 enum color_names {
238     COLOR_BLACK   = 0,
239     COLOR_RED     = 1,
240     COLOR_GREEN   = 2,
241     COLOR_YELLOW  = 3,
242     COLOR_BLUE    = 4,
243     COLOR_MAGENTA = 5,
244     COLOR_CYAN    = 6,
245     COLOR_WHITE   = 7
246 };
247
248 static const uint32_t color_table_rgb[2][8] = {
249     {   /* dark */
250         RGB(0x00, 0x00, 0x00),  /* black */
251         RGB(0xaa, 0x00, 0x00),  /* red */
252         RGB(0x00, 0xaa, 0x00),  /* green */
253         RGB(0xaa, 0xaa, 0x00),  /* yellow */
254         RGB(0x00, 0x00, 0xaa),  /* blue */
255         RGB(0xaa, 0x00, 0xaa),  /* magenta */
256         RGB(0x00, 0xaa, 0xaa),  /* cyan */
257         RGB(0xaa, 0xaa, 0xaa),  /* white */
258     },
259     {   /* bright */
260         RGB(0x00, 0x00, 0x00),  /* black */
261         RGB(0xff, 0x00, 0x00),  /* red */
262         RGB(0x00, 0xff, 0x00),  /* green */
263         RGB(0xff, 0xff, 0x00),  /* yellow */
264         RGB(0x00, 0x00, 0xff),  /* blue */
265         RGB(0xff, 0x00, 0xff),  /* magenta */
266         RGB(0x00, 0xff, 0xff),  /* cyan */
267         RGB(0xff, 0xff, 0xff),  /* white */
268     }
269 };
270
271 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
272 {
273     switch(ds->depth) {
274     case 8:
275         col |= col << 8;
276         col |= col << 16;
277         break;
278     case 15:
279     case 16:
280         col |= col << 16;
281         break;
282     default:
283         break;
284     }
285
286     return col;
287 }
288 #ifdef DEBUG_CONSOLE
289 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
290 {
291     if (t_attrib->bold) {
292         printf("b");
293     } else {
294         printf(" ");
295     }
296     if (t_attrib->uline) {
297         printf("u");
298     } else {
299         printf(" ");
300     }
301     if (t_attrib->blink) {
302         printf("l");
303     } else {
304         printf(" ");
305     }
306     if (t_attrib->invers) {
307         printf("i");
308     } else {
309         printf(" ");
310     }
311     if (t_attrib->unvisible) {
312         printf("n");
313     } else {
314         printf(" ");
315     }
316
317     printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
318 }
319 #endif
320
321 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch, 
322                           TextAttributes *t_attrib)
323 {
324     uint8_t *d;
325     const uint8_t *font_ptr;
326     unsigned int font_data, linesize, xorcol, bpp;
327     int i;
328     unsigned int fgcol, bgcol;
329
330 #ifdef DEBUG_CONSOLE
331     printf("x: %2i y: %2i", x, y);
332     console_print_text_attributes(t_attrib, ch);
333 #endif
334
335     if (t_attrib->invers) {
336         bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
337         fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
338     } else {
339         fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
340         bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
341     }
342
343     bpp = (ds->depth + 7) >> 3;
344     d = ds->data + 
345         ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
346     linesize = ds->linesize;
347     font_ptr = vgafont16 + FONT_HEIGHT * ch;
348     xorcol = bgcol ^ fgcol;
349     switch(ds->depth) {
350     case 8:
351         for(i = 0; i < FONT_HEIGHT; i++) {
352             font_data = *font_ptr++;
353             if (t_attrib->uline
354                 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
355                 font_data = 0xFFFF;
356             }
357             ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
358             ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
359             d += linesize;
360         }
361         break;
362     case 16:
363     case 15:
364         for(i = 0; i < FONT_HEIGHT; i++) {
365             font_data = *font_ptr++;
366             if (t_attrib->uline
367                 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
368                 font_data = 0xFFFF;
369             }
370             ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
371             ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
372             ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
373             ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
374             d += linesize;
375         }
376         break;
377     case 32:
378         for(i = 0; i < FONT_HEIGHT; i++) {
379             font_data = *font_ptr++;
380             if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
381                 font_data = 0xFFFF;
382             }
383             ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
384             ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
385             ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
386             ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
387             ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
388             ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
389             ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
390             ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
391             d += linesize;
392         }
393         break;
394     }
395 }
396
397 static void text_console_resize(TextConsole *s)
398 {
399     TextCell *cells, *c, *c1;
400     int w1, x, y, last_width;
401
402     last_width = s->width;
403     s->width = s->g_width / FONT_WIDTH;
404     s->height = s->g_height / FONT_HEIGHT;
405
406     w1 = last_width;
407     if (s->width < w1)
408         w1 = s->width;
409
410     cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
411     for(y = 0; y < s->total_height; y++) {
412         c = &cells[y * s->width];
413         if (w1 > 0) {
414             c1 = &s->cells[y * last_width];
415             for(x = 0; x < w1; x++) {
416                 *c++ = *c1++;
417             }
418         }
419         for(x = w1; x < s->width; x++) {
420             c->ch = ' ';
421             c->t_attrib = s->t_attrib_default;
422             c++;
423         }
424     }
425     free(s->cells);
426     s->cells = cells;
427 }
428
429 static void update_xy(TextConsole *s, int x, int y)
430 {
431     TextCell *c;
432     int y1, y2;
433
434     if (s == active_console) {
435         y1 = (s->y_base + y) % s->total_height;
436         y2 = y1 - s->y_displayed;
437         if (y2 < 0)
438             y2 += s->total_height;
439         if (y2 < s->height) {
440             c = &s->cells[y1 * s->width + x];
441             vga_putcharxy(s->ds, x, y2, c->ch, 
442                           &(c->t_attrib));
443             dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT, 
444                        FONT_WIDTH, FONT_HEIGHT);
445         }
446     }
447 }
448
449 static void console_show_cursor(TextConsole *s, int show)
450 {
451     TextCell *c;
452     int y, y1;
453
454     if (s == active_console) {
455         y1 = (s->y_base + s->y) % s->total_height;
456         y = y1 - s->y_displayed;
457         if (y < 0)
458             y += s->total_height;
459         if (y < s->height) {
460             c = &s->cells[y1 * s->width + s->x];
461             if (show) {
462                 TextAttributes t_attrib = s->t_attrib_default;
463                 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
464                 vga_putcharxy(s->ds, s->x, y, c->ch, &t_attrib);
465             } else {
466                 vga_putcharxy(s->ds, s->x, y, c->ch, 
467                               &(c->t_attrib));
468             }
469             dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT, 
470                        FONT_WIDTH, FONT_HEIGHT);
471         }
472     }
473 }
474
475 static void console_refresh(TextConsole *s)
476 {
477     TextCell *c;
478     int x, y, y1;
479
480     if (s != active_console) 
481         return;
482
483     vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
484                   color_table[0][COLOR_BLACK]);
485     y1 = s->y_displayed;
486     for(y = 0; y < s->height; y++) {
487         c = s->cells + y1 * s->width;
488         for(x = 0; x < s->width; x++) {
489             vga_putcharxy(s->ds, x, y, c->ch, 
490                           &(c->t_attrib));
491             c++;
492         }
493         if (++y1 == s->total_height)
494             y1 = 0;
495     }
496     dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
497     console_show_cursor(s, 1);
498 }
499
500 static void console_scroll(int ydelta)
501 {
502     TextConsole *s;
503     int i, y1;
504     
505     s = active_console;
506     if (!s || !s->text_console)
507         return;
508
509     if (ydelta > 0) {
510         for(i = 0; i < ydelta; i++) {
511             if (s->y_displayed == s->y_base)
512                 break;
513             if (++s->y_displayed == s->total_height)
514                 s->y_displayed = 0;
515         }
516     } else {
517         ydelta = -ydelta;
518         i = s->backscroll_height;
519         if (i > s->total_height - s->height)
520             i = s->total_height - s->height;
521         y1 = s->y_base - i;
522         if (y1 < 0)
523             y1 += s->total_height;
524         for(i = 0; i < ydelta; i++) {
525             if (s->y_displayed == y1)
526                 break;
527             if (--s->y_displayed < 0)
528                 s->y_displayed = s->total_height - 1;
529         }
530     }
531     console_refresh(s);
532 }
533
534 static void console_put_lf(TextConsole *s)
535 {
536     TextCell *c;
537     int x, y1;
538
539     s->x = 0;
540     s->y++;
541     if (s->y >= s->height) {
542         s->y = s->height - 1;
543
544         if (s->y_displayed == s->y_base) {
545             if (++s->y_displayed == s->total_height)
546                 s->y_displayed = 0;
547         }
548         if (++s->y_base == s->total_height)
549             s->y_base = 0;
550         if (s->backscroll_height < s->total_height)
551             s->backscroll_height++;
552         y1 = (s->y_base + s->height - 1) % s->total_height;
553         c = &s->cells[y1 * s->width];
554         for(x = 0; x < s->width; x++) {
555             c->ch = ' ';
556             c->t_attrib = s->t_attrib_default;
557             c++;
558         }
559         if (s == active_console && s->y_displayed == s->y_base) {
560             vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0, 
561                        s->width * FONT_WIDTH, 
562                        (s->height - 1) * FONT_HEIGHT);
563             vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
564                           s->width * FONT_WIDTH, FONT_HEIGHT, 
565                           color_table[0][s->t_attrib_default.bgcol]);
566             dpy_update(s->ds, 0, 0, 
567                        s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
568         }
569     }
570 }
571
572 /* Set console attributes depending on the current escape codes.
573  * NOTE: I know this code is not very efficient (checking every color for it
574  * self) but it is more readable and better maintainable.
575  */
576 static void console_handle_escape(TextConsole *s)
577 {
578     int i;
579
580     if (s->nb_esc_params == 0) { /* ESC[m sets all attributes to default */
581         s->t_attrib = s->t_attrib_default;
582         return;
583     }
584     for (i=0; i<s->nb_esc_params; i++) {
585         switch (s->esc_params[i]) {
586             case 0: /* reset all console attributes to default */
587                 s->t_attrib = s->t_attrib_default;
588                 break;
589             case 1:
590                 s->t_attrib.bold = 1;
591                 break;
592             case 4:
593                 s->t_attrib.uline = 1;
594                 break;
595             case 5:
596                 s->t_attrib.blink = 1;
597                 break;
598             case 7:
599                 s->t_attrib.invers = 1;
600                 break;
601             case 8:
602                 s->t_attrib.unvisible = 1;
603                 break;
604             case 22:
605                 s->t_attrib.bold = 0;
606                 break;
607             case 24:
608                 s->t_attrib.uline = 0;
609                 break;
610             case 25:
611                 s->t_attrib.blink = 0;
612                 break;
613             case 27:
614                 s->t_attrib.invers = 0;
615                 break;
616             case 28:
617                 s->t_attrib.unvisible = 0;
618                 break;
619             /* set foreground color */
620             case 30:
621                 s->t_attrib.fgcol=COLOR_BLACK;
622                 break;
623             case 31:
624                 s->t_attrib.fgcol=COLOR_RED;
625                 break;
626             case 32:
627                 s->t_attrib.fgcol=COLOR_GREEN;
628                 break;
629             case 33:
630                 s->t_attrib.fgcol=COLOR_YELLOW;
631                 break;
632             case 34:
633                 s->t_attrib.fgcol=COLOR_BLUE;
634                 break;
635             case 35:
636                 s->t_attrib.fgcol=COLOR_MAGENTA;
637                 break;
638             case 36:
639                 s->t_attrib.fgcol=COLOR_CYAN;
640                 break;
641             case 37:
642                 s->t_attrib.fgcol=COLOR_WHITE;
643                 break;
644             /* set background color */
645             case 40:
646                 s->t_attrib.bgcol=COLOR_BLACK;
647                 break;
648             case 41:
649                 s->t_attrib.bgcol=COLOR_RED;
650                 break;
651             case 42:
652                 s->t_attrib.bgcol=COLOR_GREEN;
653                 break;
654             case 43:
655                 s->t_attrib.bgcol=COLOR_YELLOW;
656                 break;
657             case 44:
658                 s->t_attrib.bgcol=COLOR_BLUE;
659                 break;
660             case 45:
661                 s->t_attrib.bgcol=COLOR_MAGENTA;
662                 break;
663             case 46:
664                 s->t_attrib.bgcol=COLOR_CYAN;
665                 break;
666             case 47:
667                 s->t_attrib.bgcol=COLOR_WHITE;
668                 break;
669         }
670     }
671 }
672
673 static void console_putchar(TextConsole *s, int ch)
674 {
675     TextCell *c;
676     int y1, i, x;
677
678     switch(s->state) {
679     case TTY_STATE_NORM:
680         switch(ch) {
681         case '\r':  /* carriage return */
682             s->x = 0;
683             break;
684         case '\n':  /* newline */
685             console_put_lf(s);
686             break;
687         case '\b':  /* backspace */
688             if(s->x > 0) s->x--;
689             y1 = (s->y_base + s->y) % s->total_height;
690             c = &s->cells[y1 * s->width + s->x];
691             c->ch = ' ';
692             c->t_attrib = s->t_attrib;
693             update_xy(s, s->x, s->y);
694             break;
695         case '\t':  /* tabspace */
696             if (s->x + (8 - (s->x % 8)) > s->width) {
697                 console_put_lf(s);
698             } else {
699                 s->x = s->x + (8 - (s->x % 8));
700             }
701             break;
702         case '\a':  /* alert aka. bell */
703             /* TODO: has to be implemented */
704             break;
705         case 27:    /* esc (introducing an escape sequence) */
706             s->state = TTY_STATE_ESC;
707             break;
708         default:
709             y1 = (s->y_base + s->y) % s->total_height;
710             c = &s->cells[y1 * s->width + s->x];
711             c->ch = ch;
712             c->t_attrib = s->t_attrib;
713             update_xy(s, s->x, s->y);
714             s->x++;
715             if (s->x >= s->width)
716                 console_put_lf(s);
717             break;
718         }
719         break;
720     case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
721         if (ch == '[') {
722             for(i=0;i<MAX_ESC_PARAMS;i++)
723                 s->esc_params[i] = 0;
724             s->nb_esc_params = 0;
725             s->state = TTY_STATE_CSI;
726         } else {
727             s->state = TTY_STATE_NORM;
728         }
729         break;
730     case TTY_STATE_CSI: /* handle escape sequence parameters */
731         if (ch >= '0' && ch <= '9') {
732             if (s->nb_esc_params < MAX_ESC_PARAMS) {
733                 s->esc_params[s->nb_esc_params] = 
734                     s->esc_params[s->nb_esc_params] * 10 + ch - '0';
735             }
736         } else {
737             s->nb_esc_params++;
738             if (ch == ';')
739                 break;
740             s->state = TTY_STATE_NORM;
741             switch(ch) {
742             case 'D':
743                 if (s->x > 0)
744                     s->x--;
745                 break;
746             case 'C':
747                 if (s->x < (s->width - 1))
748                     s->x++;
749                 break;
750             case 'K':
751                 /* clear to eol */
752                 y1 = (s->y_base + s->y) % s->total_height;
753                 for(x = s->x; x < s->width; x++) {
754                     c = &s->cells[y1 * s->width + x];
755                     c->ch = ' ';
756                     c->t_attrib = s->t_attrib_default;
757                     c++;
758                     update_xy(s, x, s->y);
759                 }
760                 break;
761             default:
762                 break;
763             }
764             console_handle_escape(s);
765             break;
766         }
767     }
768 }
769
770 void console_select(unsigned int index)
771 {
772     TextConsole *s;
773
774     if (index >= MAX_CONSOLES)
775         return;
776     s = consoles[index];
777     if (s) {
778         active_console = s;
779         if (s->text_console) {
780             if (s->g_width != s->ds->width ||
781                 s->g_height != s->ds->height) {
782                 s->g_width = s->ds->width;
783                 s->g_height = s->ds->height;
784                 text_console_resize(s);
785         }
786             console_refresh(s);
787         }
788     }
789 }
790
791 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
792 {
793     TextConsole *s = chr->opaque;
794     int i;
795
796     console_show_cursor(s, 0);
797     for(i = 0; i < len; i++) {
798         console_putchar(s, buf[i]);
799     }
800     console_show_cursor(s, 1);
801     return len;
802 }
803
804 static void console_chr_add_read_handler(CharDriverState *chr, 
805                                          IOCanRWHandler *fd_can_read, 
806                                          IOReadHandler *fd_read, void *opaque)
807 {
808     TextConsole *s = chr->opaque;
809     s->fd_read = fd_read;
810     s->fd_opaque = opaque;
811 }
812
813 static void console_send_event(CharDriverState *chr, int event)
814 {
815     TextConsole *s = chr->opaque;
816     int i;
817
818     if (event == CHR_EVENT_FOCUS) {
819         for(i = 0; i < nb_consoles; i++) {
820             if (consoles[i] == s) {
821                 console_select(i);
822                 break;
823             }
824         }
825     }
826 }
827
828 /* called when an ascii key is pressed */
829 void kbd_put_keysym(int keysym)
830 {
831     TextConsole *s;
832     uint8_t buf[16], *q;
833     int c;
834
835     s = active_console;
836     if (!s || !s->text_console)
837         return;
838
839     switch(keysym) {
840     case QEMU_KEY_CTRL_UP:
841         console_scroll(-1);
842         break;
843     case QEMU_KEY_CTRL_DOWN:
844         console_scroll(1);
845         break;
846     case QEMU_KEY_CTRL_PAGEUP:
847         console_scroll(-10);
848         break;
849     case QEMU_KEY_CTRL_PAGEDOWN:
850         console_scroll(10);
851         break;
852     default:
853         if (s->fd_read) {
854             /* convert the QEMU keysym to VT100 key string */
855             q = buf;
856             if (keysym >= 0xe100 && keysym <= 0xe11f) {
857                 *q++ = '\033';
858                 *q++ = '[';
859                 c = keysym - 0xe100;
860                 if (c >= 10)
861                     *q++ = '0' + (c / 10);
862                 *q++ = '0' + (c % 10);
863                 *q++ = '~';
864             } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
865                 *q++ = '\033';
866                 *q++ = '[';
867                 *q++ = keysym & 0xff;
868             } else {
869                 *q++ = keysym;
870             }
871             s->fd_read(s->fd_opaque, buf, q - buf);
872         }
873         break;
874     }
875 }
876
877 TextConsole *graphic_console_init(DisplayState *ds)
878 {
879     TextConsole *s;
880
881     if (nb_consoles >= MAX_CONSOLES)
882         return NULL;
883     s = qemu_mallocz(sizeof(TextConsole));
884     if (!s) {
885         return NULL;
886     }
887     if (!active_console)
888         active_console = s;
889     s->ds = ds;
890     consoles[nb_consoles++] = s;
891     return s;
892 }
893
894 int is_active_console(TextConsole *s)
895 {
896     return s == active_console;
897 }
898
899 CharDriverState *text_console_init(DisplayState *ds)
900 {
901     CharDriverState *chr;
902     TextConsole *s;
903     int i,j;
904     static int color_inited;
905
906     chr = qemu_mallocz(sizeof(CharDriverState));
907     if (!chr)
908         return NULL;
909     s = graphic_console_init(ds);
910     if (!s) {
911         free(chr);
912         return NULL;
913     }
914     s->text_console = 1;
915     chr->opaque = s;
916     chr->chr_write = console_puts;
917     chr->chr_add_read_handler = console_chr_add_read_handler;
918     chr->chr_send_event = console_send_event;
919
920     if (!color_inited) {
921         color_inited = 1;
922         for(j = 0; j < 2; j++) {
923             for(i = 0; i < 8; i++) {
924                 color_table[j][i] = col_expand(s->ds, 
925                         vga_get_color(s->ds, color_table_rgb[j][i]));
926             }
927         }
928     }
929     s->y_displayed = 0;
930     s->y_base = 0;
931     s->total_height = DEFAULT_BACKSCROLL;
932     s->x = 0;
933     s->y = 0;
934     s->g_width = s->ds->width;
935     s->g_height = s->ds->height;
936
937     /* Set text attribute defaults */
938     s->t_attrib_default.bold = 0;
939     s->t_attrib_default.uline = 0;
940     s->t_attrib_default.blink = 0;
941     s->t_attrib_default.invers = 0;
942     s->t_attrib_default.unvisible = 0;
943     s->t_attrib_default.fgcol = COLOR_WHITE;
944     s->t_attrib_default.bgcol = COLOR_BLACK;
945
946     /* set current text attributes to default */
947     s->t_attrib = s->t_attrib_default;
948     text_console_resize(s);
949
950     return chr;
951 }