xref: /openbmc/qemu/ui/console.c (revision 1dbfa005032d4fa5d7a5242da856d3487c907431)
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 "ui/console.h"
26 #include "qemu/timer.h"
27 #include "qmp-commands.h"
28 #include "sysemu/char.h"
29 
30 //#define DEBUG_CONSOLE
31 #define DEFAULT_BACKSCROLL 512
32 #define MAX_CONSOLES 12
33 #define CONSOLE_CURSOR_PERIOD 500
34 
35 typedef struct TextAttributes {
36     uint8_t fgcol:4;
37     uint8_t bgcol:4;
38     uint8_t bold:1;
39     uint8_t uline:1;
40     uint8_t blink:1;
41     uint8_t invers:1;
42     uint8_t unvisible:1;
43 } TextAttributes;
44 
45 typedef struct TextCell {
46     uint8_t ch;
47     TextAttributes t_attrib;
48 } TextCell;
49 
50 #define MAX_ESC_PARAMS 3
51 
52 enum TTYState {
53     TTY_STATE_NORM,
54     TTY_STATE_ESC,
55     TTY_STATE_CSI,
56 };
57 
58 typedef struct QEMUFIFO {
59     uint8_t *buf;
60     int buf_size;
61     int count, wptr, rptr;
62 } QEMUFIFO;
63 
64 static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
65 {
66     int l, len;
67 
68     l = f->buf_size - f->count;
69     if (len1 > l)
70         len1 = l;
71     len = len1;
72     while (len > 0) {
73         l = f->buf_size - f->wptr;
74         if (l > len)
75             l = len;
76         memcpy(f->buf + f->wptr, buf, l);
77         f->wptr += l;
78         if (f->wptr >= f->buf_size)
79             f->wptr = 0;
80         buf += l;
81         len -= l;
82     }
83     f->count += len1;
84     return len1;
85 }
86 
87 static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
88 {
89     int l, len;
90 
91     if (len1 > f->count)
92         len1 = f->count;
93     len = len1;
94     while (len > 0) {
95         l = f->buf_size - f->rptr;
96         if (l > len)
97             l = len;
98         memcpy(buf, f->buf + f->rptr, l);
99         f->rptr += l;
100         if (f->rptr >= f->buf_size)
101             f->rptr = 0;
102         buf += l;
103         len -= l;
104     }
105     f->count -= len1;
106     return len1;
107 }
108 
109 typedef enum {
110     GRAPHIC_CONSOLE,
111     TEXT_CONSOLE,
112     TEXT_CONSOLE_FIXED_SIZE
113 } console_type_t;
114 
115 struct QemuConsole {
116     int index;
117     console_type_t console_type;
118     DisplayState *ds;
119 
120     /* Graphic console state.  */
121     graphic_hw_update_ptr hw_update;
122     graphic_hw_invalidate_ptr hw_invalidate;
123     graphic_hw_screen_dump_ptr hw_screen_dump;
124     graphic_hw_text_update_ptr hw_text_update;
125     void *hw;
126     int g_width, g_height;
127 
128     /* Text console state */
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     int echo;
142     bool cursor_visible_phase;
143     QEMUTimer *cursor_timer;
144 
145     int update_x0;
146     int update_y0;
147     int update_x1;
148     int update_y1;
149 
150     enum TTYState state;
151     int esc_params[MAX_ESC_PARAMS];
152     int nb_esc_params;
153 
154     CharDriverState *chr;
155     /* fifo for key pressed */
156     QEMUFIFO out_fifo;
157     uint8_t out_fifo_buf[16];
158     QEMUTimer *kbd_timer;
159 };
160 
161 static DisplayState *display_state;
162 static QemuConsole *active_console;
163 static QemuConsole *consoles[MAX_CONSOLES];
164 static int nb_consoles = 0;
165 
166 static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
167 
168 void graphic_hw_update(QemuConsole *con)
169 {
170     if (!con) {
171         con = active_console;
172     }
173     if (con && con->hw_update) {
174         con->hw_update(con->hw);
175     }
176 }
177 
178 void graphic_hw_invalidate(QemuConsole *con)
179 {
180     if (!con) {
181         con = active_console;
182     }
183     if (con && con->hw_invalidate) {
184         con->hw_invalidate(con->hw);
185     }
186 }
187 
188 void qmp_screendump(const char *filename, Error **errp)
189 {
190     QemuConsole *previous_active_console;
191     bool cswitch;
192 
193     previous_active_console = active_console;
194     cswitch = previous_active_console && previous_active_console->index != 0;
195 
196     /* There is currently no way of specifying which screen we want to dump,
197        so always dump the first one.  */
198     if (cswitch) {
199         console_select(0);
200     }
201     if (consoles[0] && consoles[0]->hw_screen_dump) {
202         consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp);
203     } else {
204         error_setg(errp, "device doesn't support screendump");
205     }
206 
207     if (cswitch) {
208         console_select(previous_active_console->index);
209     }
210 }
211 
212 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
213 {
214     if (!con) {
215         con = active_console;
216     }
217     if (con && con->hw_text_update)
218         con->hw_text_update(con->hw, chardata);
219 }
220 
221 static void vga_fill_rect(QemuConsole *con,
222                           int posx, int posy, int width, int height,
223                           pixman_color_t color)
224 {
225     DisplaySurface *surface = qemu_console_surface(con);
226     pixman_rectangle16_t rect = {
227         .x = posx, .y = posy, .width = width, .height = height
228     };
229 
230     pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
231                                  &color, 1, &rect);
232 }
233 
234 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
235 static void vga_bitblt(QemuConsole *con,
236                        int xs, int ys, int xd, int yd, int w, int h)
237 {
238     DisplaySurface *surface = qemu_console_surface(con);
239 
240     pixman_image_composite(PIXMAN_OP_SRC,
241                            surface->image, NULL, surface->image,
242                            xs, ys, 0, 0, xd, yd, w, h);
243 }
244 
245 /***********************************************************/
246 /* basic char display */
247 
248 #define FONT_HEIGHT 16
249 #define FONT_WIDTH 8
250 
251 #include "vgafont.h"
252 
253 #ifndef CONFIG_CURSES
254 enum color_names {
255     COLOR_BLACK   = 0,
256     COLOR_RED     = 1,
257     COLOR_GREEN   = 2,
258     COLOR_YELLOW  = 3,
259     COLOR_BLUE    = 4,
260     COLOR_MAGENTA = 5,
261     COLOR_CYAN    = 6,
262     COLOR_WHITE   = 7
263 };
264 #endif
265 
266 #define QEMU_RGB(r, g, b)                                               \
267     { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
268 
269 static const pixman_color_t color_table_rgb[2][8] = {
270     {   /* dark */
271         QEMU_RGB(0x00, 0x00, 0x00),  /* black */
272         QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
273         QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
274         QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
275         QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
276         QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
277         QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
278         QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
279     },
280     {   /* bright */
281         QEMU_RGB(0x00, 0x00, 0x00),  /* black */
282         QEMU_RGB(0xff, 0x00, 0x00),  /* red */
283         QEMU_RGB(0x00, 0xff, 0x00),  /* green */
284         QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
285         QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
286         QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
287         QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
288         QEMU_RGB(0xff, 0xff, 0xff),  /* white */
289     }
290 };
291 
292 #ifdef DEBUG_CONSOLE
293 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
294 {
295     if (t_attrib->bold) {
296         printf("b");
297     } else {
298         printf(" ");
299     }
300     if (t_attrib->uline) {
301         printf("u");
302     } else {
303         printf(" ");
304     }
305     if (t_attrib->blink) {
306         printf("l");
307     } else {
308         printf(" ");
309     }
310     if (t_attrib->invers) {
311         printf("i");
312     } else {
313         printf(" ");
314     }
315     if (t_attrib->unvisible) {
316         printf("n");
317     } else {
318         printf(" ");
319     }
320 
321     printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
322 }
323 #endif
324 
325 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
326                           TextAttributes *t_attrib)
327 {
328     static pixman_image_t *glyphs[256];
329     DisplaySurface *surface = qemu_console_surface(s);
330     pixman_color_t fgcol, bgcol;
331 
332     if (t_attrib->invers) {
333         bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
334         fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
335     } else {
336         fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
337         bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
338     }
339 
340     if (!glyphs[ch]) {
341         glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
342     }
343     qemu_pixman_glyph_render(glyphs[ch], surface->image,
344                              &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
345 }
346 
347 static void text_console_resize(QemuConsole *s)
348 {
349     TextCell *cells, *c, *c1;
350     int w1, x, y, last_width;
351 
352     last_width = s->width;
353     s->width = s->g_width / FONT_WIDTH;
354     s->height = s->g_height / FONT_HEIGHT;
355 
356     w1 = last_width;
357     if (s->width < w1)
358         w1 = s->width;
359 
360     cells = g_malloc(s->width * s->total_height * sizeof(TextCell));
361     for(y = 0; y < s->total_height; y++) {
362         c = &cells[y * s->width];
363         if (w1 > 0) {
364             c1 = &s->cells[y * last_width];
365             for(x = 0; x < w1; x++) {
366                 *c++ = *c1++;
367             }
368         }
369         for(x = w1; x < s->width; x++) {
370             c->ch = ' ';
371             c->t_attrib = s->t_attrib_default;
372             c++;
373         }
374     }
375     g_free(s->cells);
376     s->cells = cells;
377 }
378 
379 static inline void text_update_xy(QemuConsole *s, int x, int y)
380 {
381     s->text_x[0] = MIN(s->text_x[0], x);
382     s->text_x[1] = MAX(s->text_x[1], x);
383     s->text_y[0] = MIN(s->text_y[0], y);
384     s->text_y[1] = MAX(s->text_y[1], y);
385 }
386 
387 static void invalidate_xy(QemuConsole *s, int x, int y)
388 {
389     if (s->update_x0 > x * FONT_WIDTH)
390         s->update_x0 = x * FONT_WIDTH;
391     if (s->update_y0 > y * FONT_HEIGHT)
392         s->update_y0 = y * FONT_HEIGHT;
393     if (s->update_x1 < (x + 1) * FONT_WIDTH)
394         s->update_x1 = (x + 1) * FONT_WIDTH;
395     if (s->update_y1 < (y + 1) * FONT_HEIGHT)
396         s->update_y1 = (y + 1) * FONT_HEIGHT;
397 }
398 
399 static void update_xy(QemuConsole *s, int x, int y)
400 {
401     TextCell *c;
402     int y1, y2;
403 
404     if (s != active_console) {
405         return;
406     }
407 
408     if (s->ds->have_text) {
409         text_update_xy(s, x, y);
410     }
411 
412     if (s->ds->have_gfx) {
413         y1 = (s->y_base + y) % s->total_height;
414         y2 = y1 - s->y_displayed;
415         if (y2 < 0)
416             y2 += s->total_height;
417         if (y2 < s->height) {
418             c = &s->cells[y1 * s->width + x];
419             vga_putcharxy(s, x, y2, c->ch,
420                           &(c->t_attrib));
421             invalidate_xy(s, x, y2);
422         }
423     }
424 }
425 
426 static void console_show_cursor(QemuConsole *s, int show)
427 {
428     TextCell *c;
429     int y, y1;
430     int x = s->x;
431 
432     if (s != active_console) {
433         return;
434     }
435 
436     if (s->ds->have_text) {
437         s->cursor_invalidate = 1;
438     }
439 
440     if (s->ds->have_gfx) {
441         if (x >= s->width) {
442             x = s->width - 1;
443         }
444         y1 = (s->y_base + s->y) % s->total_height;
445         y = y1 - s->y_displayed;
446         if (y < 0)
447             y += s->total_height;
448         if (y < s->height) {
449             c = &s->cells[y1 * s->width + x];
450             if (show && s->cursor_visible_phase) {
451                 TextAttributes t_attrib = s->t_attrib_default;
452                 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
453                 vga_putcharxy(s, x, y, c->ch, &t_attrib);
454             } else {
455                 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
456             }
457             invalidate_xy(s, x, y);
458         }
459     }
460 }
461 
462 static void console_refresh(QemuConsole *s)
463 {
464     DisplaySurface *surface = qemu_console_surface(s);
465     TextCell *c;
466     int x, y, y1;
467 
468     if (s != active_console)
469         return;
470 
471     if (s->ds->have_text) {
472         s->text_x[0] = 0;
473         s->text_y[0] = 0;
474         s->text_x[1] = s->width - 1;
475         s->text_y[1] = s->height - 1;
476         s->cursor_invalidate = 1;
477     }
478 
479     if (s->ds->have_gfx) {
480         vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
481                       color_table_rgb[0][COLOR_BLACK]);
482         y1 = s->y_displayed;
483         for (y = 0; y < s->height; y++) {
484             c = s->cells + y1 * s->width;
485             for (x = 0; x < s->width; x++) {
486                 vga_putcharxy(s, x, y, c->ch,
487                               &(c->t_attrib));
488                 c++;
489             }
490             if (++y1 == s->total_height) {
491                 y1 = 0;
492             }
493         }
494         console_show_cursor(s, 1);
495         dpy_gfx_update(s, 0, 0,
496                        surface_width(surface), surface_height(surface));
497     }
498 }
499 
500 static void console_scroll(int ydelta)
501 {
502     QemuConsole *s;
503     int i, y1;
504 
505     s = active_console;
506     if (!s || (s->console_type == GRAPHIC_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(QemuConsole *s)
535 {
536     TextCell *c;
537     int x, y1;
538 
539     s->y++;
540     if (s->y >= s->height) {
541         s->y = s->height - 1;
542 
543         if (s->y_displayed == s->y_base) {
544             if (++s->y_displayed == s->total_height)
545                 s->y_displayed = 0;
546         }
547         if (++s->y_base == s->total_height)
548             s->y_base = 0;
549         if (s->backscroll_height < s->total_height)
550             s->backscroll_height++;
551         y1 = (s->y_base + s->height - 1) % s->total_height;
552         c = &s->cells[y1 * s->width];
553         for(x = 0; x < s->width; x++) {
554             c->ch = ' ';
555             c->t_attrib = s->t_attrib_default;
556             c++;
557         }
558         if (s == active_console && s->y_displayed == s->y_base) {
559             if (s->ds->have_text) {
560                 s->text_x[0] = 0;
561                 s->text_y[0] = 0;
562                 s->text_x[1] = s->width - 1;
563                 s->text_y[1] = s->height - 1;
564             }
565 
566             if (s->ds->have_gfx) {
567                 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
568                            s->width * FONT_WIDTH,
569                            (s->height - 1) * FONT_HEIGHT);
570                 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
571                               s->width * FONT_WIDTH, FONT_HEIGHT,
572                               color_table_rgb[0][s->t_attrib_default.bgcol]);
573                 s->update_x0 = 0;
574                 s->update_y0 = 0;
575                 s->update_x1 = s->width * FONT_WIDTH;
576                 s->update_y1 = s->height * FONT_HEIGHT;
577             }
578         }
579     }
580 }
581 
582 /* Set console attributes depending on the current escape codes.
583  * NOTE: I know this code is not very efficient (checking every color for it
584  * self) but it is more readable and better maintainable.
585  */
586 static void console_handle_escape(QemuConsole *s)
587 {
588     int i;
589 
590     for (i=0; i<s->nb_esc_params; i++) {
591         switch (s->esc_params[i]) {
592             case 0: /* reset all console attributes to default */
593                 s->t_attrib = s->t_attrib_default;
594                 break;
595             case 1:
596                 s->t_attrib.bold = 1;
597                 break;
598             case 4:
599                 s->t_attrib.uline = 1;
600                 break;
601             case 5:
602                 s->t_attrib.blink = 1;
603                 break;
604             case 7:
605                 s->t_attrib.invers = 1;
606                 break;
607             case 8:
608                 s->t_attrib.unvisible = 1;
609                 break;
610             case 22:
611                 s->t_attrib.bold = 0;
612                 break;
613             case 24:
614                 s->t_attrib.uline = 0;
615                 break;
616             case 25:
617                 s->t_attrib.blink = 0;
618                 break;
619             case 27:
620                 s->t_attrib.invers = 0;
621                 break;
622             case 28:
623                 s->t_attrib.unvisible = 0;
624                 break;
625             /* set foreground color */
626             case 30:
627                 s->t_attrib.fgcol=COLOR_BLACK;
628                 break;
629             case 31:
630                 s->t_attrib.fgcol=COLOR_RED;
631                 break;
632             case 32:
633                 s->t_attrib.fgcol=COLOR_GREEN;
634                 break;
635             case 33:
636                 s->t_attrib.fgcol=COLOR_YELLOW;
637                 break;
638             case 34:
639                 s->t_attrib.fgcol=COLOR_BLUE;
640                 break;
641             case 35:
642                 s->t_attrib.fgcol=COLOR_MAGENTA;
643                 break;
644             case 36:
645                 s->t_attrib.fgcol=COLOR_CYAN;
646                 break;
647             case 37:
648                 s->t_attrib.fgcol=COLOR_WHITE;
649                 break;
650             /* set background color */
651             case 40:
652                 s->t_attrib.bgcol=COLOR_BLACK;
653                 break;
654             case 41:
655                 s->t_attrib.bgcol=COLOR_RED;
656                 break;
657             case 42:
658                 s->t_attrib.bgcol=COLOR_GREEN;
659                 break;
660             case 43:
661                 s->t_attrib.bgcol=COLOR_YELLOW;
662                 break;
663             case 44:
664                 s->t_attrib.bgcol=COLOR_BLUE;
665                 break;
666             case 45:
667                 s->t_attrib.bgcol=COLOR_MAGENTA;
668                 break;
669             case 46:
670                 s->t_attrib.bgcol=COLOR_CYAN;
671                 break;
672             case 47:
673                 s->t_attrib.bgcol=COLOR_WHITE;
674                 break;
675         }
676     }
677 }
678 
679 static void console_clear_xy(QemuConsole *s, int x, int y)
680 {
681     int y1 = (s->y_base + y) % s->total_height;
682     TextCell *c = &s->cells[y1 * s->width + x];
683     c->ch = ' ';
684     c->t_attrib = s->t_attrib_default;
685     update_xy(s, x, y);
686 }
687 
688 /* set cursor, checking bounds */
689 static void set_cursor(QemuConsole *s, int x, int y)
690 {
691     if (x < 0) {
692         x = 0;
693     }
694     if (y < 0) {
695         y = 0;
696     }
697     if (y >= s->height) {
698         y = s->height - 1;
699     }
700     if (x >= s->width) {
701         x = s->width - 1;
702     }
703 
704     s->x = x;
705     s->y = y;
706 }
707 
708 static void console_putchar(QemuConsole *s, int ch)
709 {
710     TextCell *c;
711     int y1, i;
712     int x, y;
713 
714     switch(s->state) {
715     case TTY_STATE_NORM:
716         switch(ch) {
717         case '\r':  /* carriage return */
718             s->x = 0;
719             break;
720         case '\n':  /* newline */
721             console_put_lf(s);
722             break;
723         case '\b':  /* backspace */
724             if (s->x > 0)
725                 s->x--;
726             break;
727         case '\t':  /* tabspace */
728             if (s->x + (8 - (s->x % 8)) > s->width) {
729                 s->x = 0;
730                 console_put_lf(s);
731             } else {
732                 s->x = s->x + (8 - (s->x % 8));
733             }
734             break;
735         case '\a':  /* alert aka. bell */
736             /* TODO: has to be implemented */
737             break;
738         case 14:
739             /* SI (shift in), character set 0 (ignored) */
740             break;
741         case 15:
742             /* SO (shift out), character set 1 (ignored) */
743             break;
744         case 27:    /* esc (introducing an escape sequence) */
745             s->state = TTY_STATE_ESC;
746             break;
747         default:
748             if (s->x >= s->width) {
749                 /* line wrap */
750                 s->x = 0;
751                 console_put_lf(s);
752             }
753             y1 = (s->y_base + s->y) % s->total_height;
754             c = &s->cells[y1 * s->width + s->x];
755             c->ch = ch;
756             c->t_attrib = s->t_attrib;
757             update_xy(s, s->x, s->y);
758             s->x++;
759             break;
760         }
761         break;
762     case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
763         if (ch == '[') {
764             for(i=0;i<MAX_ESC_PARAMS;i++)
765                 s->esc_params[i] = 0;
766             s->nb_esc_params = 0;
767             s->state = TTY_STATE_CSI;
768         } else {
769             s->state = TTY_STATE_NORM;
770         }
771         break;
772     case TTY_STATE_CSI: /* handle escape sequence parameters */
773         if (ch >= '0' && ch <= '9') {
774             if (s->nb_esc_params < MAX_ESC_PARAMS) {
775                 int *param = &s->esc_params[s->nb_esc_params];
776                 int digit = (ch - '0');
777 
778                 *param = (*param <= (INT_MAX - digit) / 10) ?
779                          *param * 10 + digit : INT_MAX;
780             }
781         } else {
782             if (s->nb_esc_params < MAX_ESC_PARAMS)
783                 s->nb_esc_params++;
784             if (ch == ';')
785                 break;
786 #ifdef DEBUG_CONSOLE
787             fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
788                     s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
789 #endif
790             s->state = TTY_STATE_NORM;
791             switch(ch) {
792             case 'A':
793                 /* move cursor up */
794                 if (s->esc_params[0] == 0) {
795                     s->esc_params[0] = 1;
796                 }
797                 set_cursor(s, s->x, s->y - s->esc_params[0]);
798                 break;
799             case 'B':
800                 /* move cursor down */
801                 if (s->esc_params[0] == 0) {
802                     s->esc_params[0] = 1;
803                 }
804                 set_cursor(s, s->x, s->y + s->esc_params[0]);
805                 break;
806             case 'C':
807                 /* move cursor right */
808                 if (s->esc_params[0] == 0) {
809                     s->esc_params[0] = 1;
810                 }
811                 set_cursor(s, s->x + s->esc_params[0], s->y);
812                 break;
813             case 'D':
814                 /* move cursor left */
815                 if (s->esc_params[0] == 0) {
816                     s->esc_params[0] = 1;
817                 }
818                 set_cursor(s, s->x - s->esc_params[0], s->y);
819                 break;
820             case 'G':
821                 /* move cursor to column */
822                 set_cursor(s, s->esc_params[0] - 1, s->y);
823                 break;
824             case 'f':
825             case 'H':
826                 /* move cursor to row, column */
827                 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
828                 break;
829             case 'J':
830                 switch (s->esc_params[0]) {
831                 case 0:
832                     /* clear to end of screen */
833                     for (y = s->y; y < s->height; y++) {
834                         for (x = 0; x < s->width; x++) {
835                             if (y == s->y && x < s->x) {
836                                 continue;
837                             }
838                             console_clear_xy(s, x, y);
839                         }
840                     }
841                     break;
842                 case 1:
843                     /* clear from beginning of screen */
844                     for (y = 0; y <= s->y; y++) {
845                         for (x = 0; x < s->width; x++) {
846                             if (y == s->y && x > s->x) {
847                                 break;
848                             }
849                             console_clear_xy(s, x, y);
850                         }
851                     }
852                     break;
853                 case 2:
854                     /* clear entire screen */
855                     for (y = 0; y <= s->height; y++) {
856                         for (x = 0; x < s->width; x++) {
857                             console_clear_xy(s, x, y);
858                         }
859                     }
860                     break;
861                 }
862                 break;
863             case 'K':
864                 switch (s->esc_params[0]) {
865                 case 0:
866                     /* clear to eol */
867                     for(x = s->x; x < s->width; x++) {
868                         console_clear_xy(s, x, s->y);
869                     }
870                     break;
871                 case 1:
872                     /* clear from beginning of line */
873                     for (x = 0; x <= s->x; x++) {
874                         console_clear_xy(s, x, s->y);
875                     }
876                     break;
877                 case 2:
878                     /* clear entire line */
879                     for(x = 0; x < s->width; x++) {
880                         console_clear_xy(s, x, s->y);
881                     }
882                     break;
883                 }
884                 break;
885             case 'm':
886                 console_handle_escape(s);
887                 break;
888             case 'n':
889                 /* report cursor position */
890                 /* TODO: send ESC[row;colR */
891                 break;
892             case 's':
893                 /* save cursor position */
894                 s->x_saved = s->x;
895                 s->y_saved = s->y;
896                 break;
897             case 'u':
898                 /* restore cursor position */
899                 s->x = s->x_saved;
900                 s->y = s->y_saved;
901                 break;
902             default:
903 #ifdef DEBUG_CONSOLE
904                 fprintf(stderr, "unhandled escape character '%c'\n", ch);
905 #endif
906                 break;
907             }
908             break;
909         }
910     }
911 }
912 
913 void console_select(unsigned int index)
914 {
915     DisplaySurface *surface;
916     QemuConsole *s;
917 
918     if (index >= MAX_CONSOLES)
919         return;
920 
921     trace_console_select(index);
922     if (active_console) {
923         surface = qemu_console_surface(active_console);
924         active_console->g_width = surface_width(surface);
925         active_console->g_height = surface_height(surface);
926     }
927     s = consoles[index];
928     if (s) {
929         DisplayState *ds = s->ds;
930 
931         if (active_console && active_console->cursor_timer) {
932             qemu_del_timer(active_console->cursor_timer);
933         }
934         active_console = s;
935         if (ds->have_gfx) {
936             surface = qemu_create_displaysurface(s->g_width, s->g_height);
937             dpy_gfx_replace_surface(s, surface);
938         }
939         if (ds->have_text) {
940             dpy_text_resize(s, s->width, s->height);
941         }
942         if (s->cursor_timer) {
943             qemu_mod_timer(s->cursor_timer,
944                    qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
945         }
946         graphic_hw_invalidate(s);
947     }
948 }
949 
950 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
951 {
952     QemuConsole *s = chr->opaque;
953     int i;
954 
955     s->update_x0 = s->width * FONT_WIDTH;
956     s->update_y0 = s->height * FONT_HEIGHT;
957     s->update_x1 = 0;
958     s->update_y1 = 0;
959     console_show_cursor(s, 0);
960     for(i = 0; i < len; i++) {
961         console_putchar(s, buf[i]);
962     }
963     console_show_cursor(s, 1);
964     if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
965         dpy_gfx_update(s, s->update_x0, s->update_y0,
966                        s->update_x1 - s->update_x0,
967                        s->update_y1 - s->update_y0);
968     }
969     return len;
970 }
971 
972 static void kbd_send_chars(void *opaque)
973 {
974     QemuConsole *s = opaque;
975     int len;
976     uint8_t buf[16];
977 
978     len = qemu_chr_be_can_write(s->chr);
979     if (len > s->out_fifo.count)
980         len = s->out_fifo.count;
981     if (len > 0) {
982         if (len > sizeof(buf))
983             len = sizeof(buf);
984         qemu_fifo_read(&s->out_fifo, buf, len);
985         qemu_chr_be_write(s->chr, buf, len);
986     }
987     /* characters are pending: we send them a bit later (XXX:
988        horrible, should change char device API) */
989     if (s->out_fifo.count > 0) {
990         qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
991     }
992 }
993 
994 /* called when an ascii key is pressed */
995 void kbd_put_keysym(int keysym)
996 {
997     QemuConsole *s;
998     uint8_t buf[16], *q;
999     int c;
1000 
1001     s = active_console;
1002     if (!s || (s->console_type == GRAPHIC_CONSOLE))
1003         return;
1004 
1005     switch(keysym) {
1006     case QEMU_KEY_CTRL_UP:
1007         console_scroll(-1);
1008         break;
1009     case QEMU_KEY_CTRL_DOWN:
1010         console_scroll(1);
1011         break;
1012     case QEMU_KEY_CTRL_PAGEUP:
1013         console_scroll(-10);
1014         break;
1015     case QEMU_KEY_CTRL_PAGEDOWN:
1016         console_scroll(10);
1017         break;
1018     default:
1019         /* convert the QEMU keysym to VT100 key string */
1020         q = buf;
1021         if (keysym >= 0xe100 && keysym <= 0xe11f) {
1022             *q++ = '\033';
1023             *q++ = '[';
1024             c = keysym - 0xe100;
1025             if (c >= 10)
1026                 *q++ = '0' + (c / 10);
1027             *q++ = '0' + (c % 10);
1028             *q++ = '~';
1029         } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1030             *q++ = '\033';
1031             *q++ = '[';
1032             *q++ = keysym & 0xff;
1033         } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1034             console_puts(s->chr, (const uint8_t *) "\r", 1);
1035             *q++ = '\n';
1036         } else {
1037             *q++ = keysym;
1038         }
1039         if (s->echo) {
1040             console_puts(s->chr, buf, q - buf);
1041         }
1042         if (s->chr->chr_read) {
1043             qemu_fifo_write(&s->out_fifo, buf, q - buf);
1044             kbd_send_chars(s);
1045         }
1046         break;
1047     }
1048 }
1049 
1050 static void text_console_invalidate(void *opaque)
1051 {
1052     QemuConsole *s = (QemuConsole *) opaque;
1053     DisplaySurface *surface = qemu_console_surface(s);
1054 
1055     if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1056         s->g_width = surface_width(surface);
1057         s->g_height = surface_height(surface);
1058         text_console_resize(s);
1059     }
1060     console_refresh(s);
1061 }
1062 
1063 static void text_console_update(void *opaque, console_ch_t *chardata)
1064 {
1065     QemuConsole *s = (QemuConsole *) opaque;
1066     int i, j, src;
1067 
1068     if (s->text_x[0] <= s->text_x[1]) {
1069         src = (s->y_base + s->text_y[0]) * s->width;
1070         chardata += s->text_y[0] * s->width;
1071         for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1072             for (j = 0; j < s->width; j ++, src ++)
1073                 console_write_ch(chardata ++, s->cells[src].ch |
1074                                 (s->cells[src].t_attrib.fgcol << 12) |
1075                                 (s->cells[src].t_attrib.bgcol << 8) |
1076                                 (s->cells[src].t_attrib.bold << 21));
1077         dpy_text_update(s, s->text_x[0], s->text_y[0],
1078                         s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1079         s->text_x[0] = s->width;
1080         s->text_y[0] = s->height;
1081         s->text_x[1] = 0;
1082         s->text_y[1] = 0;
1083     }
1084     if (s->cursor_invalidate) {
1085         dpy_text_cursor(s, s->x, s->y);
1086         s->cursor_invalidate = 0;
1087     }
1088 }
1089 
1090 static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
1091 {
1092     QemuConsole *s;
1093     int i;
1094 
1095     if (nb_consoles >= MAX_CONSOLES)
1096         return NULL;
1097     s = g_malloc0(sizeof(QemuConsole));
1098     if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1099         (console_type == GRAPHIC_CONSOLE))) {
1100         active_console = s;
1101     }
1102     s->ds = ds;
1103     s->console_type = console_type;
1104     if (console_type != GRAPHIC_CONSOLE) {
1105         s->index = nb_consoles;
1106         consoles[nb_consoles++] = s;
1107     } else {
1108         /* HACK: Put graphical consoles before text consoles.  */
1109         for (i = nb_consoles; i > 0; i--) {
1110             if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1111                 break;
1112             consoles[i] = consoles[i - 1];
1113             consoles[i]->index = i;
1114         }
1115         s->index = i;
1116         consoles[i] = s;
1117         nb_consoles++;
1118     }
1119     return s;
1120 }
1121 
1122 static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1123                                int linesize, PixelFormat pf, int newflags)
1124 {
1125     surface->pf = pf;
1126 
1127     qemu_pixman_image_unref(surface->image);
1128     surface->image = NULL;
1129 
1130     surface->format = qemu_pixman_get_format(&pf);
1131     assert(surface->format != 0);
1132     surface->image = pixman_image_create_bits(surface->format,
1133                                               width, height,
1134                                               NULL, linesize);
1135     assert(surface->image != NULL);
1136 
1137     surface->flags = newflags | QEMU_ALLOCATED_FLAG;
1138 #ifdef HOST_WORDS_BIGENDIAN
1139     surface->flags |= QEMU_BIG_ENDIAN_FLAG;
1140 #endif
1141 }
1142 
1143 DisplaySurface *qemu_create_displaysurface(int width, int height)
1144 {
1145     DisplaySurface *surface = g_new0(DisplaySurface, 1);
1146     int linesize = width * 4;
1147 
1148     trace_displaysurface_create(surface, width, height);
1149     qemu_alloc_display(surface, width, height, linesize,
1150                        qemu_default_pixelformat(32), 0);
1151     return surface;
1152 }
1153 
1154 DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
1155                                                 int linesize, uint8_t *data,
1156                                                 bool byteswap)
1157 {
1158     DisplaySurface *surface = g_new0(DisplaySurface, 1);
1159 
1160     trace_displaysurface_create_from(surface, width, height, bpp, byteswap);
1161     if (byteswap) {
1162         surface->pf = qemu_different_endianness_pixelformat(bpp);
1163     } else {
1164         surface->pf = qemu_default_pixelformat(bpp);
1165     }
1166 
1167     surface->format = qemu_pixman_get_format(&surface->pf);
1168     assert(surface->format != 0);
1169     surface->image = pixman_image_create_bits(surface->format,
1170                                               width, height,
1171                                               (void *)data, linesize);
1172     assert(surface->image != NULL);
1173 
1174 #ifdef HOST_WORDS_BIGENDIAN
1175     surface->flags = QEMU_BIG_ENDIAN_FLAG;
1176 #endif
1177 
1178     return surface;
1179 }
1180 
1181 void qemu_free_displaysurface(DisplaySurface *surface)
1182 {
1183     if (surface == NULL) {
1184         return;
1185     }
1186     trace_displaysurface_free(surface);
1187     qemu_pixman_image_unref(surface->image);
1188     g_free(surface);
1189 }
1190 
1191 void register_displaychangelistener(DisplayState *ds,
1192                                     DisplayChangeListener *dcl)
1193 {
1194     trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1195     dcl->ds = ds;
1196     QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
1197     gui_setup_refresh(ds);
1198     if (dcl->ops->dpy_gfx_switch) {
1199         dcl->ops->dpy_gfx_switch(dcl, ds->surface);
1200     }
1201 }
1202 
1203 void unregister_displaychangelistener(DisplayChangeListener *dcl)
1204 {
1205     DisplayState *ds = dcl->ds;
1206     trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1207     QLIST_REMOVE(dcl, next);
1208     gui_setup_refresh(ds);
1209 }
1210 
1211 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1212 {
1213     DisplayState *s = con->ds;
1214     struct DisplayChangeListener *dcl;
1215     int width = pixman_image_get_width(s->surface->image);
1216     int height = pixman_image_get_height(s->surface->image);
1217 
1218     x = MAX(x, 0);
1219     y = MAX(y, 0);
1220     x = MIN(x, width);
1221     y = MIN(y, height);
1222     w = MIN(w, width - x);
1223     h = MIN(h, height - y);
1224 
1225     QLIST_FOREACH(dcl, &s->listeners, next) {
1226         if (dcl->ops->dpy_gfx_update) {
1227             dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1228         }
1229     }
1230 }
1231 
1232 void dpy_gfx_replace_surface(QemuConsole *con,
1233                              DisplaySurface *surface)
1234 {
1235     DisplayState *s = con->ds;
1236     DisplaySurface *old_surface = s->surface;
1237     struct DisplayChangeListener *dcl;
1238 
1239     s->surface = surface;
1240     QLIST_FOREACH(dcl, &s->listeners, next) {
1241         if (dcl->ops->dpy_gfx_switch) {
1242             dcl->ops->dpy_gfx_switch(dcl, surface);
1243         }
1244     }
1245     qemu_free_displaysurface(old_surface);
1246 }
1247 
1248 void dpy_refresh(DisplayState *s)
1249 {
1250     struct DisplayChangeListener *dcl;
1251     QLIST_FOREACH(dcl, &s->listeners, next) {
1252         if (dcl->ops->dpy_refresh) {
1253             dcl->ops->dpy_refresh(dcl);
1254         }
1255     }
1256 }
1257 
1258 void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
1259                   int dst_x, int dst_y, int w, int h)
1260 {
1261     DisplayState *s = con->ds;
1262     struct DisplayChangeListener *dcl;
1263     QLIST_FOREACH(dcl, &s->listeners, next) {
1264         if (dcl->ops->dpy_gfx_copy) {
1265             dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
1266         } else { /* TODO */
1267             dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
1268         }
1269     }
1270 }
1271 
1272 void dpy_text_cursor(QemuConsole *con, int x, int y)
1273 {
1274     DisplayState *s = con->ds;
1275     struct DisplayChangeListener *dcl;
1276     QLIST_FOREACH(dcl, &s->listeners, next) {
1277         if (dcl->ops->dpy_text_cursor) {
1278             dcl->ops->dpy_text_cursor(dcl, x, y);
1279         }
1280     }
1281 }
1282 
1283 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1284 {
1285     DisplayState *s = con->ds;
1286     struct DisplayChangeListener *dcl;
1287     QLIST_FOREACH(dcl, &s->listeners, next) {
1288         if (dcl->ops->dpy_text_update) {
1289             dcl->ops->dpy_text_update(dcl, x, y, w, h);
1290         }
1291     }
1292 }
1293 
1294 void dpy_text_resize(QemuConsole *con, int w, int h)
1295 {
1296     DisplayState *s = con->ds;
1297     struct DisplayChangeListener *dcl;
1298     QLIST_FOREACH(dcl, &s->listeners, next) {
1299         if (dcl->ops->dpy_text_resize) {
1300             dcl->ops->dpy_text_resize(dcl, w, h);
1301         }
1302     }
1303 }
1304 
1305 void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1306 {
1307     DisplayState *s = con->ds;
1308     struct DisplayChangeListener *dcl;
1309     QLIST_FOREACH(dcl, &s->listeners, next) {
1310         if (dcl->ops->dpy_mouse_set) {
1311             dcl->ops->dpy_mouse_set(dcl, x, y, on);
1312         }
1313     }
1314 }
1315 
1316 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1317 {
1318     DisplayState *s = con->ds;
1319     struct DisplayChangeListener *dcl;
1320     QLIST_FOREACH(dcl, &s->listeners, next) {
1321         if (dcl->ops->dpy_cursor_define) {
1322             dcl->ops->dpy_cursor_define(dcl, cursor);
1323         }
1324     }
1325 }
1326 
1327 bool dpy_cursor_define_supported(QemuConsole *con)
1328 {
1329     DisplayState *s = con->ds;
1330     struct DisplayChangeListener *dcl;
1331     QLIST_FOREACH(dcl, &s->listeners, next) {
1332         if (dcl->ops->dpy_cursor_define) {
1333             return true;
1334         }
1335     }
1336     return false;
1337 }
1338 
1339 /***********************************************************/
1340 /* register display */
1341 
1342 /* console.c internal use only */
1343 static DisplayState *get_alloc_displaystate(void)
1344 {
1345     if (!display_state) {
1346         display_state = g_new0(DisplayState, 1);
1347     }
1348     return display_state;
1349 }
1350 
1351 /*
1352  * Called by main(), after creating QemuConsoles
1353  * and before initializing ui (sdl/vnc/...).
1354  */
1355 DisplayState *init_displaystate(void)
1356 {
1357     int i;
1358 
1359     if (!display_state) {
1360         display_state = g_new0(DisplayState, 1);
1361     }
1362 
1363     for (i = 0; i < nb_consoles; i++) {
1364         if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
1365             consoles[i]->ds == NULL) {
1366             text_console_do_init(consoles[i]->chr, display_state);
1367         }
1368     }
1369 
1370     return display_state;
1371 }
1372 
1373 QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
1374                                   graphic_hw_invalidate_ptr invalidate,
1375                                   graphic_hw_screen_dump_ptr screen_dump,
1376                                   graphic_hw_text_update_ptr text_update,
1377                                   void *opaque)
1378 {
1379     int width = 640;
1380     int height = 480;
1381     QemuConsole *s;
1382     DisplayState *ds;
1383 
1384     ds = get_alloc_displaystate();
1385     trace_console_gfx_new();
1386     s = new_console(ds, GRAPHIC_CONSOLE);
1387     s->hw_update = update;
1388     s->hw_invalidate = invalidate;
1389     s->hw_screen_dump = screen_dump;
1390     s->hw_text_update = text_update;
1391     s->hw = opaque;
1392 
1393     if (!ds->surface) {
1394         ds->surface = qemu_create_displaysurface(width, height);
1395     }
1396     return s;
1397 }
1398 
1399 int is_graphic_console(void)
1400 {
1401     return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1402 }
1403 
1404 int is_fixedsize_console(void)
1405 {
1406     return active_console && active_console->console_type != TEXT_CONSOLE;
1407 }
1408 
1409 static void text_console_set_echo(CharDriverState *chr, bool echo)
1410 {
1411     QemuConsole *s = chr->opaque;
1412 
1413     s->echo = echo;
1414 }
1415 
1416 static void text_console_update_cursor(void *opaque)
1417 {
1418     QemuConsole *s = opaque;
1419 
1420     s->cursor_visible_phase = !s->cursor_visible_phase;
1421     graphic_hw_invalidate(s);
1422     qemu_mod_timer(s->cursor_timer,
1423                    qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1424 }
1425 
1426 static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
1427 {
1428     QemuConsole *s;
1429 
1430     s = chr->opaque;
1431 
1432     chr->chr_write = console_puts;
1433 
1434     s->out_fifo.buf = s->out_fifo_buf;
1435     s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1436     s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
1437     s->ds = ds;
1438 
1439     s->y_displayed = 0;
1440     s->y_base = 0;
1441     s->total_height = DEFAULT_BACKSCROLL;
1442     s->x = 0;
1443     s->y = 0;
1444     if (s->console_type == TEXT_CONSOLE) {
1445         s->g_width = surface_width(s->ds->surface);
1446         s->g_height = surface_height(s->ds->surface);
1447     }
1448 
1449     s->cursor_timer =
1450         qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
1451 
1452     s->hw_invalidate = text_console_invalidate;
1453     s->hw_text_update = text_console_update;
1454     s->hw = s;
1455 
1456     /* Set text attribute defaults */
1457     s->t_attrib_default.bold = 0;
1458     s->t_attrib_default.uline = 0;
1459     s->t_attrib_default.blink = 0;
1460     s->t_attrib_default.invers = 0;
1461     s->t_attrib_default.unvisible = 0;
1462     s->t_attrib_default.fgcol = COLOR_WHITE;
1463     s->t_attrib_default.bgcol = COLOR_BLACK;
1464     /* set current text attributes to default */
1465     s->t_attrib = s->t_attrib_default;
1466     text_console_resize(s);
1467 
1468     if (chr->label) {
1469         char msg[128];
1470         int len;
1471 
1472         s->t_attrib.bgcol = COLOR_BLUE;
1473         len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1474         console_puts(chr, (uint8_t*)msg, len);
1475         s->t_attrib = s->t_attrib_default;
1476     }
1477 
1478     qemu_chr_be_generic_open(chr);
1479     if (chr->init)
1480         chr->init(chr);
1481 }
1482 
1483 static CharDriverState *text_console_init(ChardevVC *vc)
1484 {
1485     CharDriverState *chr;
1486     QemuConsole *s;
1487     unsigned width = 0;
1488     unsigned height = 0;
1489 
1490     chr = g_malloc0(sizeof(CharDriverState));
1491 
1492     if (vc->has_width) {
1493         width = vc->width;
1494     } else if (vc->has_cols) {
1495         width = vc->cols * FONT_WIDTH;
1496     }
1497 
1498     if (vc->has_height) {
1499         height = vc->height;
1500     } else if (vc->has_rows) {
1501         height = vc->rows * FONT_HEIGHT;
1502     }
1503 
1504     trace_console_txt_new(width, height);
1505     if (width == 0 || height == 0) {
1506         s = new_console(NULL, TEXT_CONSOLE);
1507     } else {
1508         s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1509     }
1510 
1511     if (!s) {
1512         g_free(chr);
1513         return NULL;
1514     }
1515 
1516     s->chr = chr;
1517     s->g_width = width;
1518     s->g_height = height;
1519     chr->opaque = s;
1520     chr->chr_set_echo = text_console_set_echo;
1521 
1522     if (display_state) {
1523         text_console_do_init(chr, display_state);
1524     }
1525     return chr;
1526 }
1527 
1528 static VcHandler *vc_handler = text_console_init;
1529 
1530 CharDriverState *vc_init(ChardevVC *vc)
1531 {
1532     return vc_handler(vc);
1533 }
1534 
1535 void register_vc_handler(VcHandler *handler)
1536 {
1537     vc_handler = handler;
1538 }
1539 
1540 void qemu_console_resize(QemuConsole *s, int width, int height)
1541 {
1542     s->g_width = width;
1543     s->g_height = height;
1544     if (is_graphic_console()) {
1545         DisplaySurface *surface;
1546         surface = qemu_create_displaysurface(width, height);
1547         dpy_gfx_replace_surface(s, surface);
1548     }
1549 }
1550 
1551 void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
1552                        int dst_x, int dst_y, int w, int h)
1553 {
1554     if (is_graphic_console()) {
1555         dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
1556     }
1557 }
1558 
1559 DisplaySurface *qemu_console_surface(QemuConsole *console)
1560 {
1561     return console->ds->surface;
1562 }
1563 
1564 DisplayState *qemu_console_displaystate(QemuConsole *console)
1565 {
1566     return console->ds;
1567 }
1568 
1569 PixelFormat qemu_different_endianness_pixelformat(int bpp)
1570 {
1571     PixelFormat pf;
1572 
1573     memset(&pf, 0x00, sizeof(PixelFormat));
1574 
1575     pf.bits_per_pixel = bpp;
1576     pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1577     pf.depth = bpp == 32 ? 24 : bpp;
1578 
1579     switch (bpp) {
1580         case 24:
1581             pf.rmask = 0x000000FF;
1582             pf.gmask = 0x0000FF00;
1583             pf.bmask = 0x00FF0000;
1584             pf.rmax = 255;
1585             pf.gmax = 255;
1586             pf.bmax = 255;
1587             pf.rshift = 0;
1588             pf.gshift = 8;
1589             pf.bshift = 16;
1590             pf.rbits = 8;
1591             pf.gbits = 8;
1592             pf.bbits = 8;
1593             break;
1594         case 32:
1595             pf.rmask = 0x0000FF00;
1596             pf.gmask = 0x00FF0000;
1597             pf.bmask = 0xFF000000;
1598             pf.amask = 0x00000000;
1599             pf.amax = 255;
1600             pf.rmax = 255;
1601             pf.gmax = 255;
1602             pf.bmax = 255;
1603             pf.ashift = 0;
1604             pf.rshift = 8;
1605             pf.gshift = 16;
1606             pf.bshift = 24;
1607             pf.rbits = 8;
1608             pf.gbits = 8;
1609             pf.bbits = 8;
1610             pf.abits = 8;
1611             break;
1612         default:
1613             break;
1614     }
1615     return pf;
1616 }
1617 
1618 PixelFormat qemu_default_pixelformat(int bpp)
1619 {
1620     PixelFormat pf;
1621 
1622     memset(&pf, 0x00, sizeof(PixelFormat));
1623 
1624     pf.bits_per_pixel = bpp;
1625     pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1626     pf.depth = bpp == 32 ? 24 : bpp;
1627 
1628     switch (bpp) {
1629         case 15:
1630             pf.bits_per_pixel = 16;
1631             pf.rmask = 0x00007c00;
1632             pf.gmask = 0x000003E0;
1633             pf.bmask = 0x0000001F;
1634             pf.rmax = 31;
1635             pf.gmax = 31;
1636             pf.bmax = 31;
1637             pf.rshift = 10;
1638             pf.gshift = 5;
1639             pf.bshift = 0;
1640             pf.rbits = 5;
1641             pf.gbits = 5;
1642             pf.bbits = 5;
1643             break;
1644         case 16:
1645             pf.rmask = 0x0000F800;
1646             pf.gmask = 0x000007E0;
1647             pf.bmask = 0x0000001F;
1648             pf.rmax = 31;
1649             pf.gmax = 63;
1650             pf.bmax = 31;
1651             pf.rshift = 11;
1652             pf.gshift = 5;
1653             pf.bshift = 0;
1654             pf.rbits = 5;
1655             pf.gbits = 6;
1656             pf.bbits = 5;
1657             break;
1658         case 24:
1659             pf.rmask = 0x00FF0000;
1660             pf.gmask = 0x0000FF00;
1661             pf.bmask = 0x000000FF;
1662             pf.rmax = 255;
1663             pf.gmax = 255;
1664             pf.bmax = 255;
1665             pf.rshift = 16;
1666             pf.gshift = 8;
1667             pf.bshift = 0;
1668             pf.rbits = 8;
1669             pf.gbits = 8;
1670             pf.bbits = 8;
1671             break;
1672         case 32:
1673             pf.rmask = 0x00FF0000;
1674             pf.gmask = 0x0000FF00;
1675             pf.bmask = 0x000000FF;
1676             pf.rmax = 255;
1677             pf.gmax = 255;
1678             pf.bmax = 255;
1679             pf.rshift = 16;
1680             pf.gshift = 8;
1681             pf.bshift = 0;
1682             pf.rbits = 8;
1683             pf.gbits = 8;
1684             pf.bbits = 8;
1685             break;
1686         default:
1687             break;
1688     }
1689     return pf;
1690 }
1691 
1692 static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
1693                               Error **errp)
1694 {
1695     int val;
1696 
1697     backend->vc = g_new0(ChardevVC, 1);
1698 
1699     val = qemu_opt_get_number(opts, "width", 0);
1700     if (val != 0) {
1701         backend->vc->has_width = true;
1702         backend->vc->width = val;
1703     }
1704 
1705     val = qemu_opt_get_number(opts, "height", 0);
1706     if (val != 0) {
1707         backend->vc->has_height = true;
1708         backend->vc->height = val;
1709     }
1710 
1711     val = qemu_opt_get_number(opts, "cols", 0);
1712     if (val != 0) {
1713         backend->vc->has_cols = true;
1714         backend->vc->cols = val;
1715     }
1716 
1717     val = qemu_opt_get_number(opts, "rows", 0);
1718     if (val != 0) {
1719         backend->vc->has_rows = true;
1720         backend->vc->rows = val;
1721     }
1722 }
1723 
1724 static void register_types(void)
1725 {
1726     register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
1727                               qemu_chr_parse_vc);
1728 }
1729 
1730 type_init(register_types);
1731