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