xref: /openbmc/qemu/ui/console.c (revision a1a62ced)
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(QemuTextConsole *s, int keysym)
1139 {
1140     uint8_t buf[16], *q;
1141     int c;
1142     uint32_t num_free;
1143 
1144     switch(keysym) {
1145     case QEMU_KEY_CTRL_UP:
1146         console_scroll(s, -1);
1147         break;
1148     case QEMU_KEY_CTRL_DOWN:
1149         console_scroll(s, 1);
1150         break;
1151     case QEMU_KEY_CTRL_PAGEUP:
1152         console_scroll(s, -10);
1153         break;
1154     case QEMU_KEY_CTRL_PAGEDOWN:
1155         console_scroll(s, 10);
1156         break;
1157     default:
1158         /* convert the QEMU keysym to VT100 key string */
1159         q = buf;
1160         if (keysym >= 0xe100 && keysym <= 0xe11f) {
1161             *q++ = '\033';
1162             *q++ = '[';
1163             c = keysym - 0xe100;
1164             if (c >= 10)
1165                 *q++ = '0' + (c / 10);
1166             *q++ = '0' + (c % 10);
1167             *q++ = '~';
1168         } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1169             *q++ = '\033';
1170             *q++ = '[';
1171             *q++ = keysym & 0xff;
1172         } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1173             qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
1174             *q++ = '\n';
1175         } else {
1176             *q++ = keysym;
1177         }
1178         if (s->echo) {
1179             qemu_chr_write(s->chr, buf, q - buf, true);
1180         }
1181         num_free = fifo8_num_free(&s->out_fifo);
1182         fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
1183         kbd_send_chars(s);
1184         break;
1185     }
1186 }
1187 
1188 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
1189     [Q_KEY_CODE_UP]     = QEMU_KEY_UP,
1190     [Q_KEY_CODE_DOWN]   = QEMU_KEY_DOWN,
1191     [Q_KEY_CODE_RIGHT]  = QEMU_KEY_RIGHT,
1192     [Q_KEY_CODE_LEFT]   = QEMU_KEY_LEFT,
1193     [Q_KEY_CODE_HOME]   = QEMU_KEY_HOME,
1194     [Q_KEY_CODE_END]    = QEMU_KEY_END,
1195     [Q_KEY_CODE_PGUP]   = QEMU_KEY_PAGEUP,
1196     [Q_KEY_CODE_PGDN]   = QEMU_KEY_PAGEDOWN,
1197     [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
1198     [Q_KEY_CODE_TAB]    = QEMU_KEY_TAB,
1199     [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
1200 };
1201 
1202 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
1203     [Q_KEY_CODE_UP]     = QEMU_KEY_CTRL_UP,
1204     [Q_KEY_CODE_DOWN]   = QEMU_KEY_CTRL_DOWN,
1205     [Q_KEY_CODE_RIGHT]  = QEMU_KEY_CTRL_RIGHT,
1206     [Q_KEY_CODE_LEFT]   = QEMU_KEY_CTRL_LEFT,
1207     [Q_KEY_CODE_HOME]   = QEMU_KEY_CTRL_HOME,
1208     [Q_KEY_CODE_END]    = QEMU_KEY_CTRL_END,
1209     [Q_KEY_CODE_PGUP]   = QEMU_KEY_CTRL_PAGEUP,
1210     [Q_KEY_CODE_PGDN]   = QEMU_KEY_CTRL_PAGEDOWN,
1211 };
1212 
1213 bool kbd_put_qcode_console(QemuTextConsole *s, int qcode, bool ctrl)
1214 {
1215     int keysym;
1216 
1217     keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
1218     if (keysym == 0) {
1219         return false;
1220     }
1221     kbd_put_keysym_console(s, keysym);
1222     return true;
1223 }
1224 
1225 void kbd_put_string_console(QemuTextConsole *s, const char *str, int len)
1226 {
1227     int i;
1228 
1229     for (i = 0; i < len && str[i]; i++) {
1230         kbd_put_keysym_console(s, str[i]);
1231     }
1232 }
1233 
1234 void kbd_put_keysym(int keysym)
1235 {
1236     if (QEMU_IS_TEXT_CONSOLE(active_console)) {
1237         kbd_put_keysym_console(QEMU_TEXT_CONSOLE(active_console), keysym);
1238     }
1239 }
1240 
1241 static void text_console_invalidate(void *opaque)
1242 {
1243     QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
1244 
1245     if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
1246         text_console_resize(QEMU_TEXT_CONSOLE(s));
1247     }
1248     console_refresh(s);
1249 }
1250 
1251 static void text_console_update(void *opaque, console_ch_t *chardata)
1252 {
1253     QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
1254     int i, j, src;
1255 
1256     if (s->text_x[0] <= s->text_x[1]) {
1257         src = (s->y_base + s->text_y[0]) * s->width;
1258         chardata += s->text_y[0] * s->width;
1259         for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1260             for (j = 0; j < s->width; j++, src++) {
1261                 console_write_ch(chardata ++,
1262                                  ATTR2CHTYPE(s->cells[src].ch,
1263                                              s->cells[src].t_attrib.fgcol,
1264                                              s->cells[src].t_attrib.bgcol,
1265                                              s->cells[src].t_attrib.bold));
1266             }
1267         dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0],
1268                         s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1269         s->text_x[0] = s->width;
1270         s->text_y[0] = s->height;
1271         s->text_x[1] = 0;
1272         s->text_y[1] = 0;
1273     }
1274     if (s->cursor_invalidate) {
1275         dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y);
1276         s->cursor_invalidate = 0;
1277     }
1278 }
1279 
1280 static void
1281 qemu_console_register(QemuConsole *c)
1282 {
1283     int i;
1284 
1285     if (!active_console || (!QEMU_IS_GRAPHIC_CONSOLE(active_console) &&
1286                             QEMU_IS_GRAPHIC_CONSOLE(c))) {
1287         active_console = c;
1288     }
1289 
1290     if (QTAILQ_EMPTY(&consoles)) {
1291         c->index = 0;
1292         QTAILQ_INSERT_TAIL(&consoles, c, next);
1293     } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) {
1294         QemuConsole *last = QTAILQ_LAST(&consoles);
1295         c->index = last->index + 1;
1296         QTAILQ_INSERT_TAIL(&consoles, c, next);
1297     } else {
1298         /*
1299          * HACK: Put graphical consoles before text consoles.
1300          *
1301          * Only do that for coldplugged devices.  After initial device
1302          * initialization we will not renumber the consoles any more.
1303          */
1304         QemuConsole *it = QTAILQ_FIRST(&consoles);
1305 
1306         while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) {
1307             it = QTAILQ_NEXT(it, next);
1308         }
1309         if (QEMU_IS_GRAPHIC_CONSOLE(it)) {
1310             /* have no text consoles */
1311             c->index = it->index + 1;
1312             QTAILQ_INSERT_AFTER(&consoles, it, c, next);
1313         } else {
1314             c->index = it->index;
1315             QTAILQ_INSERT_BEFORE(it, c, next);
1316             /* renumber text consoles */
1317             for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) {
1318                 it->index = i;
1319             }
1320         }
1321     }
1322 }
1323 
1324 static void
1325 qemu_console_finalize(Object *obj)
1326 {
1327     QemuConsole *c = QEMU_CONSOLE(obj);
1328 
1329     /* TODO: check this code path, and unregister from consoles */
1330     g_clear_pointer(&c->surface, qemu_free_displaysurface);
1331     g_clear_pointer(&c->gl_unblock_timer, timer_free);
1332     g_clear_pointer(&c->ui_timer, timer_free);
1333 }
1334 
1335 static void
1336 qemu_console_class_init(ObjectClass *oc, void *data)
1337 {
1338 }
1339 
1340 static void
1341 qemu_console_init(Object *obj)
1342 {
1343     QemuConsole *c = QEMU_CONSOLE(obj);
1344     DisplayState *ds = get_alloc_displaystate();
1345 
1346     qemu_co_queue_init(&c->dump_queue);
1347     c->ds = ds;
1348     c->window_id = -1;
1349     c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1350                                dpy_set_ui_info_timer, c);
1351     qemu_console_register(c);
1352 }
1353 
1354 static void
1355 qemu_graphic_console_finalize(Object *obj)
1356 {
1357     QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj);
1358 
1359     g_clear_pointer(&c->device, object_unref);
1360 }
1361 
1362 static void
1363 qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name,
1364                                    void *opaque, Error **errp)
1365 {
1366     QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj);
1367 
1368     visit_type_uint32(v, name, &c->head, errp);
1369 }
1370 
1371 static void
1372 qemu_graphic_console_class_init(ObjectClass *oc, void *data)
1373 {
1374     object_class_property_add_link(oc, "device", TYPE_DEVICE,
1375                                    offsetof(QemuGraphicConsole, device),
1376                                    object_property_allow_set_link,
1377                                    OBJ_PROP_LINK_STRONG);
1378     object_class_property_add(oc, "head", "uint32",
1379                               qemu_graphic_console_prop_get_head,
1380                               NULL, NULL, NULL);
1381 }
1382 
1383 static void
1384 qemu_graphic_console_init(Object *obj)
1385 {
1386 }
1387 
1388 static void
1389 qemu_text_console_finalize(Object *obj)
1390 {
1391 }
1392 
1393 static void
1394 qemu_text_console_class_init(ObjectClass *oc, void *data)
1395 {
1396     if (!cursor_timer) {
1397         cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1398                                     text_console_update_cursor, NULL);
1399     }
1400 }
1401 
1402 static const GraphicHwOps text_console_ops = {
1403     .invalidate  = text_console_invalidate,
1404     .text_update = text_console_update,
1405 };
1406 
1407 static void
1408 qemu_text_console_init(Object *obj)
1409 {
1410     QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
1411 
1412     fifo8_create(&c->out_fifo, 16);
1413     c->total_height = DEFAULT_BACKSCROLL;
1414     QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
1415     QEMU_CONSOLE(c)->hw = c;
1416 }
1417 
1418 static void
1419 qemu_fixed_text_console_finalize(Object *obj)
1420 {
1421 }
1422 
1423 static void
1424 qemu_fixed_text_console_class_init(ObjectClass *oc, void *data)
1425 {
1426 }
1427 
1428 static void
1429 qemu_fixed_text_console_init(Object *obj)
1430 {
1431 }
1432 
1433 #ifdef WIN32
1434 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
1435                                           HANDLE h, uint32_t offset)
1436 {
1437     assert(!surface->handle);
1438 
1439     surface->handle = h;
1440     surface->handle_offset = offset;
1441 }
1442 
1443 static void
1444 win32_pixman_image_destroy(pixman_image_t *image, void *data)
1445 {
1446     DisplaySurface *surface = data;
1447 
1448     if (!surface->handle) {
1449         return;
1450     }
1451 
1452     assert(surface->handle_offset == 0);
1453 
1454     qemu_win32_map_free(
1455         pixman_image_get_data(surface->image),
1456         surface->handle,
1457         &error_warn
1458     );
1459 }
1460 #endif
1461 
1462 DisplaySurface *qemu_create_displaysurface(int width, int height)
1463 {
1464     DisplaySurface *surface;
1465     void *bits = NULL;
1466 #ifdef WIN32
1467     HANDLE handle = NULL;
1468 #endif
1469 
1470     trace_displaysurface_create(width, height);
1471 
1472 #ifdef WIN32
1473     bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
1474 #endif
1475 
1476     surface = qemu_create_displaysurface_from(
1477         width, height,
1478         PIXMAN_x8r8g8b8,
1479         width * 4, bits
1480     );
1481     surface->flags = QEMU_ALLOCATED_FLAG;
1482 
1483 #ifdef WIN32
1484     qemu_displaysurface_win32_set_handle(surface, handle, 0);
1485 #endif
1486     return surface;
1487 }
1488 
1489 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
1490                                                 pixman_format_code_t format,
1491                                                 int linesize, uint8_t *data)
1492 {
1493     DisplaySurface *surface = g_new0(DisplaySurface, 1);
1494 
1495     trace_displaysurface_create_from(surface, width, height, format);
1496     surface->format = format;
1497     surface->image = pixman_image_create_bits(surface->format,
1498                                               width, height,
1499                                               (void *)data, linesize);
1500     assert(surface->image != NULL);
1501 #ifdef WIN32
1502     pixman_image_set_destroy_function(surface->image,
1503                                       win32_pixman_image_destroy, surface);
1504 #endif
1505 
1506     return surface;
1507 }
1508 
1509 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
1510 {
1511     DisplaySurface *surface = g_new0(DisplaySurface, 1);
1512 
1513     trace_displaysurface_create_pixman(surface);
1514     surface->format = pixman_image_get_format(image);
1515     surface->image = pixman_image_ref(image);
1516 
1517     return surface;
1518 }
1519 
1520 DisplaySurface *qemu_create_placeholder_surface(int w, int h,
1521                                                 const char *msg)
1522 {
1523     DisplaySurface *surface = qemu_create_displaysurface(w, h);
1524     pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK;
1525     pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY;
1526     pixman_image_t *glyph;
1527     int len, x, y, i;
1528 
1529     len = strlen(msg);
1530     x = (w / FONT_WIDTH  - len) / 2;
1531     y = (h / FONT_HEIGHT - 1)   / 2;
1532     for (i = 0; i < len; i++) {
1533         glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1534         qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1535                                  x+i, y, FONT_WIDTH, FONT_HEIGHT);
1536         qemu_pixman_image_unref(glyph);
1537     }
1538     surface->flags |= QEMU_PLACEHOLDER_FLAG;
1539     return surface;
1540 }
1541 
1542 void qemu_free_displaysurface(DisplaySurface *surface)
1543 {
1544     if (surface == NULL) {
1545         return;
1546     }
1547     trace_displaysurface_free(surface);
1548     qemu_pixman_image_unref(surface->image);
1549     g_free(surface);
1550 }
1551 
1552 bool console_has_gl(QemuConsole *con)
1553 {
1554     return con->gl != NULL;
1555 }
1556 
1557 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
1558 {
1559     if (dcl->ops->dpy_has_dmabuf) {
1560         return dcl->ops->dpy_has_dmabuf(dcl);
1561     }
1562 
1563     if (dcl->ops->dpy_gl_scanout_dmabuf) {
1564         return true;
1565     }
1566 
1567     return false;
1568 }
1569 
1570 static bool console_compatible_with(QemuConsole *con,
1571                                     DisplayChangeListener *dcl, Error **errp)
1572 {
1573     int flags;
1574 
1575     flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
1576 
1577     if (console_has_gl(con) &&
1578         !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
1579         error_setg(errp, "Display %s is incompatible with the GL context",
1580                    dcl->ops->dpy_name);
1581         return false;
1582     }
1583 
1584     if (flags & GRAPHIC_FLAGS_GL &&
1585         !console_has_gl(con)) {
1586         error_setg(errp, "The console requires a GL context.");
1587         return false;
1588 
1589     }
1590 
1591     if (flags & GRAPHIC_FLAGS_DMABUF &&
1592         !displaychangelistener_has_dmabuf(dcl)) {
1593         error_setg(errp, "The console requires display DMABUF support.");
1594         return false;
1595     }
1596 
1597     return true;
1598 }
1599 
1600 void console_handle_touch_event(QemuConsole *con,
1601                                 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
1602                                 uint64_t num_slot,
1603                                 int width, int height,
1604                                 double x, double y,
1605                                 InputMultiTouchType type,
1606                                 Error **errp)
1607 {
1608     struct touch_slot *slot;
1609     bool needs_sync = false;
1610     int update;
1611     int i;
1612 
1613     if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
1614         error_setg(errp,
1615                    "Unexpected touch slot number: % " PRId64" >= %d",
1616                    num_slot, INPUT_EVENT_SLOTS_MAX);
1617         return;
1618     }
1619 
1620     slot = &touch_slots[num_slot];
1621     slot->x = x;
1622     slot->y = y;
1623 
1624     if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
1625         slot->tracking_id = num_slot;
1626     }
1627 
1628     for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
1629         if (i == num_slot) {
1630             update = type;
1631         } else {
1632             update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
1633         }
1634 
1635         slot = &touch_slots[i];
1636 
1637         if (slot->tracking_id == -1) {
1638             continue;
1639         }
1640 
1641         if (update == INPUT_MULTI_TOUCH_TYPE_END) {
1642             slot->tracking_id = -1;
1643             qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1644             needs_sync = true;
1645         } else {
1646             qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1647             qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
1648             qemu_input_queue_mtt_abs(con,
1649                                     INPUT_AXIS_X, (int) slot->x,
1650                                     0, width,
1651                                     i, slot->tracking_id);
1652             qemu_input_queue_mtt_abs(con,
1653                                     INPUT_AXIS_Y, (int) slot->y,
1654                                     0, height,
1655                                     i, slot->tracking_id);
1656             needs_sync = true;
1657         }
1658     }
1659 
1660     if (needs_sync) {
1661         qemu_input_event_sync();
1662     }
1663 }
1664 
1665 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
1666 {
1667     /* display has opengl support */
1668     assert(con);
1669     if (con->gl) {
1670         error_report("The console already has an OpenGL context.");
1671         exit(1);
1672     }
1673     con->gl = gl;
1674 }
1675 
1676 static void
1677 dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con)
1678 {
1679     if (con && con->cursor && dcl->ops->dpy_cursor_define) {
1680         dcl->ops->dpy_cursor_define(dcl, con->cursor);
1681     }
1682     if (con && dcl->ops->dpy_mouse_set) {
1683         dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
1684     }
1685 }
1686 void register_displaychangelistener(DisplayChangeListener *dcl)
1687 {
1688     QemuConsole *con;
1689 
1690     assert(!dcl->ds);
1691 
1692     trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1693     dcl->ds = get_alloc_displaystate();
1694     QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1695     gui_setup_refresh(dcl->ds);
1696     if (dcl->con) {
1697         dcl->con->dcls++;
1698         con = dcl->con;
1699     } else {
1700         con = active_console;
1701     }
1702     displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL);
1703     if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
1704         dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(con));
1705     }
1706     text_console_update_cursor(NULL);
1707 }
1708 
1709 void update_displaychangelistener(DisplayChangeListener *dcl,
1710                                   uint64_t interval)
1711 {
1712     DisplayState *ds = dcl->ds;
1713 
1714     dcl->update_interval = interval;
1715     if (!ds->refreshing && ds->update_interval > interval) {
1716         timer_mod(ds->gui_timer, ds->last_update + interval);
1717     }
1718 }
1719 
1720 void unregister_displaychangelistener(DisplayChangeListener *dcl)
1721 {
1722     DisplayState *ds = dcl->ds;
1723     trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1724     if (dcl->con) {
1725         dcl->con->dcls--;
1726     }
1727     QLIST_REMOVE(dcl, next);
1728     dcl->ds = NULL;
1729     gui_setup_refresh(ds);
1730 }
1731 
1732 static void dpy_set_ui_info_timer(void *opaque)
1733 {
1734     QemuConsole *con = opaque;
1735     uint32_t head = qemu_console_get_head(con);
1736 
1737     con->hw_ops->ui_info(con->hw, head, &con->ui_info);
1738 }
1739 
1740 bool dpy_ui_info_supported(QemuConsole *con)
1741 {
1742     if (con == NULL) {
1743         con = active_console;
1744     }
1745 
1746     return con->hw_ops->ui_info != NULL;
1747 }
1748 
1749 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
1750 {
1751     if (con == NULL) {
1752         con = active_console;
1753     }
1754 
1755     return &con->ui_info;
1756 }
1757 
1758 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
1759 {
1760     if (con == NULL) {
1761         con = active_console;
1762     }
1763 
1764     if (!dpy_ui_info_supported(con)) {
1765         return -1;
1766     }
1767     if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
1768         /* nothing changed -- ignore */
1769         return 0;
1770     }
1771 
1772     /*
1773      * Typically we get a flood of these as the user resizes the window.
1774      * Wait until the dust has settled (one second without updates), then
1775      * go notify the guest.
1776      */
1777     con->ui_info = *info;
1778     timer_mod(con->ui_timer,
1779               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0));
1780     return 0;
1781 }
1782 
1783 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1784 {
1785     DisplayState *s = con->ds;
1786     DisplayChangeListener *dcl;
1787     int width = qemu_console_get_width(con, x + w);
1788     int height = qemu_console_get_height(con, y + h);
1789 
1790     x = MAX(x, 0);
1791     y = MAX(y, 0);
1792     x = MIN(x, width);
1793     y = MIN(y, height);
1794     w = MIN(w, width - x);
1795     h = MIN(h, height - y);
1796 
1797     if (!qemu_console_is_visible(con)) {
1798         return;
1799     }
1800     dpy_gfx_update_texture(con, con->surface, x, y, w, h);
1801     QLIST_FOREACH(dcl, &s->listeners, next) {
1802         if (con != (dcl->con ? dcl->con : active_console)) {
1803             continue;
1804         }
1805         if (dcl->ops->dpy_gfx_update) {
1806             dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1807         }
1808     }
1809 }
1810 
1811 void dpy_gfx_update_full(QemuConsole *con)
1812 {
1813     int w = qemu_console_get_width(con, 0);
1814     int h = qemu_console_get_height(con, 0);
1815 
1816     dpy_gfx_update(con, 0, 0, w, h);
1817 }
1818 
1819 void dpy_gfx_replace_surface(QemuConsole *con,
1820                              DisplaySurface *surface)
1821 {
1822     static const char placeholder_msg[] = "Display output is not active.";
1823     DisplayState *s = con->ds;
1824     DisplaySurface *old_surface = con->surface;
1825     DisplaySurface *new_surface = surface;
1826     DisplayChangeListener *dcl;
1827     int width;
1828     int height;
1829 
1830     if (!surface) {
1831         if (old_surface) {
1832             width = surface_width(old_surface);
1833             height = surface_height(old_surface);
1834         } else {
1835             width = 640;
1836             height = 480;
1837         }
1838 
1839         new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
1840     }
1841 
1842     assert(old_surface != new_surface);
1843 
1844     con->scanout.kind = SCANOUT_SURFACE;
1845     con->surface = new_surface;
1846     dpy_gfx_create_texture(con, new_surface);
1847     QLIST_FOREACH(dcl, &s->listeners, next) {
1848         if (con != (dcl->con ? dcl->con : active_console)) {
1849             continue;
1850         }
1851         displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
1852     }
1853     dpy_gfx_destroy_texture(con, old_surface);
1854     qemu_free_displaysurface(old_surface);
1855 }
1856 
1857 bool dpy_gfx_check_format(QemuConsole *con,
1858                           pixman_format_code_t format)
1859 {
1860     DisplayChangeListener *dcl;
1861     DisplayState *s = con->ds;
1862 
1863     QLIST_FOREACH(dcl, &s->listeners, next) {
1864         if (dcl->con && dcl->con != con) {
1865             /* dcl bound to another console -> skip */
1866             continue;
1867         }
1868         if (dcl->ops->dpy_gfx_check_format) {
1869             if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
1870                 return false;
1871             }
1872         } else {
1873             /* default is to allow native 32 bpp only */
1874             if (format != qemu_default_pixman_format(32, true)) {
1875                 return false;
1876             }
1877         }
1878     }
1879     return true;
1880 }
1881 
1882 static void dpy_refresh(DisplayState *s)
1883 {
1884     DisplayChangeListener *dcl;
1885 
1886     QLIST_FOREACH(dcl, &s->listeners, next) {
1887         if (dcl->ops->dpy_refresh) {
1888             dcl->ops->dpy_refresh(dcl);
1889         }
1890     }
1891 }
1892 
1893 void dpy_text_cursor(QemuConsole *con, int x, int y)
1894 {
1895     DisplayState *s = con->ds;
1896     DisplayChangeListener *dcl;
1897 
1898     if (!qemu_console_is_visible(con)) {
1899         return;
1900     }
1901     QLIST_FOREACH(dcl, &s->listeners, next) {
1902         if (con != (dcl->con ? dcl->con : active_console)) {
1903             continue;
1904         }
1905         if (dcl->ops->dpy_text_cursor) {
1906             dcl->ops->dpy_text_cursor(dcl, x, y);
1907         }
1908     }
1909 }
1910 
1911 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1912 {
1913     DisplayState *s = con->ds;
1914     DisplayChangeListener *dcl;
1915 
1916     if (!qemu_console_is_visible(con)) {
1917         return;
1918     }
1919     QLIST_FOREACH(dcl, &s->listeners, next) {
1920         if (con != (dcl->con ? dcl->con : active_console)) {
1921             continue;
1922         }
1923         if (dcl->ops->dpy_text_update) {
1924             dcl->ops->dpy_text_update(dcl, x, y, w, h);
1925         }
1926     }
1927 }
1928 
1929 void dpy_text_resize(QemuConsole *con, int w, int h)
1930 {
1931     DisplayState *s = con->ds;
1932     DisplayChangeListener *dcl;
1933 
1934     if (!qemu_console_is_visible(con)) {
1935         return;
1936     }
1937     QLIST_FOREACH(dcl, &s->listeners, next) {
1938         if (con != (dcl->con ? dcl->con : active_console)) {
1939             continue;
1940         }
1941         if (dcl->ops->dpy_text_resize) {
1942             dcl->ops->dpy_text_resize(dcl, w, h);
1943         }
1944     }
1945 }
1946 
1947 void dpy_mouse_set(QemuConsole *c, int x, int y, int on)
1948 {
1949     QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
1950     DisplayState *s = c->ds;
1951     DisplayChangeListener *dcl;
1952 
1953     con->cursor_x = x;
1954     con->cursor_y = y;
1955     con->cursor_on = on;
1956     if (!qemu_console_is_visible(c)) {
1957         return;
1958     }
1959     QLIST_FOREACH(dcl, &s->listeners, next) {
1960         if (c != (dcl->con ? dcl->con : active_console)) {
1961             continue;
1962         }
1963         if (dcl->ops->dpy_mouse_set) {
1964             dcl->ops->dpy_mouse_set(dcl, x, y, on);
1965         }
1966     }
1967 }
1968 
1969 void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor)
1970 {
1971     QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
1972     DisplayState *s = c->ds;
1973     DisplayChangeListener *dcl;
1974 
1975     cursor_unref(con->cursor);
1976     con->cursor = cursor_ref(cursor);
1977     if (!qemu_console_is_visible(c)) {
1978         return;
1979     }
1980     QLIST_FOREACH(dcl, &s->listeners, next) {
1981         if (c != (dcl->con ? dcl->con : active_console)) {
1982             continue;
1983         }
1984         if (dcl->ops->dpy_cursor_define) {
1985             dcl->ops->dpy_cursor_define(dcl, cursor);
1986         }
1987     }
1988 }
1989 
1990 bool dpy_cursor_define_supported(QemuConsole *con)
1991 {
1992     DisplayState *s = con->ds;
1993     DisplayChangeListener *dcl;
1994 
1995     QLIST_FOREACH(dcl, &s->listeners, next) {
1996         if (dcl->ops->dpy_cursor_define) {
1997             return true;
1998         }
1999     }
2000     return false;
2001 }
2002 
2003 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
2004                                 struct QEMUGLParams *qparams)
2005 {
2006     assert(con->gl);
2007     return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
2008 }
2009 
2010 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
2011 {
2012     assert(con->gl);
2013     con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
2014 }
2015 
2016 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
2017 {
2018     assert(con->gl);
2019     return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
2020 }
2021 
2022 void dpy_gl_scanout_disable(QemuConsole *con)
2023 {
2024     DisplayState *s = con->ds;
2025     DisplayChangeListener *dcl;
2026 
2027     if (con->scanout.kind != SCANOUT_SURFACE) {
2028         con->scanout.kind = SCANOUT_NONE;
2029     }
2030     QLIST_FOREACH(dcl, &s->listeners, next) {
2031         if (con != (dcl->con ? dcl->con : active_console)) {
2032             continue;
2033         }
2034         if (dcl->ops->dpy_gl_scanout_disable) {
2035             dcl->ops->dpy_gl_scanout_disable(dcl);
2036         }
2037     }
2038 }
2039 
2040 void dpy_gl_scanout_texture(QemuConsole *con,
2041                             uint32_t backing_id,
2042                             bool backing_y_0_top,
2043                             uint32_t backing_width,
2044                             uint32_t backing_height,
2045                             uint32_t x, uint32_t y,
2046                             uint32_t width, uint32_t height,
2047                             void *d3d_tex2d)
2048 {
2049     DisplayState *s = con->ds;
2050     DisplayChangeListener *dcl;
2051 
2052     con->scanout.kind = SCANOUT_TEXTURE;
2053     con->scanout.texture = (ScanoutTexture) {
2054         backing_id, backing_y_0_top, backing_width, backing_height,
2055         x, y, width, height, d3d_tex2d,
2056     };
2057     QLIST_FOREACH(dcl, &s->listeners, next) {
2058         if (con != (dcl->con ? dcl->con : active_console)) {
2059             continue;
2060         }
2061         if (dcl->ops->dpy_gl_scanout_texture) {
2062             dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
2063                                              backing_y_0_top,
2064                                              backing_width, backing_height,
2065                                              x, y, width, height,
2066                                              d3d_tex2d);
2067         }
2068     }
2069 }
2070 
2071 void dpy_gl_scanout_dmabuf(QemuConsole *con,
2072                            QemuDmaBuf *dmabuf)
2073 {
2074     DisplayState *s = con->ds;
2075     DisplayChangeListener *dcl;
2076 
2077     con->scanout.kind = SCANOUT_DMABUF;
2078     con->scanout.dmabuf = dmabuf;
2079     QLIST_FOREACH(dcl, &s->listeners, next) {
2080         if (con != (dcl->con ? dcl->con : active_console)) {
2081             continue;
2082         }
2083         if (dcl->ops->dpy_gl_scanout_dmabuf) {
2084             dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
2085         }
2086     }
2087 }
2088 
2089 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
2090                           bool have_hot, uint32_t hot_x, uint32_t hot_y)
2091 {
2092     DisplayState *s = con->ds;
2093     DisplayChangeListener *dcl;
2094 
2095     QLIST_FOREACH(dcl, &s->listeners, next) {
2096         if (con != (dcl->con ? dcl->con : active_console)) {
2097             continue;
2098         }
2099         if (dcl->ops->dpy_gl_cursor_dmabuf) {
2100             dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf,
2101                                            have_hot, hot_x, hot_y);
2102         }
2103     }
2104 }
2105 
2106 void dpy_gl_cursor_position(QemuConsole *con,
2107                             uint32_t pos_x, uint32_t pos_y)
2108 {
2109     DisplayState *s = con->ds;
2110     DisplayChangeListener *dcl;
2111 
2112     QLIST_FOREACH(dcl, &s->listeners, next) {
2113         if (con != (dcl->con ? dcl->con : active_console)) {
2114             continue;
2115         }
2116         if (dcl->ops->dpy_gl_cursor_position) {
2117             dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y);
2118         }
2119     }
2120 }
2121 
2122 void dpy_gl_release_dmabuf(QemuConsole *con,
2123                           QemuDmaBuf *dmabuf)
2124 {
2125     DisplayState *s = con->ds;
2126     DisplayChangeListener *dcl;
2127 
2128     QLIST_FOREACH(dcl, &s->listeners, next) {
2129         if (con != (dcl->con ? dcl->con : active_console)) {
2130             continue;
2131         }
2132         if (dcl->ops->dpy_gl_release_dmabuf) {
2133             dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf);
2134         }
2135     }
2136 }
2137 
2138 void dpy_gl_update(QemuConsole *con,
2139                    uint32_t x, uint32_t y, uint32_t w, uint32_t h)
2140 {
2141     DisplayState *s = con->ds;
2142     DisplayChangeListener *dcl;
2143 
2144     assert(con->gl);
2145 
2146     graphic_hw_gl_block(con, true);
2147     QLIST_FOREACH(dcl, &s->listeners, next) {
2148         if (con != (dcl->con ? dcl->con : active_console)) {
2149             continue;
2150         }
2151         if (dcl->ops->dpy_gl_update) {
2152             dcl->ops->dpy_gl_update(dcl, x, y, w, h);
2153         }
2154     }
2155     graphic_hw_gl_block(con, false);
2156 }
2157 
2158 /***********************************************************/
2159 /* register display */
2160 
2161 /* console.c internal use only */
2162 static DisplayState *get_alloc_displaystate(void)
2163 {
2164     if (!display_state) {
2165         display_state = g_new0(DisplayState, 1);
2166     }
2167     return display_state;
2168 }
2169 
2170 /*
2171  * Called by main(), after creating QemuConsoles
2172  * and before initializing ui (sdl/vnc/...).
2173  */
2174 DisplayState *init_displaystate(void)
2175 {
2176     gchar *name;
2177     QemuConsole *con;
2178 
2179     QTAILQ_FOREACH(con, &consoles, next) {
2180         /* Hook up into the qom tree here (not in object_new()), once
2181          * all QemuConsoles are created and the order / numbering
2182          * doesn't change any more */
2183         name = g_strdup_printf("console[%d]", con->index);
2184         object_property_add_child(container_get(object_get_root(), "/backend"),
2185                                   name, OBJECT(con));
2186         g_free(name);
2187     }
2188 
2189     return display_state;
2190 }
2191 
2192 void graphic_console_set_hwops(QemuConsole *con,
2193                                const GraphicHwOps *hw_ops,
2194                                void *opaque)
2195 {
2196     con->hw_ops = hw_ops;
2197     con->hw = opaque;
2198 }
2199 
2200 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
2201                                   const GraphicHwOps *hw_ops,
2202                                   void *opaque)
2203 {
2204     static const char noinit[] =
2205         "Guest has not initialized the display (yet).";
2206     int width = 640;
2207     int height = 480;
2208     QemuConsole *s;
2209     DisplaySurface *surface;
2210 
2211     s = qemu_graphic_console_lookup_unused();
2212     if (s) {
2213         trace_console_gfx_reuse(s->index);
2214         width = qemu_console_get_width(s, 0);
2215         height = qemu_console_get_height(s, 0);
2216     } else {
2217         trace_console_gfx_new();
2218         s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE);
2219     }
2220     QEMU_GRAPHIC_CONSOLE(s)->head = head;
2221     graphic_console_set_hwops(s, hw_ops, opaque);
2222     if (dev) {
2223         object_property_set_link(OBJECT(s), "device", OBJECT(dev),
2224                                  &error_abort);
2225     }
2226 
2227     surface = qemu_create_placeholder_surface(width, height, noinit);
2228     dpy_gfx_replace_surface(s, surface);
2229     s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2230                                        graphic_hw_gl_unblock_timer, s);
2231     return s;
2232 }
2233 
2234 static const GraphicHwOps unused_ops = {
2235     /* no callbacks */
2236 };
2237 
2238 void graphic_console_close(QemuConsole *con)
2239 {
2240     static const char unplugged[] =
2241         "Guest display has been unplugged";
2242     DisplaySurface *surface;
2243     int width = qemu_console_get_width(con, 640);
2244     int height = qemu_console_get_height(con, 480);
2245 
2246     trace_console_gfx_close(con->index);
2247     object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
2248     graphic_console_set_hwops(con, &unused_ops, NULL);
2249 
2250     if (con->gl) {
2251         dpy_gl_scanout_disable(con);
2252     }
2253     surface = qemu_create_placeholder_surface(width, height, unplugged);
2254     dpy_gfx_replace_surface(con, surface);
2255 }
2256 
2257 QemuConsole *qemu_console_lookup_by_index(unsigned int index)
2258 {
2259     QemuConsole *con;
2260 
2261     QTAILQ_FOREACH(con, &consoles, next) {
2262         if (con->index == index) {
2263             return con;
2264         }
2265     }
2266     return NULL;
2267 }
2268 
2269 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
2270 {
2271     QemuConsole *con;
2272     Object *obj;
2273     uint32_t h;
2274 
2275     QTAILQ_FOREACH(con, &consoles, next) {
2276         obj = object_property_get_link(OBJECT(con),
2277                                        "device", &error_abort);
2278         if (DEVICE(obj) != dev) {
2279             continue;
2280         }
2281         h = object_property_get_uint(OBJECT(con),
2282                                      "head", &error_abort);
2283         if (h != head) {
2284             continue;
2285         }
2286         return con;
2287     }
2288     return NULL;
2289 }
2290 
2291 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
2292                                                 uint32_t head, Error **errp)
2293 {
2294     DeviceState *dev;
2295     QemuConsole *con;
2296 
2297     dev = qdev_find_recursive(sysbus_get_default(), device_id);
2298     if (dev == NULL) {
2299         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
2300                   "Device '%s' not found", device_id);
2301         return NULL;
2302     }
2303 
2304     con = qemu_console_lookup_by_device(dev, head);
2305     if (con == NULL) {
2306         error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
2307                    device_id, head);
2308         return NULL;
2309     }
2310 
2311     return con;
2312 }
2313 
2314 static QemuConsole *qemu_graphic_console_lookup_unused(void)
2315 {
2316     QemuConsole *con;
2317     Object *obj;
2318 
2319     QTAILQ_FOREACH(con, &consoles, next) {
2320         if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) {
2321             continue;
2322         }
2323         obj = object_property_get_link(OBJECT(con),
2324                                        "device", &error_abort);
2325         if (obj != NULL) {
2326             continue;
2327         }
2328         return con;
2329     }
2330     return NULL;
2331 }
2332 
2333 QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
2334 {
2335     if (con == NULL) {
2336         con = active_console;
2337     }
2338     return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL;
2339 }
2340 
2341 bool qemu_console_is_visible(QemuConsole *con)
2342 {
2343     return (con == active_console) || (con->dcls > 0);
2344 }
2345 
2346 bool qemu_console_is_graphic(QemuConsole *con)
2347 {
2348     if (con == NULL) {
2349         con = active_console;
2350     }
2351     return con && QEMU_IS_GRAPHIC_CONSOLE(con);
2352 }
2353 
2354 bool qemu_console_is_fixedsize(QemuConsole *con)
2355 {
2356     if (con == NULL) {
2357         con = active_console;
2358     }
2359     return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con));
2360 }
2361 
2362 bool qemu_console_is_gl_blocked(QemuConsole *con)
2363 {
2364     assert(con != NULL);
2365     return con->gl_block;
2366 }
2367 
2368 bool qemu_console_is_multihead(DeviceState *dev)
2369 {
2370     QemuConsole *con;
2371     Object *obj;
2372     uint32_t f = 0xffffffff;
2373     uint32_t h;
2374 
2375     QTAILQ_FOREACH(con, &consoles, next) {
2376         obj = object_property_get_link(OBJECT(con),
2377                                        "device", &error_abort);
2378         if (DEVICE(obj) != dev) {
2379             continue;
2380         }
2381 
2382         h = object_property_get_uint(OBJECT(con),
2383                                      "head", &error_abort);
2384         if (f == 0xffffffff) {
2385             f = h;
2386         } else if (h != f) {
2387             return true;
2388         }
2389     }
2390     return false;
2391 }
2392 
2393 char *qemu_console_get_label(QemuConsole *con)
2394 {
2395     if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
2396         QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con);
2397         if (c->device) {
2398             DeviceState *dev;
2399             bool multihead;
2400 
2401             dev = DEVICE(c->device);
2402             multihead = qemu_console_is_multihead(dev);
2403             if (multihead) {
2404                 return g_strdup_printf("%s.%d", dev->id ?
2405                                        dev->id :
2406                                        object_get_typename(c->device),
2407                                        c->head);
2408             } else {
2409                 return g_strdup_printf("%s", dev->id ?
2410                                        dev->id :
2411                                        object_get_typename(c->device));
2412             }
2413         }
2414         return g_strdup("VGA");
2415     } else if (QEMU_IS_TEXT_CONSOLE(con)) {
2416         QemuTextConsole *c = QEMU_TEXT_CONSOLE(con);
2417         if (c->chr && c->chr->label) {
2418             return g_strdup(c->chr->label);
2419         }
2420     }
2421 
2422     return g_strdup_printf("vc%d", con->index);
2423 }
2424 
2425 int qemu_console_get_index(QemuConsole *con)
2426 {
2427     if (con == NULL) {
2428         con = active_console;
2429     }
2430     return con ? con->index : -1;
2431 }
2432 
2433 uint32_t qemu_console_get_head(QemuConsole *con)
2434 {
2435     if (con == NULL) {
2436         con = active_console;
2437     }
2438     if (con == NULL) {
2439         return -1;
2440     }
2441     if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
2442         return QEMU_GRAPHIC_CONSOLE(con)->head;
2443     }
2444     return 0;
2445 }
2446 
2447 int qemu_console_get_width(QemuConsole *con, int fallback)
2448 {
2449     if (con == NULL) {
2450         con = active_console;
2451     }
2452     if (con == NULL) {
2453         return fallback;
2454     }
2455     switch (con->scanout.kind) {
2456     case SCANOUT_DMABUF:
2457         return con->scanout.dmabuf->width;
2458     case SCANOUT_TEXTURE:
2459         return con->scanout.texture.width;
2460     case SCANOUT_SURFACE:
2461         return surface_width(con->surface);
2462     default:
2463         return fallback;
2464     }
2465 }
2466 
2467 int qemu_console_get_height(QemuConsole *con, int fallback)
2468 {
2469     if (con == NULL) {
2470         con = active_console;
2471     }
2472     if (con == NULL) {
2473         return fallback;
2474     }
2475     switch (con->scanout.kind) {
2476     case SCANOUT_DMABUF:
2477         return con->scanout.dmabuf->height;
2478     case SCANOUT_TEXTURE:
2479         return con->scanout.texture.height;
2480     case SCANOUT_SURFACE:
2481         return surface_height(con->surface);
2482     default:
2483         return fallback;
2484     }
2485 }
2486 
2487 static void vc_chr_accept_input(Chardev *chr)
2488 {
2489     VCChardev *drv = VC_CHARDEV(chr);
2490 
2491     kbd_send_chars(drv->console);
2492 }
2493 
2494 static void vc_chr_set_echo(Chardev *chr, bool echo)
2495 {
2496     VCChardev *drv = VC_CHARDEV(chr);
2497 
2498     drv->console->echo = echo;
2499 }
2500 
2501 int qemu_invalidate_text_consoles(void)
2502 {
2503     QemuConsole *s;
2504     int count = 0;
2505 
2506     QTAILQ_FOREACH(s, &consoles, next) {
2507         if (qemu_console_is_graphic(s) ||
2508             !qemu_console_is_visible(s)) {
2509             continue;
2510         }
2511         count++;
2512         graphic_hw_invalidate(s);
2513     }
2514 
2515     return count;
2516 }
2517 
2518 static void text_console_update_cursor(void *opaque)
2519 {
2520     cursor_visible_phase = !cursor_visible_phase;
2521 
2522     if (qemu_invalidate_text_consoles()) {
2523         timer_mod(cursor_timer,
2524                   qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
2525     }
2526 }
2527 
2528 static void vc_chr_open(Chardev *chr,
2529                         ChardevBackend *backend,
2530                         bool *be_opened,
2531                         Error **errp)
2532 {
2533     ChardevVC *vc = backend->u.vc.data;
2534     VCChardev *drv = VC_CHARDEV(chr);
2535     QemuTextConsole *s;
2536     unsigned width = 0;
2537     unsigned height = 0;
2538 
2539     if (vc->has_width) {
2540         width = vc->width;
2541     } else if (vc->has_cols) {
2542         width = vc->cols * FONT_WIDTH;
2543     }
2544 
2545     if (vc->has_height) {
2546         height = vc->height;
2547     } else if (vc->has_rows) {
2548         height = vc->rows * FONT_HEIGHT;
2549     }
2550 
2551     trace_console_txt_new(width, height);
2552     if (width == 0 || height == 0) {
2553         s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE));
2554         width = qemu_console_get_width(NULL, 80 * FONT_WIDTH);
2555         height = qemu_console_get_height(NULL, 24 * FONT_HEIGHT);
2556     } else {
2557         s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
2558     }
2559 
2560     dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
2561 
2562     s->chr = chr;
2563     drv->console = s;
2564 
2565     /* set current text attributes to default */
2566     drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
2567     text_console_resize(s);
2568 
2569     if (chr->label) {
2570         char *msg;
2571 
2572         drv->t_attrib.bgcol = QEMU_COLOR_BLUE;
2573         msg = g_strdup_printf("%s console\r\n", chr->label);
2574         qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
2575         g_free(msg);
2576         drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
2577     }
2578 
2579     *be_opened = true;
2580 }
2581 
2582 void qemu_console_resize(QemuConsole *s, int width, int height)
2583 {
2584     DisplaySurface *surface = qemu_console_surface(s);
2585 
2586     assert(QEMU_IS_GRAPHIC_CONSOLE(s));
2587 
2588     if ((s->scanout.kind != SCANOUT_SURFACE ||
2589          (surface && surface->flags & QEMU_ALLOCATED_FLAG)) &&
2590         qemu_console_get_width(s, -1) == width &&
2591         qemu_console_get_height(s, -1) == height) {
2592         return;
2593     }
2594 
2595     surface = qemu_create_displaysurface(width, height);
2596     dpy_gfx_replace_surface(s, surface);
2597 }
2598 
2599 DisplaySurface *qemu_console_surface(QemuConsole *console)
2600 {
2601     switch (console->scanout.kind) {
2602     case SCANOUT_SURFACE:
2603         return console->surface;
2604     default:
2605         return NULL;
2606     }
2607 }
2608 
2609 PixelFormat qemu_default_pixelformat(int bpp)
2610 {
2611     pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
2612     PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
2613     return pf;
2614 }
2615 
2616 static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
2617 
2618 void qemu_display_register(QemuDisplay *ui)
2619 {
2620     assert(ui->type < DISPLAY_TYPE__MAX);
2621     dpys[ui->type] = ui;
2622 }
2623 
2624 bool qemu_display_find_default(DisplayOptions *opts)
2625 {
2626     static DisplayType prio[] = {
2627 #if defined(CONFIG_GTK)
2628         DISPLAY_TYPE_GTK,
2629 #endif
2630 #if defined(CONFIG_SDL)
2631         DISPLAY_TYPE_SDL,
2632 #endif
2633 #if defined(CONFIG_COCOA)
2634         DISPLAY_TYPE_COCOA
2635 #endif
2636     };
2637     int i;
2638 
2639     for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
2640         if (dpys[prio[i]] == NULL) {
2641             Error *local_err = NULL;
2642             int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
2643             if (rv < 0) {
2644                 error_report_err(local_err);
2645             }
2646         }
2647         if (dpys[prio[i]] == NULL) {
2648             continue;
2649         }
2650         opts->type = prio[i];
2651         return true;
2652     }
2653     return false;
2654 }
2655 
2656 void qemu_display_early_init(DisplayOptions *opts)
2657 {
2658     assert(opts->type < DISPLAY_TYPE__MAX);
2659     if (opts->type == DISPLAY_TYPE_NONE) {
2660         return;
2661     }
2662     if (dpys[opts->type] == NULL) {
2663         Error *local_err = NULL;
2664         int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
2665         if (rv < 0) {
2666             error_report_err(local_err);
2667         }
2668     }
2669     if (dpys[opts->type] == NULL) {
2670         error_report("Display '%s' is not available.",
2671                      DisplayType_str(opts->type));
2672         exit(1);
2673     }
2674     if (dpys[opts->type]->early_init) {
2675         dpys[opts->type]->early_init(opts);
2676     }
2677 }
2678 
2679 void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
2680 {
2681     assert(opts->type < DISPLAY_TYPE__MAX);
2682     if (opts->type == DISPLAY_TYPE_NONE) {
2683         return;
2684     }
2685     assert(dpys[opts->type] != NULL);
2686     dpys[opts->type]->init(ds, opts);
2687 }
2688 
2689 void qemu_display_help(void)
2690 {
2691     int idx;
2692 
2693     printf("Available display backend types:\n");
2694     printf("none\n");
2695     for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
2696         if (!dpys[idx]) {
2697             Error *local_err = NULL;
2698             int rv = ui_module_load(DisplayType_str(idx), &local_err);
2699             if (rv < 0) {
2700                 error_report_err(local_err);
2701             }
2702         }
2703         if (dpys[idx]) {
2704             printf("%s\n",  DisplayType_str(dpys[idx]->type));
2705         }
2706     }
2707 }
2708 
2709 static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp)
2710 {
2711     int val;
2712     ChardevVC *vc;
2713 
2714     backend->type = CHARDEV_BACKEND_KIND_VC;
2715     vc = backend->u.vc.data = g_new0(ChardevVC, 1);
2716     qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
2717 
2718     val = qemu_opt_get_number(opts, "width", 0);
2719     if (val != 0) {
2720         vc->has_width = true;
2721         vc->width = val;
2722     }
2723 
2724     val = qemu_opt_get_number(opts, "height", 0);
2725     if (val != 0) {
2726         vc->has_height = true;
2727         vc->height = val;
2728     }
2729 
2730     val = qemu_opt_get_number(opts, "cols", 0);
2731     if (val != 0) {
2732         vc->has_cols = true;
2733         vc->cols = val;
2734     }
2735 
2736     val = qemu_opt_get_number(opts, "rows", 0);
2737     if (val != 0) {
2738         vc->has_rows = true;
2739         vc->rows = val;
2740     }
2741 }
2742 
2743 static void char_vc_class_init(ObjectClass *oc, void *data)
2744 {
2745     ChardevClass *cc = CHARDEV_CLASS(oc);
2746 
2747     cc->parse = vc_chr_parse;
2748     cc->open = vc_chr_open;
2749     cc->chr_write = vc_chr_write;
2750     cc->chr_accept_input = vc_chr_accept_input;
2751     cc->chr_set_echo = vc_chr_set_echo;
2752 }
2753 
2754 static const TypeInfo char_vc_type_info = {
2755     .name = TYPE_CHARDEV_VC,
2756     .parent = TYPE_CHARDEV,
2757     .instance_size = sizeof(VCChardev),
2758     .class_init = char_vc_class_init,
2759 };
2760 
2761 void qemu_console_early_init(void)
2762 {
2763     /* set the default vc driver */
2764     if (!object_class_by_name(TYPE_CHARDEV_VC)) {
2765         type_register(&char_vc_type_info);
2766     }
2767 }
2768