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