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