xref: /openbmc/qemu/ui/console.c (revision 135b03cb)
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 
25 #include "qemu/osdep.h"
26 #include "ui/console.h"
27 #include "hw/qdev-core.h"
28 #include "qapi/error.h"
29 #include "qapi/qapi-commands-ui.h"
30 #include "qemu/module.h"
31 #include "qemu/option.h"
32 #include "qemu/timer.h"
33 #include "chardev/char-fe.h"
34 #include "trace.h"
35 #include "exec/memory.h"
36 
37 #define DEFAULT_BACKSCROLL 512
38 #define CONSOLE_CURSOR_PERIOD 500
39 
40 typedef struct TextAttributes {
41     uint8_t fgcol:4;
42     uint8_t bgcol:4;
43     uint8_t bold:1;
44     uint8_t uline:1;
45     uint8_t blink:1;
46     uint8_t invers:1;
47     uint8_t unvisible:1;
48 } TextAttributes;
49 
50 typedef struct TextCell {
51     uint8_t ch;
52     TextAttributes t_attrib;
53 } TextCell;
54 
55 #define MAX_ESC_PARAMS 3
56 
57 enum TTYState {
58     TTY_STATE_NORM,
59     TTY_STATE_ESC,
60     TTY_STATE_CSI,
61 };
62 
63 typedef struct QEMUFIFO {
64     uint8_t *buf;
65     int buf_size;
66     int count, wptr, rptr;
67 } QEMUFIFO;
68 
69 static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
70 {
71     int l, len;
72 
73     l = f->buf_size - f->count;
74     if (len1 > l)
75         len1 = l;
76     len = len1;
77     while (len > 0) {
78         l = f->buf_size - f->wptr;
79         if (l > len)
80             l = len;
81         memcpy(f->buf + f->wptr, buf, l);
82         f->wptr += l;
83         if (f->wptr >= f->buf_size)
84             f->wptr = 0;
85         buf += l;
86         len -= l;
87     }
88     f->count += len1;
89     return len1;
90 }
91 
92 static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
93 {
94     int l, len;
95 
96     if (len1 > f->count)
97         len1 = f->count;
98     len = len1;
99     while (len > 0) {
100         l = f->buf_size - f->rptr;
101         if (l > len)
102             l = len;
103         memcpy(buf, f->buf + f->rptr, l);
104         f->rptr += l;
105         if (f->rptr >= f->buf_size)
106             f->rptr = 0;
107         buf += l;
108         len -= l;
109     }
110     f->count -= len1;
111     return len1;
112 }
113 
114 typedef enum {
115     GRAPHIC_CONSOLE,
116     TEXT_CONSOLE,
117     TEXT_CONSOLE_FIXED_SIZE
118 } console_type_t;
119 
120 struct QemuConsole {
121     Object parent;
122 
123     int index;
124     console_type_t console_type;
125     DisplayState *ds;
126     DisplaySurface *surface;
127     int dcls;
128     DisplayChangeListener *gl;
129     bool gl_block;
130     int window_id;
131 
132     /* Graphic console state.  */
133     Object *device;
134     uint32_t head;
135     QemuUIInfo ui_info;
136     QEMUTimer *ui_timer;
137     const GraphicHwOps *hw_ops;
138     void *hw;
139 
140     /* Text console state */
141     int width;
142     int height;
143     int total_height;
144     int backscroll_height;
145     int x, y;
146     int x_saved, y_saved;
147     int y_displayed;
148     int y_base;
149     TextAttributes t_attrib_default; /* default text attributes */
150     TextAttributes t_attrib; /* currently active text attributes */
151     TextCell *cells;
152     int text_x[2], text_y[2], cursor_invalidate;
153     int echo;
154 
155     int update_x0;
156     int update_y0;
157     int update_x1;
158     int update_y1;
159 
160     enum TTYState state;
161     int esc_params[MAX_ESC_PARAMS];
162     int nb_esc_params;
163 
164     Chardev *chr;
165     /* fifo for key pressed */
166     QEMUFIFO out_fifo;
167     uint8_t out_fifo_buf[16];
168     QEMUTimer *kbd_timer;
169 
170     QTAILQ_ENTRY(QemuConsole) next;
171 };
172 
173 struct DisplayState {
174     QEMUTimer *gui_timer;
175     uint64_t last_update;
176     uint64_t update_interval;
177     bool refreshing;
178     bool have_gfx;
179     bool have_text;
180 
181     QLIST_HEAD(, DisplayChangeListener) listeners;
182 };
183 
184 static DisplayState *display_state;
185 static QemuConsole *active_console;
186 static QTAILQ_HEAD(, QemuConsole) consoles =
187     QTAILQ_HEAD_INITIALIZER(consoles);
188 static bool cursor_visible_phase;
189 static QEMUTimer *cursor_timer;
190 
191 static void text_console_do_init(Chardev *chr, DisplayState *ds);
192 static void dpy_refresh(DisplayState *s);
193 static DisplayState *get_alloc_displaystate(void);
194 static void text_console_update_cursor_timer(void);
195 static void text_console_update_cursor(void *opaque);
196 
197 static void gui_update(void *opaque)
198 {
199     uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
200     uint64_t dcl_interval;
201     DisplayState *ds = opaque;
202     DisplayChangeListener *dcl;
203     QemuConsole *con;
204 
205     ds->refreshing = true;
206     dpy_refresh(ds);
207     ds->refreshing = false;
208 
209     QLIST_FOREACH(dcl, &ds->listeners, next) {
210         dcl_interval = dcl->update_interval ?
211             dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
212         if (interval > dcl_interval) {
213             interval = dcl_interval;
214         }
215     }
216     if (ds->update_interval != interval) {
217         ds->update_interval = interval;
218         QTAILQ_FOREACH(con, &consoles, next) {
219             if (con->hw_ops->update_interval) {
220                 con->hw_ops->update_interval(con->hw, interval);
221             }
222         }
223         trace_console_refresh(interval);
224     }
225     ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
226     timer_mod(ds->gui_timer, ds->last_update + interval);
227 }
228 
229 static void gui_setup_refresh(DisplayState *ds)
230 {
231     DisplayChangeListener *dcl;
232     bool need_timer = false;
233     bool have_gfx = false;
234     bool have_text = false;
235 
236     QLIST_FOREACH(dcl, &ds->listeners, next) {
237         if (dcl->ops->dpy_refresh != NULL) {
238             need_timer = true;
239         }
240         if (dcl->ops->dpy_gfx_update != NULL) {
241             have_gfx = true;
242         }
243         if (dcl->ops->dpy_text_update != NULL) {
244             have_text = true;
245         }
246     }
247 
248     if (need_timer && ds->gui_timer == NULL) {
249         ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
250         timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
251     }
252     if (!need_timer && ds->gui_timer != NULL) {
253         timer_del(ds->gui_timer);
254         timer_free(ds->gui_timer);
255         ds->gui_timer = NULL;
256     }
257 
258     ds->have_gfx = have_gfx;
259     ds->have_text = have_text;
260 }
261 
262 void graphic_hw_update(QemuConsole *con)
263 {
264     if (!con) {
265         con = active_console;
266     }
267     if (con && con->hw_ops->gfx_update) {
268         con->hw_ops->gfx_update(con->hw);
269     }
270 }
271 
272 void graphic_hw_gl_block(QemuConsole *con, bool block)
273 {
274     assert(con != NULL);
275 
276     con->gl_block = block;
277     if (con->hw_ops->gl_block) {
278         con->hw_ops->gl_block(con->hw, block);
279     }
280 }
281 
282 int qemu_console_get_window_id(QemuConsole *con)
283 {
284     return con->window_id;
285 }
286 
287 void qemu_console_set_window_id(QemuConsole *con, int window_id)
288 {
289     con->window_id = window_id;
290 }
291 
292 void graphic_hw_invalidate(QemuConsole *con)
293 {
294     if (!con) {
295         con = active_console;
296     }
297     if (con && con->hw_ops->invalidate) {
298         con->hw_ops->invalidate(con->hw);
299     }
300 }
301 
302 static void ppm_save(const char *filename, DisplaySurface *ds,
303                      Error **errp)
304 {
305     int width = pixman_image_get_width(ds->image);
306     int height = pixman_image_get_height(ds->image);
307     int fd;
308     FILE *f;
309     int y;
310     int ret;
311     pixman_image_t *linebuf;
312 
313     trace_ppm_save(filename, ds);
314     fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
315     if (fd == -1) {
316         error_setg(errp, "failed to open file '%s': %s", filename,
317                    strerror(errno));
318         return;
319     }
320     f = fdopen(fd, "wb");
321     ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
322     if (ret < 0) {
323         linebuf = NULL;
324         goto write_err;
325     }
326     linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
327     for (y = 0; y < height; y++) {
328         qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
329         clearerr(f);
330         ret = fwrite(pixman_image_get_data(linebuf), 1,
331                      pixman_image_get_stride(linebuf), f);
332         (void)ret;
333         if (ferror(f)) {
334             goto write_err;
335         }
336     }
337 
338 out:
339     qemu_pixman_image_unref(linebuf);
340     fclose(f);
341     return;
342 
343 write_err:
344     error_setg(errp, "failed to write to file '%s': %s", filename,
345                strerror(errno));
346     unlink(filename);
347     goto out;
348 }
349 
350 void qmp_screendump(const char *filename, bool has_device, const char *device,
351                     bool has_head, int64_t head, Error **errp)
352 {
353     QemuConsole *con;
354     DisplaySurface *surface;
355 
356     if (has_device) {
357         con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
358                                                  errp);
359         if (!con) {
360             return;
361         }
362     } else {
363         if (has_head) {
364             error_setg(errp, "'head' must be specified together with 'device'");
365             return;
366         }
367         con = qemu_console_lookup_by_index(0);
368         if (!con) {
369             error_setg(errp, "There is no console to take a screendump from");
370             return;
371         }
372     }
373 
374     graphic_hw_update(con);
375     surface = qemu_console_surface(con);
376     if (!surface) {
377         error_setg(errp, "no surface");
378         return;
379     }
380 
381     ppm_save(filename, surface, errp);
382 }
383 
384 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
385 {
386     if (!con) {
387         con = active_console;
388     }
389     if (con && con->hw_ops->text_update) {
390         con->hw_ops->text_update(con->hw, chardata);
391     }
392 }
393 
394 static void vga_fill_rect(QemuConsole *con,
395                           int posx, int posy, int width, int height,
396                           pixman_color_t color)
397 {
398     DisplaySurface *surface = qemu_console_surface(con);
399     pixman_rectangle16_t rect = {
400         .x = posx, .y = posy, .width = width, .height = height
401     };
402 
403     pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
404                                  &color, 1, &rect);
405 }
406 
407 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
408 static void vga_bitblt(QemuConsole *con,
409                        int xs, int ys, int xd, int yd, int w, int h)
410 {
411     DisplaySurface *surface = qemu_console_surface(con);
412 
413     pixman_image_composite(PIXMAN_OP_SRC,
414                            surface->image, NULL, surface->image,
415                            xs, ys, 0, 0, xd, yd, w, h);
416 }
417 
418 /***********************************************************/
419 /* basic char display */
420 
421 #define FONT_HEIGHT 16
422 #define FONT_WIDTH 8
423 
424 #include "vgafont.h"
425 
426 #define QEMU_RGB(r, g, b)                                               \
427     { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
428 
429 static const pixman_color_t color_table_rgb[2][8] = {
430     {   /* dark */
431         [QEMU_COLOR_BLACK]   = QEMU_RGB(0x00, 0x00, 0x00),  /* black */
432         [QEMU_COLOR_BLUE]    = QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
433         [QEMU_COLOR_GREEN]   = QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
434         [QEMU_COLOR_CYAN]    = QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
435         [QEMU_COLOR_RED]     = QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
436         [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
437         [QEMU_COLOR_YELLOW]  = QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
438         [QEMU_COLOR_WHITE]   = QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
439     },
440     {   /* bright */
441         [QEMU_COLOR_BLACK]   = QEMU_RGB(0x00, 0x00, 0x00),  /* black */
442         [QEMU_COLOR_BLUE]    = QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
443         [QEMU_COLOR_GREEN]   = QEMU_RGB(0x00, 0xff, 0x00),  /* green */
444         [QEMU_COLOR_CYAN]    = QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
445         [QEMU_COLOR_RED]     = QEMU_RGB(0xff, 0x00, 0x00),  /* red */
446         [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
447         [QEMU_COLOR_YELLOW]  = QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
448         [QEMU_COLOR_WHITE]   = QEMU_RGB(0xff, 0xff, 0xff),  /* white */
449     }
450 };
451 
452 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
453                           TextAttributes *t_attrib)
454 {
455     static pixman_image_t *glyphs[256];
456     DisplaySurface *surface = qemu_console_surface(s);
457     pixman_color_t fgcol, bgcol;
458 
459     if (t_attrib->invers) {
460         bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
461         fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
462     } else {
463         fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
464         bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
465     }
466 
467     if (!glyphs[ch]) {
468         glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
469     }
470     qemu_pixman_glyph_render(glyphs[ch], surface->image,
471                              &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
472 }
473 
474 static void text_console_resize(QemuConsole *s)
475 {
476     TextCell *cells, *c, *c1;
477     int w1, x, y, last_width;
478 
479     last_width = s->width;
480     s->width = surface_width(s->surface) / FONT_WIDTH;
481     s->height = surface_height(s->surface) / FONT_HEIGHT;
482 
483     w1 = last_width;
484     if (s->width < w1)
485         w1 = s->width;
486 
487     cells = g_new(TextCell, s->width * s->total_height + 1);
488     for(y = 0; y < s->total_height; y++) {
489         c = &cells[y * s->width];
490         if (w1 > 0) {
491             c1 = &s->cells[y * last_width];
492             for(x = 0; x < w1; x++) {
493                 *c++ = *c1++;
494             }
495         }
496         for(x = w1; x < s->width; x++) {
497             c->ch = ' ';
498             c->t_attrib = s->t_attrib_default;
499             c++;
500         }
501     }
502     g_free(s->cells);
503     s->cells = cells;
504 }
505 
506 static inline void text_update_xy(QemuConsole *s, int x, int y)
507 {
508     s->text_x[0] = MIN(s->text_x[0], x);
509     s->text_x[1] = MAX(s->text_x[1], x);
510     s->text_y[0] = MIN(s->text_y[0], y);
511     s->text_y[1] = MAX(s->text_y[1], y);
512 }
513 
514 static void invalidate_xy(QemuConsole *s, int x, int y)
515 {
516     if (!qemu_console_is_visible(s)) {
517         return;
518     }
519     if (s->update_x0 > x * FONT_WIDTH)
520         s->update_x0 = x * FONT_WIDTH;
521     if (s->update_y0 > y * FONT_HEIGHT)
522         s->update_y0 = y * FONT_HEIGHT;
523     if (s->update_x1 < (x + 1) * FONT_WIDTH)
524         s->update_x1 = (x + 1) * FONT_WIDTH;
525     if (s->update_y1 < (y + 1) * FONT_HEIGHT)
526         s->update_y1 = (y + 1) * FONT_HEIGHT;
527 }
528 
529 static void update_xy(QemuConsole *s, int x, int y)
530 {
531     TextCell *c;
532     int y1, y2;
533 
534     if (s->ds->have_text) {
535         text_update_xy(s, x, y);
536     }
537 
538     y1 = (s->y_base + y) % s->total_height;
539     y2 = y1 - s->y_displayed;
540     if (y2 < 0) {
541         y2 += s->total_height;
542     }
543     if (y2 < s->height) {
544         if (x >= s->width) {
545             x = s->width - 1;
546         }
547         c = &s->cells[y1 * s->width + x];
548         vga_putcharxy(s, x, y2, c->ch,
549                       &(c->t_attrib));
550         invalidate_xy(s, x, y2);
551     }
552 }
553 
554 static void console_show_cursor(QemuConsole *s, int show)
555 {
556     TextCell *c;
557     int y, y1;
558     int x = s->x;
559 
560     if (s->ds->have_text) {
561         s->cursor_invalidate = 1;
562     }
563 
564     if (x >= s->width) {
565         x = s->width - 1;
566     }
567     y1 = (s->y_base + s->y) % s->total_height;
568     y = y1 - s->y_displayed;
569     if (y < 0) {
570         y += s->total_height;
571     }
572     if (y < s->height) {
573         c = &s->cells[y1 * s->width + x];
574         if (show && cursor_visible_phase) {
575             TextAttributes t_attrib = s->t_attrib_default;
576             t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
577             vga_putcharxy(s, x, y, c->ch, &t_attrib);
578         } else {
579             vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
580         }
581         invalidate_xy(s, x, y);
582     }
583 }
584 
585 static void console_refresh(QemuConsole *s)
586 {
587     DisplaySurface *surface = qemu_console_surface(s);
588     TextCell *c;
589     int x, y, y1;
590 
591     if (s->ds->have_text) {
592         s->text_x[0] = 0;
593         s->text_y[0] = 0;
594         s->text_x[1] = s->width - 1;
595         s->text_y[1] = s->height - 1;
596         s->cursor_invalidate = 1;
597     }
598 
599     vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
600                   color_table_rgb[0][QEMU_COLOR_BLACK]);
601     y1 = s->y_displayed;
602     for (y = 0; y < s->height; y++) {
603         c = s->cells + y1 * s->width;
604         for (x = 0; x < s->width; x++) {
605             vga_putcharxy(s, x, y, c->ch,
606                           &(c->t_attrib));
607             c++;
608         }
609         if (++y1 == s->total_height) {
610             y1 = 0;
611         }
612     }
613     console_show_cursor(s, 1);
614     dpy_gfx_update(s, 0, 0,
615                    surface_width(surface), surface_height(surface));
616 }
617 
618 static void console_scroll(QemuConsole *s, int ydelta)
619 {
620     int i, y1;
621 
622     if (ydelta > 0) {
623         for(i = 0; i < ydelta; i++) {
624             if (s->y_displayed == s->y_base)
625                 break;
626             if (++s->y_displayed == s->total_height)
627                 s->y_displayed = 0;
628         }
629     } else {
630         ydelta = -ydelta;
631         i = s->backscroll_height;
632         if (i > s->total_height - s->height)
633             i = s->total_height - s->height;
634         y1 = s->y_base - i;
635         if (y1 < 0)
636             y1 += s->total_height;
637         for(i = 0; i < ydelta; i++) {
638             if (s->y_displayed == y1)
639                 break;
640             if (--s->y_displayed < 0)
641                 s->y_displayed = s->total_height - 1;
642         }
643     }
644     console_refresh(s);
645 }
646 
647 static void console_put_lf(QemuConsole *s)
648 {
649     TextCell *c;
650     int x, y1;
651 
652     s->y++;
653     if (s->y >= s->height) {
654         s->y = s->height - 1;
655 
656         if (s->y_displayed == s->y_base) {
657             if (++s->y_displayed == s->total_height)
658                 s->y_displayed = 0;
659         }
660         if (++s->y_base == s->total_height)
661             s->y_base = 0;
662         if (s->backscroll_height < s->total_height)
663             s->backscroll_height++;
664         y1 = (s->y_base + s->height - 1) % s->total_height;
665         c = &s->cells[y1 * s->width];
666         for(x = 0; x < s->width; x++) {
667             c->ch = ' ';
668             c->t_attrib = s->t_attrib_default;
669             c++;
670         }
671         if (s->y_displayed == s->y_base) {
672             if (s->ds->have_text) {
673                 s->text_x[0] = 0;
674                 s->text_y[0] = 0;
675                 s->text_x[1] = s->width - 1;
676                 s->text_y[1] = s->height - 1;
677             }
678 
679             vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
680                        s->width * FONT_WIDTH,
681                        (s->height - 1) * FONT_HEIGHT);
682             vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
683                           s->width * FONT_WIDTH, FONT_HEIGHT,
684                           color_table_rgb[0][s->t_attrib_default.bgcol]);
685             s->update_x0 = 0;
686             s->update_y0 = 0;
687             s->update_x1 = s->width * FONT_WIDTH;
688             s->update_y1 = s->height * FONT_HEIGHT;
689         }
690     }
691 }
692 
693 /* Set console attributes depending on the current escape codes.
694  * NOTE: I know this code is not very efficient (checking every color for it
695  * self) but it is more readable and better maintainable.
696  */
697 static void console_handle_escape(QemuConsole *s)
698 {
699     int i;
700 
701     for (i=0; i<s->nb_esc_params; i++) {
702         switch (s->esc_params[i]) {
703             case 0: /* reset all console attributes to default */
704                 s->t_attrib = s->t_attrib_default;
705                 break;
706             case 1:
707                 s->t_attrib.bold = 1;
708                 break;
709             case 4:
710                 s->t_attrib.uline = 1;
711                 break;
712             case 5:
713                 s->t_attrib.blink = 1;
714                 break;
715             case 7:
716                 s->t_attrib.invers = 1;
717                 break;
718             case 8:
719                 s->t_attrib.unvisible = 1;
720                 break;
721             case 22:
722                 s->t_attrib.bold = 0;
723                 break;
724             case 24:
725                 s->t_attrib.uline = 0;
726                 break;
727             case 25:
728                 s->t_attrib.blink = 0;
729                 break;
730             case 27:
731                 s->t_attrib.invers = 0;
732                 break;
733             case 28:
734                 s->t_attrib.unvisible = 0;
735                 break;
736             /* set foreground color */
737             case 30:
738                 s->t_attrib.fgcol = QEMU_COLOR_BLACK;
739                 break;
740             case 31:
741                 s->t_attrib.fgcol = QEMU_COLOR_RED;
742                 break;
743             case 32:
744                 s->t_attrib.fgcol = QEMU_COLOR_GREEN;
745                 break;
746             case 33:
747                 s->t_attrib.fgcol = QEMU_COLOR_YELLOW;
748                 break;
749             case 34:
750                 s->t_attrib.fgcol = QEMU_COLOR_BLUE;
751                 break;
752             case 35:
753                 s->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
754                 break;
755             case 36:
756                 s->t_attrib.fgcol = QEMU_COLOR_CYAN;
757                 break;
758             case 37:
759                 s->t_attrib.fgcol = QEMU_COLOR_WHITE;
760                 break;
761             /* set background color */
762             case 40:
763                 s->t_attrib.bgcol = QEMU_COLOR_BLACK;
764                 break;
765             case 41:
766                 s->t_attrib.bgcol = QEMU_COLOR_RED;
767                 break;
768             case 42:
769                 s->t_attrib.bgcol = QEMU_COLOR_GREEN;
770                 break;
771             case 43:
772                 s->t_attrib.bgcol = QEMU_COLOR_YELLOW;
773                 break;
774             case 44:
775                 s->t_attrib.bgcol = QEMU_COLOR_BLUE;
776                 break;
777             case 45:
778                 s->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
779                 break;
780             case 46:
781                 s->t_attrib.bgcol = QEMU_COLOR_CYAN;
782                 break;
783             case 47:
784                 s->t_attrib.bgcol = QEMU_COLOR_WHITE;
785                 break;
786         }
787     }
788 }
789 
790 static void console_clear_xy(QemuConsole *s, int x, int y)
791 {
792     int y1 = (s->y_base + y) % s->total_height;
793     if (x >= s->width) {
794         x = s->width - 1;
795     }
796     TextCell *c = &s->cells[y1 * s->width + x];
797     c->ch = ' ';
798     c->t_attrib = s->t_attrib_default;
799     update_xy(s, x, y);
800 }
801 
802 static void console_put_one(QemuConsole *s, int ch)
803 {
804     TextCell *c;
805     int y1;
806     if (s->x >= s->width) {
807         /* line wrap */
808         s->x = 0;
809         console_put_lf(s);
810     }
811     y1 = (s->y_base + s->y) % s->total_height;
812     c = &s->cells[y1 * s->width + s->x];
813     c->ch = ch;
814     c->t_attrib = s->t_attrib;
815     update_xy(s, s->x, s->y);
816     s->x++;
817 }
818 
819 static void console_respond_str(QemuConsole *s, const char *buf)
820 {
821     while (*buf) {
822         console_put_one(s, *buf);
823         buf++;
824     }
825 }
826 
827 /* set cursor, checking bounds */
828 static void set_cursor(QemuConsole *s, int x, int y)
829 {
830     if (x < 0) {
831         x = 0;
832     }
833     if (y < 0) {
834         y = 0;
835     }
836     if (y >= s->height) {
837         y = s->height - 1;
838     }
839     if (x >= s->width) {
840         x = s->width - 1;
841     }
842 
843     s->x = x;
844     s->y = y;
845 }
846 
847 static void console_putchar(QemuConsole *s, int ch)
848 {
849     int i;
850     int x, y;
851     char response[40];
852 
853     switch(s->state) {
854     case TTY_STATE_NORM:
855         switch(ch) {
856         case '\r':  /* carriage return */
857             s->x = 0;
858             break;
859         case '\n':  /* newline */
860             console_put_lf(s);
861             break;
862         case '\b':  /* backspace */
863             if (s->x > 0)
864                 s->x--;
865             break;
866         case '\t':  /* tabspace */
867             if (s->x + (8 - (s->x % 8)) > s->width) {
868                 s->x = 0;
869                 console_put_lf(s);
870             } else {
871                 s->x = s->x + (8 - (s->x % 8));
872             }
873             break;
874         case '\a':  /* alert aka. bell */
875             /* TODO: has to be implemented */
876             break;
877         case 14:
878             /* SI (shift in), character set 0 (ignored) */
879             break;
880         case 15:
881             /* SO (shift out), character set 1 (ignored) */
882             break;
883         case 27:    /* esc (introducing an escape sequence) */
884             s->state = TTY_STATE_ESC;
885             break;
886         default:
887             console_put_one(s, ch);
888             break;
889         }
890         break;
891     case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
892         if (ch == '[') {
893             for(i=0;i<MAX_ESC_PARAMS;i++)
894                 s->esc_params[i] = 0;
895             s->nb_esc_params = 0;
896             s->state = TTY_STATE_CSI;
897         } else {
898             s->state = TTY_STATE_NORM;
899         }
900         break;
901     case TTY_STATE_CSI: /* handle escape sequence parameters */
902         if (ch >= '0' && ch <= '9') {
903             if (s->nb_esc_params < MAX_ESC_PARAMS) {
904                 int *param = &s->esc_params[s->nb_esc_params];
905                 int digit = (ch - '0');
906 
907                 *param = (*param <= (INT_MAX - digit) / 10) ?
908                          *param * 10 + digit : INT_MAX;
909             }
910         } else {
911             if (s->nb_esc_params < MAX_ESC_PARAMS)
912                 s->nb_esc_params++;
913             if (ch == ';' || ch == '?') {
914                 break;
915             }
916             trace_console_putchar_csi(s->esc_params[0], s->esc_params[1],
917                                       ch, s->nb_esc_params);
918             s->state = TTY_STATE_NORM;
919             switch(ch) {
920             case 'A':
921                 /* move cursor up */
922                 if (s->esc_params[0] == 0) {
923                     s->esc_params[0] = 1;
924                 }
925                 set_cursor(s, s->x, s->y - s->esc_params[0]);
926                 break;
927             case 'B':
928                 /* move cursor down */
929                 if (s->esc_params[0] == 0) {
930                     s->esc_params[0] = 1;
931                 }
932                 set_cursor(s, s->x, s->y + s->esc_params[0]);
933                 break;
934             case 'C':
935                 /* move cursor right */
936                 if (s->esc_params[0] == 0) {
937                     s->esc_params[0] = 1;
938                 }
939                 set_cursor(s, s->x + s->esc_params[0], s->y);
940                 break;
941             case 'D':
942                 /* move cursor left */
943                 if (s->esc_params[0] == 0) {
944                     s->esc_params[0] = 1;
945                 }
946                 set_cursor(s, s->x - s->esc_params[0], s->y);
947                 break;
948             case 'G':
949                 /* move cursor to column */
950                 set_cursor(s, s->esc_params[0] - 1, s->y);
951                 break;
952             case 'f':
953             case 'H':
954                 /* move cursor to row, column */
955                 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
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                 break;
991             case 'K':
992                 switch (s->esc_params[0]) {
993                 case 0:
994                     /* clear to eol */
995                     for(x = s->x; x < s->width; x++) {
996                         console_clear_xy(s, x, s->y);
997                     }
998                     break;
999                 case 1:
1000                     /* clear from beginning of line */
1001                     for (x = 0; x <= s->x && x < s->width; x++) {
1002                         console_clear_xy(s, x, s->y);
1003                     }
1004                     break;
1005                 case 2:
1006                     /* clear entire line */
1007                     for(x = 0; x < s->width; x++) {
1008                         console_clear_xy(s, x, s->y);
1009                     }
1010                     break;
1011                 }
1012                 break;
1013             case 'm':
1014                 console_handle_escape(s);
1015                 break;
1016             case 'n':
1017                 switch (s->esc_params[0]) {
1018                 case 5:
1019                     /* report console status (always succeed)*/
1020                     console_respond_str(s, "\033[0n");
1021                     break;
1022                 case 6:
1023                     /* report cursor position */
1024                     sprintf(response, "\033[%d;%dR",
1025                            (s->y_base + s->y) % s->total_height + 1,
1026                             s->x + 1);
1027                     console_respond_str(s, response);
1028                     break;
1029                 }
1030                 break;
1031             case 's':
1032                 /* save cursor position */
1033                 s->x_saved = s->x;
1034                 s->y_saved = s->y;
1035                 break;
1036             case 'u':
1037                 /* restore cursor position */
1038                 s->x = s->x_saved;
1039                 s->y = s->y_saved;
1040                 break;
1041             default:
1042                 trace_console_putchar_unhandled(ch);
1043                 break;
1044             }
1045             break;
1046         }
1047     }
1048 }
1049 
1050 void console_select(unsigned int index)
1051 {
1052     DisplayChangeListener *dcl;
1053     QemuConsole *s;
1054 
1055     trace_console_select(index);
1056     s = qemu_console_lookup_by_index(index);
1057     if (s) {
1058         DisplayState *ds = s->ds;
1059 
1060         active_console = s;
1061         if (ds->have_gfx) {
1062             QLIST_FOREACH(dcl, &ds->listeners, next) {
1063                 if (dcl->con != NULL) {
1064                     continue;
1065                 }
1066                 if (dcl->ops->dpy_gfx_switch) {
1067                     dcl->ops->dpy_gfx_switch(dcl, s->surface);
1068                 }
1069             }
1070             if (s->surface) {
1071                 dpy_gfx_update(s, 0, 0, surface_width(s->surface),
1072                                surface_height(s->surface));
1073             }
1074         }
1075         if (ds->have_text) {
1076             dpy_text_resize(s, s->width, s->height);
1077         }
1078         text_console_update_cursor(NULL);
1079     }
1080 }
1081 
1082 typedef struct VCChardev {
1083     Chardev parent;
1084     QemuConsole *console;
1085 } VCChardev;
1086 
1087 #define TYPE_CHARDEV_VC "chardev-vc"
1088 #define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
1089 
1090 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1091 {
1092     VCChardev *drv = VC_CHARDEV(chr);
1093     QemuConsole *s = drv->console;
1094     int i;
1095 
1096     if (!s->ds) {
1097         return 0;
1098     }
1099 
1100     s->update_x0 = s->width * FONT_WIDTH;
1101     s->update_y0 = s->height * FONT_HEIGHT;
1102     s->update_x1 = 0;
1103     s->update_y1 = 0;
1104     console_show_cursor(s, 0);
1105     for(i = 0; i < len; i++) {
1106         console_putchar(s, buf[i]);
1107     }
1108     console_show_cursor(s, 1);
1109     if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
1110         dpy_gfx_update(s, s->update_x0, s->update_y0,
1111                        s->update_x1 - s->update_x0,
1112                        s->update_y1 - s->update_y0);
1113     }
1114     return len;
1115 }
1116 
1117 static void kbd_send_chars(void *opaque)
1118 {
1119     QemuConsole *s = opaque;
1120     int len;
1121     uint8_t buf[16];
1122 
1123     len = qemu_chr_be_can_write(s->chr);
1124     if (len > s->out_fifo.count)
1125         len = s->out_fifo.count;
1126     if (len > 0) {
1127         if (len > sizeof(buf))
1128             len = sizeof(buf);
1129         qemu_fifo_read(&s->out_fifo, buf, len);
1130         qemu_chr_be_write(s->chr, buf, len);
1131     }
1132     /* characters are pending: we send them a bit later (XXX:
1133        horrible, should change char device API) */
1134     if (s->out_fifo.count > 0) {
1135         timer_mod(s->kbd_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1);
1136     }
1137 }
1138 
1139 /* called when an ascii key is pressed */
1140 void kbd_put_keysym_console(QemuConsole *s, int keysym)
1141 {
1142     uint8_t buf[16], *q;
1143     CharBackend *be;
1144     int c;
1145 
1146     if (!s || (s->console_type == GRAPHIC_CONSOLE))
1147         return;
1148 
1149     switch(keysym) {
1150     case QEMU_KEY_CTRL_UP:
1151         console_scroll(s, -1);
1152         break;
1153     case QEMU_KEY_CTRL_DOWN:
1154         console_scroll(s, 1);
1155         break;
1156     case QEMU_KEY_CTRL_PAGEUP:
1157         console_scroll(s, -10);
1158         break;
1159     case QEMU_KEY_CTRL_PAGEDOWN:
1160         console_scroll(s, 10);
1161         break;
1162     default:
1163         /* convert the QEMU keysym to VT100 key string */
1164         q = buf;
1165         if (keysym >= 0xe100 && keysym <= 0xe11f) {
1166             *q++ = '\033';
1167             *q++ = '[';
1168             c = keysym - 0xe100;
1169             if (c >= 10)
1170                 *q++ = '0' + (c / 10);
1171             *q++ = '0' + (c % 10);
1172             *q++ = '~';
1173         } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1174             *q++ = '\033';
1175             *q++ = '[';
1176             *q++ = keysym & 0xff;
1177         } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1178             vc_chr_write(s->chr, (const uint8_t *) "\r", 1);
1179             *q++ = '\n';
1180         } else {
1181             *q++ = keysym;
1182         }
1183         if (s->echo) {
1184             vc_chr_write(s->chr, buf, q - buf);
1185         }
1186         be = s->chr->be;
1187         if (be && be->chr_read) {
1188             qemu_fifo_write(&s->out_fifo, buf, q - buf);
1189             kbd_send_chars(s);
1190         }
1191         break;
1192     }
1193 }
1194 
1195 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
1196     [Q_KEY_CODE_UP]     = QEMU_KEY_UP,
1197     [Q_KEY_CODE_DOWN]   = QEMU_KEY_DOWN,
1198     [Q_KEY_CODE_RIGHT]  = QEMU_KEY_RIGHT,
1199     [Q_KEY_CODE_LEFT]   = QEMU_KEY_LEFT,
1200     [Q_KEY_CODE_HOME]   = QEMU_KEY_HOME,
1201     [Q_KEY_CODE_END]    = QEMU_KEY_END,
1202     [Q_KEY_CODE_PGUP]   = QEMU_KEY_PAGEUP,
1203     [Q_KEY_CODE_PGDN]   = QEMU_KEY_PAGEDOWN,
1204     [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
1205     [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
1206 };
1207 
1208 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
1209     [Q_KEY_CODE_UP]     = QEMU_KEY_CTRL_UP,
1210     [Q_KEY_CODE_DOWN]   = QEMU_KEY_CTRL_DOWN,
1211     [Q_KEY_CODE_RIGHT]  = QEMU_KEY_CTRL_RIGHT,
1212     [Q_KEY_CODE_LEFT]   = QEMU_KEY_CTRL_LEFT,
1213     [Q_KEY_CODE_HOME]   = QEMU_KEY_CTRL_HOME,
1214     [Q_KEY_CODE_END]    = QEMU_KEY_CTRL_END,
1215     [Q_KEY_CODE_PGUP]   = QEMU_KEY_CTRL_PAGEUP,
1216     [Q_KEY_CODE_PGDN]   = QEMU_KEY_CTRL_PAGEDOWN,
1217 };
1218 
1219 bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
1220 {
1221     int keysym;
1222 
1223     keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
1224     if (keysym == 0) {
1225         return false;
1226     }
1227     kbd_put_keysym_console(s, keysym);
1228     return true;
1229 }
1230 
1231 void kbd_put_string_console(QemuConsole *s, const char *str, int len)
1232 {
1233     int i;
1234 
1235     for (i = 0; i < len && str[i]; i++) {
1236         kbd_put_keysym_console(s, str[i]);
1237     }
1238 }
1239 
1240 void kbd_put_keysym(int keysym)
1241 {
1242     kbd_put_keysym_console(active_console, keysym);
1243 }
1244 
1245 static void text_console_invalidate(void *opaque)
1246 {
1247     QemuConsole *s = (QemuConsole *) opaque;
1248 
1249     if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1250         text_console_resize(s);
1251     }
1252     console_refresh(s);
1253 }
1254 
1255 static void text_console_update(void *opaque, console_ch_t *chardata)
1256 {
1257     QemuConsole *s = (QemuConsole *) opaque;
1258     int i, j, src;
1259 
1260     if (s->text_x[0] <= s->text_x[1]) {
1261         src = (s->y_base + s->text_y[0]) * s->width;
1262         chardata += s->text_y[0] * s->width;
1263         for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1264             for (j = 0; j < s->width; j++, src++) {
1265                 console_write_ch(chardata ++,
1266                                  ATTR2CHTYPE(s->cells[src].ch,
1267                                              s->cells[src].t_attrib.fgcol,
1268                                              s->cells[src].t_attrib.bgcol,
1269                                              s->cells[src].t_attrib.bold));
1270             }
1271         dpy_text_update(s, s->text_x[0], s->text_y[0],
1272                         s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1273         s->text_x[0] = s->width;
1274         s->text_y[0] = s->height;
1275         s->text_x[1] = 0;
1276         s->text_y[1] = 0;
1277     }
1278     if (s->cursor_invalidate) {
1279         dpy_text_cursor(s, s->x, s->y);
1280         s->cursor_invalidate = 0;
1281     }
1282 }
1283 
1284 static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
1285                                 uint32_t head)
1286 {
1287     Object *obj;
1288     QemuConsole *s;
1289     int i;
1290 
1291     obj = object_new(TYPE_QEMU_CONSOLE);
1292     s = QEMU_CONSOLE(obj);
1293     s->head = head;
1294     object_property_add_link(obj, "device", TYPE_DEVICE,
1295                              (Object **)&s->device,
1296                              object_property_allow_set_link,
1297                              OBJ_PROP_LINK_STRONG,
1298                              &error_abort);
1299     object_property_add_uint32_ptr(obj, "head",
1300                                    &s->head, &error_abort);
1301 
1302     if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1303         (console_type == GRAPHIC_CONSOLE))) {
1304         active_console = s;
1305     }
1306     s->ds = ds;
1307     s->console_type = console_type;
1308 
1309     if (QTAILQ_EMPTY(&consoles)) {
1310         s->index = 0;
1311         QTAILQ_INSERT_TAIL(&consoles, s, next);
1312     } else if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) {
1313         QemuConsole *last = QTAILQ_LAST(&consoles);
1314         s->index = last->index + 1;
1315         QTAILQ_INSERT_TAIL(&consoles, s, next);
1316     } else {
1317         /*
1318          * HACK: Put graphical consoles before text consoles.
1319          *
1320          * Only do that for coldplugged devices.  After initial device
1321          * initialization we will not renumber the consoles any more.
1322          */
1323         QemuConsole *c = QTAILQ_FIRST(&consoles);
1324 
1325         while (QTAILQ_NEXT(c, next) != NULL &&
1326                c->console_type == GRAPHIC_CONSOLE) {
1327             c = QTAILQ_NEXT(c, next);
1328         }
1329         if (c->console_type == GRAPHIC_CONSOLE) {
1330             /* have no text consoles */
1331             s->index = c->index + 1;
1332             QTAILQ_INSERT_AFTER(&consoles, c, s, next);
1333         } else {
1334             s->index = c->index;
1335             QTAILQ_INSERT_BEFORE(c, s, next);
1336             /* renumber text consoles */
1337             for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) {
1338                 c->index = i;
1339             }
1340         }
1341     }
1342     return s;
1343 }
1344 
1345 static void qemu_alloc_display(DisplaySurface *surface, int width, int height)
1346 {
1347     qemu_pixman_image_unref(surface->image);
1348     surface->image = NULL;
1349 
1350     surface->format = PIXMAN_x8r8g8b8;
1351     surface->image = pixman_image_create_bits(surface->format,
1352                                               width, height,
1353                                               NULL, width * 4);
1354     assert(surface->image != NULL);
1355 
1356     surface->flags = QEMU_ALLOCATED_FLAG;
1357 }
1358 
1359 DisplaySurface *qemu_create_displaysurface(int width, int height)
1360 {
1361     DisplaySurface *surface = g_new0(DisplaySurface, 1);
1362 
1363     trace_displaysurface_create(surface, width, height);
1364     qemu_alloc_display(surface, width, height);
1365     return surface;
1366 }
1367 
1368 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
1369                                                 pixman_format_code_t format,
1370                                                 int linesize, uint8_t *data)
1371 {
1372     DisplaySurface *surface = g_new0(DisplaySurface, 1);
1373 
1374     trace_displaysurface_create_from(surface, width, height, format);
1375     surface->format = format;
1376     surface->image = pixman_image_create_bits(surface->format,
1377                                               width, height,
1378                                               (void *)data, linesize);
1379     assert(surface->image != NULL);
1380 
1381     return surface;
1382 }
1383 
1384 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
1385 {
1386     DisplaySurface *surface = g_new0(DisplaySurface, 1);
1387 
1388     trace_displaysurface_create_pixman(surface);
1389     surface->format = pixman_image_get_format(image);
1390     surface->image = pixman_image_ref(image);
1391 
1392     return surface;
1393 }
1394 
1395 DisplaySurface *qemu_create_message_surface(int w, int h,
1396                                             const char *msg)
1397 {
1398     DisplaySurface *surface = qemu_create_displaysurface(w, h);
1399     pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
1400     pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
1401     pixman_image_t *glyph;
1402     int len, x, y, i;
1403 
1404     len = strlen(msg);
1405     x = (w / FONT_WIDTH  - len) / 2;
1406     y = (h / FONT_HEIGHT - 1)   / 2;
1407     for (i = 0; i < len; i++) {
1408         glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1409         qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1410                                  x+i, y, FONT_WIDTH, FONT_HEIGHT);
1411         qemu_pixman_image_unref(glyph);
1412     }
1413     return surface;
1414 }
1415 
1416 void qemu_free_displaysurface(DisplaySurface *surface)
1417 {
1418     if (surface == NULL) {
1419         return;
1420     }
1421     trace_displaysurface_free(surface);
1422     qemu_pixman_image_unref(surface->image);
1423     g_free(surface);
1424 }
1425 
1426 bool console_has_gl(QemuConsole *con)
1427 {
1428     return con->gl != NULL;
1429 }
1430 
1431 bool console_has_gl_dmabuf(QemuConsole *con)
1432 {
1433     return con->gl != NULL && con->gl->ops->dpy_gl_scanout_dmabuf != NULL;
1434 }
1435 
1436 void register_displaychangelistener(DisplayChangeListener *dcl)
1437 {
1438     static const char nodev[] =
1439         "This VM has no graphic display device.";
1440     static DisplaySurface *dummy;
1441     QemuConsole *con;
1442 
1443     assert(!dcl->ds);
1444 
1445     if (dcl->ops->dpy_gl_ctx_create) {
1446         /* display has opengl support */
1447         assert(dcl->con);
1448         if (dcl->con->gl) {
1449             fprintf(stderr, "can't register two opengl displays (%s, %s)\n",
1450                     dcl->ops->dpy_name, dcl->con->gl->ops->dpy_name);
1451             exit(1);
1452         }
1453         dcl->con->gl = dcl;
1454     }
1455 
1456     trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1457     dcl->ds = get_alloc_displaystate();
1458     QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1459     gui_setup_refresh(dcl->ds);
1460     if (dcl->con) {
1461         dcl->con->dcls++;
1462         con = dcl->con;
1463     } else {
1464         con = active_console;
1465     }
1466     if (dcl->ops->dpy_gfx_switch) {
1467         if (con) {
1468             dcl->ops->dpy_gfx_switch(dcl, con->surface);
1469         } else {
1470             if (!dummy) {
1471                 dummy = qemu_create_message_surface(640, 480, nodev);
1472             }
1473             dcl->ops->dpy_gfx_switch(dcl, dummy);
1474         }
1475     }
1476     text_console_update_cursor(NULL);
1477 }
1478 
1479 void update_displaychangelistener(DisplayChangeListener *dcl,
1480                                   uint64_t interval)
1481 {
1482     DisplayState *ds = dcl->ds;
1483 
1484     dcl->update_interval = interval;
1485     if (!ds->refreshing && ds->update_interval > interval) {
1486         timer_mod(ds->gui_timer, ds->last_update + interval);
1487     }
1488 }
1489 
1490 void unregister_displaychangelistener(DisplayChangeListener *dcl)
1491 {
1492     DisplayState *ds = dcl->ds;
1493     trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1494     if (dcl->con) {
1495         dcl->con->dcls--;
1496     }
1497     QLIST_REMOVE(dcl, next);
1498     dcl->ds = NULL;
1499     gui_setup_refresh(ds);
1500 }
1501 
1502 static void dpy_set_ui_info_timer(void *opaque)
1503 {
1504     QemuConsole *con = opaque;
1505 
1506     con->hw_ops->ui_info(con->hw, con->head, &con->ui_info);
1507 }
1508 
1509 bool dpy_ui_info_supported(QemuConsole *con)
1510 {
1511     return con->hw_ops->ui_info != NULL;
1512 }
1513 
1514 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info)
1515 {
1516     assert(con != NULL);
1517 
1518     if (!dpy_ui_info_supported(con)) {
1519         return -1;
1520     }
1521     if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
1522         /* nothing changed -- ignore */
1523         return 0;
1524     }
1525 
1526     /*
1527      * Typically we get a flood of these as the user resizes the window.
1528      * Wait until the dust has settled (one second without updates), then
1529      * go notify the guest.
1530      */
1531     con->ui_info = *info;
1532     timer_mod(con->ui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
1533     return 0;
1534 }
1535 
1536 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1537 {
1538     DisplayState *s = con->ds;
1539     DisplayChangeListener *dcl;
1540     int width = w;
1541     int height = h;
1542 
1543     if (con->surface) {
1544         width = surface_width(con->surface);
1545         height = surface_height(con->surface);
1546     }
1547     x = MAX(x, 0);
1548     y = MAX(y, 0);
1549     x = MIN(x, width);
1550     y = MIN(y, height);
1551     w = MIN(w, width - x);
1552     h = MIN(h, height - y);
1553 
1554     if (!qemu_console_is_visible(con)) {
1555         return;
1556     }
1557     QLIST_FOREACH(dcl, &s->listeners, next) {
1558         if (con != (dcl->con ? dcl->con : active_console)) {
1559             continue;
1560         }
1561         if (dcl->ops->dpy_gfx_update) {
1562             dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1563         }
1564     }
1565 }
1566 
1567 void dpy_gfx_update_full(QemuConsole *con)
1568 {
1569     if (!con->surface) {
1570         return;
1571     }
1572     dpy_gfx_update(con, 0, 0,
1573                    surface_width(con->surface),
1574                    surface_height(con->surface));
1575 }
1576 
1577 void dpy_gfx_replace_surface(QemuConsole *con,
1578                              DisplaySurface *surface)
1579 {
1580     DisplayState *s = con->ds;
1581     DisplaySurface *old_surface = con->surface;
1582     DisplayChangeListener *dcl;
1583 
1584     assert(old_surface != surface || surface == NULL);
1585 
1586     con->surface = surface;
1587     QLIST_FOREACH(dcl, &s->listeners, next) {
1588         if (con != (dcl->con ? dcl->con : active_console)) {
1589             continue;
1590         }
1591         if (dcl->ops->dpy_gfx_switch) {
1592             dcl->ops->dpy_gfx_switch(dcl, surface);
1593         }
1594     }
1595     qemu_free_displaysurface(old_surface);
1596 }
1597 
1598 bool dpy_gfx_check_format(QemuConsole *con,
1599                           pixman_format_code_t format)
1600 {
1601     DisplayChangeListener *dcl;
1602     DisplayState *s = con->ds;
1603 
1604     QLIST_FOREACH(dcl, &s->listeners, next) {
1605         if (dcl->con && dcl->con != con) {
1606             /* dcl bound to another console -> skip */
1607             continue;
1608         }
1609         if (dcl->ops->dpy_gfx_check_format) {
1610             if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
1611                 return false;
1612             }
1613         } else {
1614             /* default is to whitelist native 32 bpp only */
1615             if (format != qemu_default_pixman_format(32, true)) {
1616                 return false;
1617             }
1618         }
1619     }
1620     return true;
1621 }
1622 
1623 static void dpy_refresh(DisplayState *s)
1624 {
1625     DisplayChangeListener *dcl;
1626 
1627     QLIST_FOREACH(dcl, &s->listeners, next) {
1628         if (dcl->ops->dpy_refresh) {
1629             dcl->ops->dpy_refresh(dcl);
1630         }
1631     }
1632 }
1633 
1634 void dpy_text_cursor(QemuConsole *con, int x, int y)
1635 {
1636     DisplayState *s = con->ds;
1637     DisplayChangeListener *dcl;
1638 
1639     if (!qemu_console_is_visible(con)) {
1640         return;
1641     }
1642     QLIST_FOREACH(dcl, &s->listeners, next) {
1643         if (con != (dcl->con ? dcl->con : active_console)) {
1644             continue;
1645         }
1646         if (dcl->ops->dpy_text_cursor) {
1647             dcl->ops->dpy_text_cursor(dcl, x, y);
1648         }
1649     }
1650 }
1651 
1652 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1653 {
1654     DisplayState *s = con->ds;
1655     DisplayChangeListener *dcl;
1656 
1657     if (!qemu_console_is_visible(con)) {
1658         return;
1659     }
1660     QLIST_FOREACH(dcl, &s->listeners, next) {
1661         if (con != (dcl->con ? dcl->con : active_console)) {
1662             continue;
1663         }
1664         if (dcl->ops->dpy_text_update) {
1665             dcl->ops->dpy_text_update(dcl, x, y, w, h);
1666         }
1667     }
1668 }
1669 
1670 void dpy_text_resize(QemuConsole *con, int w, int h)
1671 {
1672     DisplayState *s = con->ds;
1673     DisplayChangeListener *dcl;
1674 
1675     if (!qemu_console_is_visible(con)) {
1676         return;
1677     }
1678     QLIST_FOREACH(dcl, &s->listeners, next) {
1679         if (con != (dcl->con ? dcl->con : active_console)) {
1680             continue;
1681         }
1682         if (dcl->ops->dpy_text_resize) {
1683             dcl->ops->dpy_text_resize(dcl, w, h);
1684         }
1685     }
1686 }
1687 
1688 void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1689 {
1690     DisplayState *s = con->ds;
1691     DisplayChangeListener *dcl;
1692 
1693     if (!qemu_console_is_visible(con)) {
1694         return;
1695     }
1696     QLIST_FOREACH(dcl, &s->listeners, next) {
1697         if (con != (dcl->con ? dcl->con : active_console)) {
1698             continue;
1699         }
1700         if (dcl->ops->dpy_mouse_set) {
1701             dcl->ops->dpy_mouse_set(dcl, x, y, on);
1702         }
1703     }
1704 }
1705 
1706 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1707 {
1708     DisplayState *s = con->ds;
1709     DisplayChangeListener *dcl;
1710 
1711     if (!qemu_console_is_visible(con)) {
1712         return;
1713     }
1714     QLIST_FOREACH(dcl, &s->listeners, next) {
1715         if (con != (dcl->con ? dcl->con : active_console)) {
1716             continue;
1717         }
1718         if (dcl->ops->dpy_cursor_define) {
1719             dcl->ops->dpy_cursor_define(dcl, cursor);
1720         }
1721     }
1722 }
1723 
1724 bool dpy_cursor_define_supported(QemuConsole *con)
1725 {
1726     DisplayState *s = con->ds;
1727     DisplayChangeListener *dcl;
1728 
1729     QLIST_FOREACH(dcl, &s->listeners, next) {
1730         if (dcl->ops->dpy_cursor_define) {
1731             return true;
1732         }
1733     }
1734     return false;
1735 }
1736 
1737 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
1738                                 struct QEMUGLParams *qparams)
1739 {
1740     assert(con->gl);
1741     return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
1742 }
1743 
1744 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
1745 {
1746     assert(con->gl);
1747     con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
1748 }
1749 
1750 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
1751 {
1752     assert(con->gl);
1753     return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
1754 }
1755 
1756 QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con)
1757 {
1758     assert(con->gl);
1759     return con->gl->ops->dpy_gl_ctx_get_current(con->gl);
1760 }
1761 
1762 void dpy_gl_scanout_disable(QemuConsole *con)
1763 {
1764     assert(con->gl);
1765     if (con->gl->ops->dpy_gl_scanout_disable) {
1766         con->gl->ops->dpy_gl_scanout_disable(con->gl);
1767     } else {
1768         con->gl->ops->dpy_gl_scanout_texture(con->gl, 0, false, 0, 0,
1769                                              0, 0, 0, 0);
1770     }
1771 }
1772 
1773 void dpy_gl_scanout_texture(QemuConsole *con,
1774                             uint32_t backing_id,
1775                             bool backing_y_0_top,
1776                             uint32_t backing_width,
1777                             uint32_t backing_height,
1778                             uint32_t x, uint32_t y,
1779                             uint32_t width, uint32_t height)
1780 {
1781     assert(con->gl);
1782     con->gl->ops->dpy_gl_scanout_texture(con->gl, backing_id,
1783                                          backing_y_0_top,
1784                                          backing_width, backing_height,
1785                                          x, y, width, height);
1786 }
1787 
1788 void dpy_gl_scanout_dmabuf(QemuConsole *con,
1789                            QemuDmaBuf *dmabuf)
1790 {
1791     assert(con->gl);
1792     con->gl->ops->dpy_gl_scanout_dmabuf(con->gl, dmabuf);
1793 }
1794 
1795 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
1796                           bool have_hot, uint32_t hot_x, uint32_t hot_y)
1797 {
1798     assert(con->gl);
1799 
1800     if (con->gl->ops->dpy_gl_cursor_dmabuf) {
1801         con->gl->ops->dpy_gl_cursor_dmabuf(con->gl, dmabuf,
1802                                            have_hot, hot_x, hot_y);
1803     }
1804 }
1805 
1806 void dpy_gl_cursor_position(QemuConsole *con,
1807                             uint32_t pos_x, uint32_t pos_y)
1808 {
1809     assert(con->gl);
1810 
1811     if (con->gl->ops->dpy_gl_cursor_position) {
1812         con->gl->ops->dpy_gl_cursor_position(con->gl, pos_x, pos_y);
1813     }
1814 }
1815 
1816 void dpy_gl_release_dmabuf(QemuConsole *con,
1817                           QemuDmaBuf *dmabuf)
1818 {
1819     assert(con->gl);
1820 
1821     if (con->gl->ops->dpy_gl_release_dmabuf) {
1822         con->gl->ops->dpy_gl_release_dmabuf(con->gl, dmabuf);
1823     }
1824 }
1825 
1826 void dpy_gl_update(QemuConsole *con,
1827                    uint32_t x, uint32_t y, uint32_t w, uint32_t h)
1828 {
1829     assert(con->gl);
1830     con->gl->ops->dpy_gl_update(con->gl, x, y, w, h);
1831 }
1832 
1833 /***********************************************************/
1834 /* register display */
1835 
1836 /* console.c internal use only */
1837 static DisplayState *get_alloc_displaystate(void)
1838 {
1839     if (!display_state) {
1840         display_state = g_new0(DisplayState, 1);
1841         cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1842                                     text_console_update_cursor, NULL);
1843     }
1844     return display_state;
1845 }
1846 
1847 /*
1848  * Called by main(), after creating QemuConsoles
1849  * and before initializing ui (sdl/vnc/...).
1850  */
1851 DisplayState *init_displaystate(void)
1852 {
1853     gchar *name;
1854     QemuConsole *con;
1855 
1856     get_alloc_displaystate();
1857     QTAILQ_FOREACH(con, &consoles, next) {
1858         if (con->console_type != GRAPHIC_CONSOLE &&
1859             con->ds == NULL) {
1860             text_console_do_init(con->chr, display_state);
1861         }
1862 
1863         /* Hook up into the qom tree here (not in new_console()), once
1864          * all QemuConsoles are created and the order / numbering
1865          * doesn't change any more */
1866         name = g_strdup_printf("console[%d]", con->index);
1867         object_property_add_child(container_get(object_get_root(), "/backend"),
1868                                   name, OBJECT(con), &error_abort);
1869         g_free(name);
1870     }
1871 
1872     return display_state;
1873 }
1874 
1875 void graphic_console_set_hwops(QemuConsole *con,
1876                                const GraphicHwOps *hw_ops,
1877                                void *opaque)
1878 {
1879     con->hw_ops = hw_ops;
1880     con->hw = opaque;
1881 }
1882 
1883 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
1884                                   const GraphicHwOps *hw_ops,
1885                                   void *opaque)
1886 {
1887     static const char noinit[] =
1888         "Guest has not initialized the display (yet).";
1889     int width = 640;
1890     int height = 480;
1891     QemuConsole *s;
1892     DisplayState *ds;
1893     DisplaySurface *surface;
1894 
1895     ds = get_alloc_displaystate();
1896     s = qemu_console_lookup_unused();
1897     if (s) {
1898         trace_console_gfx_reuse(s->index);
1899         if (s->surface) {
1900             width = surface_width(s->surface);
1901             height = surface_height(s->surface);
1902         }
1903     } else {
1904         trace_console_gfx_new();
1905         s = new_console(ds, GRAPHIC_CONSOLE, head);
1906         s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1907                                    dpy_set_ui_info_timer, s);
1908     }
1909     graphic_console_set_hwops(s, hw_ops, opaque);
1910     if (dev) {
1911         object_property_set_link(OBJECT(s), OBJECT(dev), "device",
1912                                  &error_abort);
1913     }
1914 
1915     surface = qemu_create_message_surface(width, height, noinit);
1916     dpy_gfx_replace_surface(s, surface);
1917     return s;
1918 }
1919 
1920 static const GraphicHwOps unused_ops = {
1921     /* no callbacks */
1922 };
1923 
1924 void graphic_console_close(QemuConsole *con)
1925 {
1926     static const char unplugged[] =
1927         "Guest display has been unplugged";
1928     DisplaySurface *surface;
1929     int width = 640;
1930     int height = 480;
1931 
1932     if (con->surface) {
1933         width = surface_width(con->surface);
1934         height = surface_height(con->surface);
1935     }
1936 
1937     trace_console_gfx_close(con->index);
1938     object_property_set_link(OBJECT(con), NULL, "device", &error_abort);
1939     graphic_console_set_hwops(con, &unused_ops, NULL);
1940 
1941     if (con->gl) {
1942         dpy_gl_scanout_disable(con);
1943     }
1944     surface = qemu_create_message_surface(width, height, unplugged);
1945     dpy_gfx_replace_surface(con, surface);
1946 }
1947 
1948 QemuConsole *qemu_console_lookup_by_index(unsigned int index)
1949 {
1950     QemuConsole *con;
1951 
1952     QTAILQ_FOREACH(con, &consoles, next) {
1953         if (con->index == index) {
1954             return con;
1955         }
1956     }
1957     return NULL;
1958 }
1959 
1960 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
1961 {
1962     QemuConsole *con;
1963     Object *obj;
1964     uint32_t h;
1965 
1966     QTAILQ_FOREACH(con, &consoles, next) {
1967         obj = object_property_get_link(OBJECT(con),
1968                                        "device", &error_abort);
1969         if (DEVICE(obj) != dev) {
1970             continue;
1971         }
1972         h = object_property_get_uint(OBJECT(con),
1973                                      "head", &error_abort);
1974         if (h != head) {
1975             continue;
1976         }
1977         return con;
1978     }
1979     return NULL;
1980 }
1981 
1982 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
1983                                                 uint32_t head, Error **errp)
1984 {
1985     DeviceState *dev;
1986     QemuConsole *con;
1987 
1988     dev = qdev_find_recursive(sysbus_get_default(), device_id);
1989     if (dev == NULL) {
1990         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
1991                   "Device '%s' not found", device_id);
1992         return NULL;
1993     }
1994 
1995     con = qemu_console_lookup_by_device(dev, head);
1996     if (con == NULL) {
1997         error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
1998                    device_id, head);
1999         return NULL;
2000     }
2001 
2002     return con;
2003 }
2004 
2005 QemuConsole *qemu_console_lookup_unused(void)
2006 {
2007     QemuConsole *con;
2008     Object *obj;
2009 
2010     QTAILQ_FOREACH(con, &consoles, next) {
2011         if (con->hw_ops != &unused_ops) {
2012             continue;
2013         }
2014         obj = object_property_get_link(OBJECT(con),
2015                                        "device", &error_abort);
2016         if (obj != NULL) {
2017             continue;
2018         }
2019         return con;
2020     }
2021     return NULL;
2022 }
2023 
2024 bool qemu_console_is_visible(QemuConsole *con)
2025 {
2026     return (con == active_console) || (con->dcls > 0);
2027 }
2028 
2029 bool qemu_console_is_graphic(QemuConsole *con)
2030 {
2031     if (con == NULL) {
2032         con = active_console;
2033     }
2034     return con && (con->console_type == GRAPHIC_CONSOLE);
2035 }
2036 
2037 bool qemu_console_is_fixedsize(QemuConsole *con)
2038 {
2039     if (con == NULL) {
2040         con = active_console;
2041     }
2042     return con && (con->console_type != TEXT_CONSOLE);
2043 }
2044 
2045 bool qemu_console_is_gl_blocked(QemuConsole *con)
2046 {
2047     assert(con != NULL);
2048     return con->gl_block;
2049 }
2050 
2051 char *qemu_console_get_label(QemuConsole *con)
2052 {
2053     if (con->console_type == GRAPHIC_CONSOLE) {
2054         if (con->device) {
2055             return g_strdup(object_get_typename(con->device));
2056         }
2057         return g_strdup("VGA");
2058     } else {
2059         if (con->chr && con->chr->label) {
2060             return g_strdup(con->chr->label);
2061         }
2062         return g_strdup_printf("vc%d", con->index);
2063     }
2064 }
2065 
2066 int qemu_console_get_index(QemuConsole *con)
2067 {
2068     if (con == NULL) {
2069         con = active_console;
2070     }
2071     return con ? con->index : -1;
2072 }
2073 
2074 uint32_t qemu_console_get_head(QemuConsole *con)
2075 {
2076     if (con == NULL) {
2077         con = active_console;
2078     }
2079     return con ? con->head : -1;
2080 }
2081 
2082 QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con)
2083 {
2084     assert(con != NULL);
2085     return &con->ui_info;
2086 }
2087 
2088 int qemu_console_get_width(QemuConsole *con, int fallback)
2089 {
2090     if (con == NULL) {
2091         con = active_console;
2092     }
2093     return con ? surface_width(con->surface) : fallback;
2094 }
2095 
2096 int qemu_console_get_height(QemuConsole *con, int fallback)
2097 {
2098     if (con == NULL) {
2099         con = active_console;
2100     }
2101     return con ? surface_height(con->surface) : fallback;
2102 }
2103 
2104 static void vc_chr_set_echo(Chardev *chr, bool echo)
2105 {
2106     VCChardev *drv = VC_CHARDEV(chr);
2107     QemuConsole *s = drv->console;
2108 
2109     s->echo = echo;
2110 }
2111 
2112 static void text_console_update_cursor_timer(void)
2113 {
2114     timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
2115               + CONSOLE_CURSOR_PERIOD / 2);
2116 }
2117 
2118 static void text_console_update_cursor(void *opaque)
2119 {
2120     QemuConsole *s;
2121     int count = 0;
2122 
2123     cursor_visible_phase = !cursor_visible_phase;
2124 
2125     QTAILQ_FOREACH(s, &consoles, next) {
2126         if (qemu_console_is_graphic(s) ||
2127             !qemu_console_is_visible(s)) {
2128             continue;
2129         }
2130         count++;
2131         graphic_hw_invalidate(s);
2132     }
2133 
2134     if (count) {
2135         text_console_update_cursor_timer();
2136     }
2137 }
2138 
2139 static const GraphicHwOps text_console_ops = {
2140     .invalidate  = text_console_invalidate,
2141     .text_update = text_console_update,
2142 };
2143 
2144 static void text_console_do_init(Chardev *chr, DisplayState *ds)
2145 {
2146     VCChardev *drv = VC_CHARDEV(chr);
2147     QemuConsole *s = drv->console;
2148     int g_width = 80 * FONT_WIDTH;
2149     int g_height = 24 * FONT_HEIGHT;
2150 
2151     s->out_fifo.buf = s->out_fifo_buf;
2152     s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
2153     s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
2154     s->ds = ds;
2155 
2156     s->y_displayed = 0;
2157     s->y_base = 0;
2158     s->total_height = DEFAULT_BACKSCROLL;
2159     s->x = 0;
2160     s->y = 0;
2161     if (!s->surface) {
2162         if (active_console && active_console->surface) {
2163             g_width = surface_width(active_console->surface);
2164             g_height = surface_height(active_console->surface);
2165         }
2166         s->surface = qemu_create_displaysurface(g_width, g_height);
2167     }
2168 
2169     s->hw_ops = &text_console_ops;
2170     s->hw = s;
2171 
2172     /* Set text attribute defaults */
2173     s->t_attrib_default.bold = 0;
2174     s->t_attrib_default.uline = 0;
2175     s->t_attrib_default.blink = 0;
2176     s->t_attrib_default.invers = 0;
2177     s->t_attrib_default.unvisible = 0;
2178     s->t_attrib_default.fgcol = QEMU_COLOR_WHITE;
2179     s->t_attrib_default.bgcol = QEMU_COLOR_BLACK;
2180     /* set current text attributes to default */
2181     s->t_attrib = s->t_attrib_default;
2182     text_console_resize(s);
2183 
2184     if (chr->label) {
2185         char msg[128];
2186         int len;
2187 
2188         s->t_attrib.bgcol = QEMU_COLOR_BLUE;
2189         len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
2190         vc_chr_write(chr, (uint8_t *)msg, len);
2191         s->t_attrib = s->t_attrib_default;
2192     }
2193 
2194     qemu_chr_be_event(chr, CHR_EVENT_OPENED);
2195 }
2196 
2197 static void vc_chr_open(Chardev *chr,
2198                         ChardevBackend *backend,
2199                         bool *be_opened,
2200                         Error **errp)
2201 {
2202     ChardevVC *vc = backend->u.vc.data;
2203     VCChardev *drv = VC_CHARDEV(chr);
2204     QemuConsole *s;
2205     unsigned width = 0;
2206     unsigned height = 0;
2207 
2208     if (vc->has_width) {
2209         width = vc->width;
2210     } else if (vc->has_cols) {
2211         width = vc->cols * FONT_WIDTH;
2212     }
2213 
2214     if (vc->has_height) {
2215         height = vc->height;
2216     } else if (vc->has_rows) {
2217         height = vc->rows * FONT_HEIGHT;
2218     }
2219 
2220     trace_console_txt_new(width, height);
2221     if (width == 0 || height == 0) {
2222         s = new_console(NULL, TEXT_CONSOLE, 0);
2223     } else {
2224         s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0);
2225         s->surface = qemu_create_displaysurface(width, height);
2226     }
2227 
2228     if (!s) {
2229         error_setg(errp, "cannot create text console");
2230         return;
2231     }
2232 
2233     s->chr = chr;
2234     drv->console = s;
2235 
2236     if (display_state) {
2237         text_console_do_init(chr, display_state);
2238     }
2239 
2240     /* console/chardev init sometimes completes elsewhere in a 2nd
2241      * stage, so defer OPENED events until they are fully initialized
2242      */
2243     *be_opened = false;
2244 }
2245 
2246 void qemu_console_resize(QemuConsole *s, int width, int height)
2247 {
2248     DisplaySurface *surface;
2249 
2250     assert(s->console_type == GRAPHIC_CONSOLE);
2251 
2252     if (s->surface && (s->surface->flags & QEMU_ALLOCATED_FLAG) &&
2253         pixman_image_get_width(s->surface->image) == width &&
2254         pixman_image_get_height(s->surface->image) == height) {
2255         return;
2256     }
2257 
2258     surface = qemu_create_displaysurface(width, height);
2259     dpy_gfx_replace_surface(s, surface);
2260 }
2261 
2262 DisplaySurface *qemu_console_surface(QemuConsole *console)
2263 {
2264     return console->surface;
2265 }
2266 
2267 PixelFormat qemu_default_pixelformat(int bpp)
2268 {
2269     pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
2270     PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
2271     return pf;
2272 }
2273 
2274 static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
2275 
2276 void qemu_display_register(QemuDisplay *ui)
2277 {
2278     assert(ui->type < DISPLAY_TYPE__MAX);
2279     dpys[ui->type] = ui;
2280 }
2281 
2282 bool qemu_display_find_default(DisplayOptions *opts)
2283 {
2284     static DisplayType prio[] = {
2285         DISPLAY_TYPE_GTK,
2286         DISPLAY_TYPE_SDL,
2287         DISPLAY_TYPE_COCOA
2288     };
2289     int i;
2290 
2291     for (i = 0; i < ARRAY_SIZE(prio); i++) {
2292         if (dpys[prio[i]] == NULL) {
2293             ui_module_load_one(DisplayType_str(prio[i]));
2294         }
2295         if (dpys[prio[i]] == NULL) {
2296             continue;
2297         }
2298         opts->type = prio[i];
2299         return true;
2300     }
2301     return false;
2302 }
2303 
2304 void qemu_display_early_init(DisplayOptions *opts)
2305 {
2306     assert(opts->type < DISPLAY_TYPE__MAX);
2307     if (opts->type == DISPLAY_TYPE_NONE) {
2308         return;
2309     }
2310     if (dpys[opts->type] == NULL) {
2311         ui_module_load_one(DisplayType_str(opts->type));
2312     }
2313     if (dpys[opts->type] == NULL) {
2314         error_report("Display '%s' is not available.",
2315                      DisplayType_str(opts->type));
2316         exit(1);
2317     }
2318     if (dpys[opts->type]->early_init) {
2319         dpys[opts->type]->early_init(opts);
2320     }
2321 }
2322 
2323 void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
2324 {
2325     assert(opts->type < DISPLAY_TYPE__MAX);
2326     if (opts->type == DISPLAY_TYPE_NONE) {
2327         return;
2328     }
2329     assert(dpys[opts->type] != NULL);
2330     dpys[opts->type]->init(ds, opts);
2331 }
2332 
2333 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
2334 {
2335     int val;
2336     ChardevVC *vc;
2337 
2338     backend->type = CHARDEV_BACKEND_KIND_VC;
2339     vc = backend->u.vc.data = g_new0(ChardevVC, 1);
2340     qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
2341 
2342     val = qemu_opt_get_number(opts, "width", 0);
2343     if (val != 0) {
2344         vc->has_width = true;
2345         vc->width = val;
2346     }
2347 
2348     val = qemu_opt_get_number(opts, "height", 0);
2349     if (val != 0) {
2350         vc->has_height = true;
2351         vc->height = val;
2352     }
2353 
2354     val = qemu_opt_get_number(opts, "cols", 0);
2355     if (val != 0) {
2356         vc->has_cols = true;
2357         vc->cols = val;
2358     }
2359 
2360     val = qemu_opt_get_number(opts, "rows", 0);
2361     if (val != 0) {
2362         vc->has_rows = true;
2363         vc->rows = val;
2364     }
2365 }
2366 
2367 static const TypeInfo qemu_console_info = {
2368     .name = TYPE_QEMU_CONSOLE,
2369     .parent = TYPE_OBJECT,
2370     .instance_size = sizeof(QemuConsole),
2371     .class_size = sizeof(QemuConsoleClass),
2372 };
2373 
2374 static void char_vc_class_init(ObjectClass *oc, void *data)
2375 {
2376     ChardevClass *cc = CHARDEV_CLASS(oc);
2377 
2378     cc->parse = qemu_chr_parse_vc;
2379     cc->open = vc_chr_open;
2380     cc->chr_write = vc_chr_write;
2381     cc->chr_set_echo = vc_chr_set_echo;
2382 }
2383 
2384 static const TypeInfo char_vc_type_info = {
2385     .name = TYPE_CHARDEV_VC,
2386     .parent = TYPE_CHARDEV,
2387     .instance_size = sizeof(VCChardev),
2388     .class_init = char_vc_class_init,
2389 };
2390 
2391 void qemu_console_early_init(void)
2392 {
2393     /* set the default vc driver */
2394     if (!object_class_by_name(TYPE_CHARDEV_VC)) {
2395         type_register(&char_vc_type_info);
2396     }
2397 }
2398 
2399 static void register_types(void)
2400 {
2401     type_register_static(&qemu_console_info);
2402 }
2403 
2404 type_init(register_types);
2405