xref: /openbmc/qemu/ui/console.c (revision 7d87775f)
1 /*
2  * QEMU graphical console
3  *
4  * Copyright (c) 2004 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 #include "qemu/osdep.h"
26 #include "ui/console.h"
27 #include "hw/qdev-core.h"
28 #include "qapi/error.h"
29 #include "qapi/qapi-commands-ui.h"
30 #include "qapi/visitor.h"
31 #include "qemu/coroutine.h"
32 #include "qemu/error-report.h"
33 #include "qemu/main-loop.h"
34 #include "qemu/module.h"
35 #include "qemu/option.h"
36 #include "chardev/char.h"
37 #include "trace.h"
38 #include "exec/memory.h"
39 #include "qom/object.h"
40 #include "qemu/memfd.h"
41 
42 #include "console-priv.h"
43 
44 OBJECT_DEFINE_ABSTRACT_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT)
45 
46 typedef struct QemuGraphicConsole {
47     QemuConsole parent;
48 
49     Object *device;
50     uint32_t head;
51 
52     QEMUCursor *cursor;
53     int cursor_x, cursor_y;
54     bool cursor_on;
55 } QemuGraphicConsole;
56 
57 typedef QemuConsoleClass QemuGraphicConsoleClass;
58 
59 OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOLE, QEMU_CONSOLE)
60 
61 struct DisplayState {
62     QEMUTimer *gui_timer;
63     uint64_t last_update;
64     uint64_t update_interval;
65     bool refreshing;
66 
67     QLIST_HEAD(, DisplayChangeListener) listeners;
68 };
69 
70 static DisplayState *display_state;
71 static QTAILQ_HEAD(, QemuConsole) consoles =
72     QTAILQ_HEAD_INITIALIZER(consoles);
73 
74 static void dpy_refresh(DisplayState *s);
75 static DisplayState *get_alloc_displaystate(void);
76 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
77 static bool console_compatible_with(QemuConsole *con,
78                                     DisplayChangeListener *dcl, Error **errp);
79 static QemuConsole *qemu_graphic_console_lookup_unused(void);
80 static void dpy_set_ui_info_timer(void *opaque);
81 
82 static void gui_update(void *opaque)
83 {
84     uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
85     uint64_t dcl_interval;
86     DisplayState *ds = opaque;
87     DisplayChangeListener *dcl;
88 
89     ds->refreshing = true;
90     dpy_refresh(ds);
91     ds->refreshing = false;
92 
93     QLIST_FOREACH(dcl, &ds->listeners, next) {
94         dcl_interval = dcl->update_interval ?
95             dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
96         if (interval > dcl_interval) {
97             interval = dcl_interval;
98         }
99     }
100     if (ds->update_interval != interval) {
101         ds->update_interval = interval;
102         trace_console_refresh(interval);
103     }
104     ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
105     timer_mod(ds->gui_timer, ds->last_update + interval);
106 }
107 
108 static void gui_setup_refresh(DisplayState *ds)
109 {
110     DisplayChangeListener *dcl;
111     bool need_timer = false;
112 
113     QLIST_FOREACH(dcl, &ds->listeners, next) {
114         if (dcl->ops->dpy_refresh != NULL) {
115             need_timer = true;
116         }
117     }
118 
119     if (need_timer && ds->gui_timer == NULL) {
120         ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
121         timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
122     }
123     if (!need_timer && ds->gui_timer != NULL) {
124         timer_free(ds->gui_timer);
125         ds->gui_timer = NULL;
126     }
127 }
128 
129 void graphic_hw_update_done(QemuConsole *con)
130 {
131     if (con) {
132         qemu_co_enter_all(&con->dump_queue, NULL);
133     }
134 }
135 
136 void graphic_hw_update(QemuConsole *con)
137 {
138     bool async = false;
139     if (!con) {
140         return;
141     }
142     if (con->hw_ops->gfx_update) {
143         con->hw_ops->gfx_update(con->hw);
144         async = con->hw_ops->gfx_update_async;
145     }
146     if (!async) {
147         graphic_hw_update_done(con);
148     }
149 }
150 
151 static void graphic_hw_update_bh(void *con)
152 {
153     graphic_hw_update(con);
154 }
155 
156 void qemu_console_co_wait_update(QemuConsole *con)
157 {
158     if (qemu_co_queue_empty(&con->dump_queue)) {
159         /* Defer the update, it will restart the pending coroutines */
160         aio_bh_schedule_oneshot(qemu_get_aio_context(),
161                                 graphic_hw_update_bh, con);
162     }
163     qemu_co_queue_wait(&con->dump_queue, NULL);
164 
165 }
166 
167 static void graphic_hw_gl_unblock_timer(void *opaque)
168 {
169     warn_report("console: no gl-unblock within one second");
170 }
171 
172 void graphic_hw_gl_block(QemuConsole *con, bool block)
173 {
174     uint64_t timeout;
175     assert(con != NULL);
176 
177     if (block) {
178         con->gl_block++;
179     } else {
180         con->gl_block--;
181     }
182     assert(con->gl_block >= 0);
183     if (!con->hw_ops->gl_block) {
184         return;
185     }
186     if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) {
187         return;
188     }
189     con->hw_ops->gl_block(con->hw, block);
190 
191     if (block) {
192         timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
193         timeout += 1000; /* one sec */
194         timer_mod(con->gl_unblock_timer, timeout);
195     } else {
196         timer_del(con->gl_unblock_timer);
197     }
198 }
199 
200 int qemu_console_get_window_id(QemuConsole *con)
201 {
202     return con->window_id;
203 }
204 
205 void qemu_console_set_window_id(QemuConsole *con, int window_id)
206 {
207     con->window_id = window_id;
208 }
209 
210 void graphic_hw_invalidate(QemuConsole *con)
211 {
212     if (con && con->hw_ops->invalidate) {
213         con->hw_ops->invalidate(con->hw);
214     }
215 }
216 
217 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
218 {
219     if (con && con->hw_ops->text_update) {
220         con->hw_ops->text_update(con->hw, chardata);
221     }
222 }
223 
224 static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl,
225                                              struct DisplaySurface *new_surface,
226                                              bool update)
227 {
228     if (dcl->ops->dpy_gfx_switch) {
229         dcl->ops->dpy_gfx_switch(dcl, new_surface);
230     }
231 
232     if (update && dcl->ops->dpy_gfx_update) {
233         dcl->ops->dpy_gfx_update(dcl, 0, 0,
234                                  surface_width(new_surface),
235                                  surface_height(new_surface));
236     }
237 }
238 
239 static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface)
240 {
241     if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) {
242         con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface);
243     }
244 }
245 
246 static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface)
247 {
248     if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) {
249         con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface);
250     }
251 }
252 
253 static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface,
254                                    int x, int y, int w, int h)
255 {
256     if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) {
257         con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h);
258     }
259 }
260 
261 static void displaychangelistener_display_console(DisplayChangeListener *dcl,
262                                                   Error **errp)
263 {
264     static const char nodev[] =
265         "This VM has no graphic display device.";
266     static DisplaySurface *dummy;
267     QemuConsole *con = dcl->con;
268 
269     if (!con || !console_compatible_with(con, dcl, errp)) {
270         if (!dummy) {
271             dummy = qemu_create_placeholder_surface(640, 480, nodev);
272         }
273         if (con) {
274             dpy_gfx_create_texture(con, dummy);
275         }
276         displaychangelistener_gfx_switch(dcl, dummy, TRUE);
277         return;
278     }
279 
280     dpy_gfx_create_texture(con, con->surface);
281     displaychangelistener_gfx_switch(dcl, con->surface,
282                                      con->scanout.kind == SCANOUT_SURFACE);
283 
284     if (con->scanout.kind == SCANOUT_DMABUF &&
285         displaychangelistener_has_dmabuf(dcl)) {
286         dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf);
287     } else if (con->scanout.kind == SCANOUT_TEXTURE &&
288                dcl->ops->dpy_gl_scanout_texture) {
289         dcl->ops->dpy_gl_scanout_texture(dcl,
290                                          con->scanout.texture.backing_id,
291                                          con->scanout.texture.backing_y_0_top,
292                                          con->scanout.texture.backing_width,
293                                          con->scanout.texture.backing_height,
294                                          con->scanout.texture.x,
295                                          con->scanout.texture.y,
296                                          con->scanout.texture.width,
297                                          con->scanout.texture.height,
298                                          con->scanout.texture.d3d_tex2d);
299     }
300 }
301 
302 void qemu_text_console_put_keysym(QemuTextConsole *s, int keysym)
303 {
304     qemu_text_console_handle_keysym(s, keysym);
305 }
306 
307 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
308     [Q_KEY_CODE_UP]     = QEMU_KEY_UP,
309     [Q_KEY_CODE_DOWN]   = QEMU_KEY_DOWN,
310     [Q_KEY_CODE_RIGHT]  = QEMU_KEY_RIGHT,
311     [Q_KEY_CODE_LEFT]   = QEMU_KEY_LEFT,
312     [Q_KEY_CODE_HOME]   = QEMU_KEY_HOME,
313     [Q_KEY_CODE_END]    = QEMU_KEY_END,
314     [Q_KEY_CODE_PGUP]   = QEMU_KEY_PAGEUP,
315     [Q_KEY_CODE_PGDN]   = QEMU_KEY_PAGEDOWN,
316     [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
317     [Q_KEY_CODE_TAB]    = QEMU_KEY_TAB,
318     [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
319 };
320 
321 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
322     [Q_KEY_CODE_UP]     = QEMU_KEY_CTRL_UP,
323     [Q_KEY_CODE_DOWN]   = QEMU_KEY_CTRL_DOWN,
324     [Q_KEY_CODE_RIGHT]  = QEMU_KEY_CTRL_RIGHT,
325     [Q_KEY_CODE_LEFT]   = QEMU_KEY_CTRL_LEFT,
326     [Q_KEY_CODE_HOME]   = QEMU_KEY_CTRL_HOME,
327     [Q_KEY_CODE_END]    = QEMU_KEY_CTRL_END,
328     [Q_KEY_CODE_PGUP]   = QEMU_KEY_CTRL_PAGEUP,
329     [Q_KEY_CODE_PGDN]   = QEMU_KEY_CTRL_PAGEDOWN,
330 };
331 
332 bool qemu_text_console_put_qcode(QemuTextConsole *s, int qcode, bool ctrl)
333 {
334     int keysym;
335 
336     keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
337     if (keysym == 0) {
338         return false;
339     }
340     qemu_text_console_put_keysym(s, keysym);
341     return true;
342 }
343 
344 void qemu_text_console_put_string(QemuTextConsole *s, const char *str, int len)
345 {
346     int i;
347 
348     for (i = 0; i < len && str[i]; i++) {
349         qemu_text_console_put_keysym(s, str[i]);
350     }
351 }
352 
353 static void
354 qemu_console_register(QemuConsole *c)
355 {
356     int i;
357 
358     if (QTAILQ_EMPTY(&consoles)) {
359         c->index = 0;
360         QTAILQ_INSERT_TAIL(&consoles, c, next);
361     } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) {
362         QemuConsole *last = QTAILQ_LAST(&consoles);
363         c->index = last->index + 1;
364         QTAILQ_INSERT_TAIL(&consoles, c, next);
365     } else {
366         /*
367          * HACK: Put graphical consoles before text consoles.
368          *
369          * Only do that for coldplugged devices.  After initial device
370          * initialization we will not renumber the consoles any more.
371          */
372         QemuConsole *it = QTAILQ_FIRST(&consoles);
373 
374         while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) {
375             it = QTAILQ_NEXT(it, next);
376         }
377         if (QEMU_IS_GRAPHIC_CONSOLE(it)) {
378             /* have no text consoles */
379             c->index = it->index + 1;
380             QTAILQ_INSERT_AFTER(&consoles, it, c, next);
381         } else {
382             c->index = it->index;
383             QTAILQ_INSERT_BEFORE(it, c, next);
384             /* renumber text consoles */
385             for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) {
386                 it->index = i;
387             }
388         }
389     }
390 }
391 
392 static void
393 qemu_console_finalize(Object *obj)
394 {
395     QemuConsole *c = QEMU_CONSOLE(obj);
396 
397     /* TODO: check this code path, and unregister from consoles */
398     g_clear_pointer(&c->surface, qemu_free_displaysurface);
399     g_clear_pointer(&c->gl_unblock_timer, timer_free);
400     g_clear_pointer(&c->ui_timer, timer_free);
401 }
402 
403 static void
404 qemu_console_class_init(ObjectClass *oc, void *data)
405 {
406 }
407 
408 static void
409 qemu_console_init(Object *obj)
410 {
411     QemuConsole *c = QEMU_CONSOLE(obj);
412     DisplayState *ds = get_alloc_displaystate();
413 
414     qemu_co_queue_init(&c->dump_queue);
415     c->ds = ds;
416     c->window_id = -1;
417     c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
418                                dpy_set_ui_info_timer, c);
419     qemu_console_register(c);
420 }
421 
422 static void
423 qemu_graphic_console_finalize(Object *obj)
424 {
425     QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj);
426 
427     g_clear_pointer(&c->device, object_unref);
428 }
429 
430 static void
431 qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name,
432                                    void *opaque, Error **errp)
433 {
434     QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj);
435 
436     visit_type_uint32(v, name, &c->head, errp);
437 }
438 
439 static void
440 qemu_graphic_console_class_init(ObjectClass *oc, void *data)
441 {
442     object_class_property_add_link(oc, "device", TYPE_DEVICE,
443                                    offsetof(QemuGraphicConsole, device),
444                                    object_property_allow_set_link,
445                                    OBJ_PROP_LINK_STRONG);
446     object_class_property_add(oc, "head", "uint32",
447                               qemu_graphic_console_prop_get_head,
448                               NULL, NULL, NULL);
449 }
450 
451 static void
452 qemu_graphic_console_init(Object *obj)
453 {
454 }
455 
456 void qemu_displaysurface_set_share_handle(DisplaySurface *surface,
457                                           qemu_pixman_shareable handle,
458                                           uint32_t offset)
459 {
460     assert(surface->share_handle == SHAREABLE_NONE);
461 
462     surface->share_handle = handle;
463     surface->share_handle_offset = offset;
464 
465 }
466 
467 DisplaySurface *qemu_create_displaysurface(int width, int height)
468 {
469     trace_displaysurface_create(width, height);
470 
471     return qemu_create_displaysurface_from(
472         width, height,
473         PIXMAN_x8r8g8b8,
474         width * 4, NULL
475     );
476 }
477 
478 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
479                                                 pixman_format_code_t format,
480                                                 int linesize, uint8_t *data)
481 {
482     DisplaySurface *surface = g_new0(DisplaySurface, 1);
483 
484     trace_displaysurface_create_from(surface, width, height, format);
485     surface->share_handle = SHAREABLE_NONE;
486 
487     if (data) {
488         surface->image = pixman_image_create_bits(format,
489                                                   width, height,
490                                                   (void *)data, linesize);
491     } else {
492         qemu_pixman_image_new_shareable(&surface->image,
493                                         &surface->share_handle,
494                                         "displaysurface",
495                                         format,
496                                         width,
497                                         height,
498                                         linesize,
499                                         &error_abort);
500         surface->flags = QEMU_ALLOCATED_FLAG;
501     }
502 
503     assert(surface->image != NULL);
504     return surface;
505 }
506 
507 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
508 {
509     DisplaySurface *surface = g_new0(DisplaySurface, 1);
510 
511     trace_displaysurface_create_pixman(surface);
512     surface->share_handle = SHAREABLE_NONE;
513     surface->image = pixman_image_ref(image);
514 
515     return surface;
516 }
517 
518 DisplaySurface *qemu_create_placeholder_surface(int w, int h,
519                                                 const char *msg)
520 {
521     DisplaySurface *surface = qemu_create_displaysurface(w, h);
522 #ifdef CONFIG_PIXMAN
523     pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK;
524     pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY;
525     pixman_image_t *glyph;
526     int len, x, y, i;
527 
528     len = strlen(msg);
529     x = (w / FONT_WIDTH  - len) / 2;
530     y = (h / FONT_HEIGHT - 1)   / 2;
531     for (i = 0; i < len; i++) {
532         glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
533         qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
534                                  x+i, y, FONT_WIDTH, FONT_HEIGHT);
535         qemu_pixman_image_unref(glyph);
536     }
537 #endif
538     surface->flags |= QEMU_PLACEHOLDER_FLAG;
539     return surface;
540 }
541 
542 void qemu_free_displaysurface(DisplaySurface *surface)
543 {
544     if (surface == NULL) {
545         return;
546     }
547     trace_displaysurface_free(surface);
548     qemu_pixman_image_unref(surface->image);
549     g_free(surface);
550 }
551 
552 bool console_has_gl(QemuConsole *con)
553 {
554     return con->gl != NULL;
555 }
556 
557 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
558 {
559     if (dcl->ops->dpy_has_dmabuf) {
560         return dcl->ops->dpy_has_dmabuf(dcl);
561     }
562 
563     if (dcl->ops->dpy_gl_scanout_dmabuf) {
564         return true;
565     }
566 
567     return false;
568 }
569 
570 static bool console_compatible_with(QemuConsole *con,
571                                     DisplayChangeListener *dcl, Error **errp)
572 {
573     int flags;
574 
575     flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
576 
577     if (console_has_gl(con) &&
578         !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
579         error_setg(errp, "Display %s is incompatible with the GL context",
580                    dcl->ops->dpy_name);
581         return false;
582     }
583 
584     if (flags & GRAPHIC_FLAGS_GL &&
585         !console_has_gl(con)) {
586         error_setg(errp, "The console requires a GL context.");
587         return false;
588 
589     }
590 
591     if (flags & GRAPHIC_FLAGS_DMABUF &&
592         !displaychangelistener_has_dmabuf(dcl)) {
593         error_setg(errp, "The console requires display DMABUF support.");
594         return false;
595     }
596 
597     return true;
598 }
599 
600 void console_handle_touch_event(QemuConsole *con,
601                                 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
602                                 uint64_t num_slot,
603                                 int width, int height,
604                                 double x, double y,
605                                 InputMultiTouchType type,
606                                 Error **errp)
607 {
608     struct touch_slot *slot;
609     bool needs_sync = false;
610     int update;
611     int i;
612 
613     if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
614         error_setg(errp,
615                    "Unexpected touch slot number: % " PRId64" >= %d",
616                    num_slot, INPUT_EVENT_SLOTS_MAX);
617         return;
618     }
619 
620     slot = &touch_slots[num_slot];
621     slot->x = x;
622     slot->y = y;
623 
624     if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
625         slot->tracking_id = num_slot;
626     }
627 
628     for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
629         if (i == num_slot) {
630             update = type;
631         } else {
632             update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
633         }
634 
635         slot = &touch_slots[i];
636 
637         if (slot->tracking_id == -1) {
638             continue;
639         }
640 
641         if (update == INPUT_MULTI_TOUCH_TYPE_END) {
642             slot->tracking_id = -1;
643             qemu_input_queue_mtt(con, update, i, slot->tracking_id);
644             needs_sync = true;
645         } else {
646             qemu_input_queue_mtt(con, update, i, slot->tracking_id);
647             qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
648             qemu_input_queue_mtt_abs(con,
649                                     INPUT_AXIS_X, (int) slot->x,
650                                     0, width,
651                                     i, slot->tracking_id);
652             qemu_input_queue_mtt_abs(con,
653                                     INPUT_AXIS_Y, (int) slot->y,
654                                     0, height,
655                                     i, slot->tracking_id);
656             needs_sync = true;
657         }
658     }
659 
660     if (needs_sync) {
661         qemu_input_event_sync();
662     }
663 }
664 
665 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
666 {
667     /* display has opengl support */
668     assert(con);
669     if (con->gl) {
670         error_report("The console already has an OpenGL context.");
671         exit(1);
672     }
673     con->gl = gl;
674 }
675 
676 static void
677 dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con)
678 {
679     if (con && con->cursor && dcl->ops->dpy_cursor_define) {
680         dcl->ops->dpy_cursor_define(dcl, con->cursor);
681     }
682     if (con && dcl->ops->dpy_mouse_set) {
683         dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
684     }
685 }
686 
687 void register_displaychangelistener(DisplayChangeListener *dcl)
688 {
689     assert(!dcl->ds);
690 
691     trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
692     dcl->ds = get_alloc_displaystate();
693     QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
694     gui_setup_refresh(dcl->ds);
695     if (dcl->con) {
696         dcl->con->dcls++;
697     }
698     displaychangelistener_display_console(dcl, &error_fatal);
699     if (QEMU_IS_GRAPHIC_CONSOLE(dcl->con)) {
700         dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(dcl->con));
701     } else if (QEMU_IS_TEXT_CONSOLE(dcl->con)) {
702         qemu_text_console_update_size(QEMU_TEXT_CONSOLE(dcl->con));
703     }
704     qemu_text_console_update_cursor();
705 }
706 
707 void update_displaychangelistener(DisplayChangeListener *dcl,
708                                   uint64_t interval)
709 {
710     DisplayState *ds = dcl->ds;
711 
712     dcl->update_interval = interval;
713     if (!ds->refreshing && ds->update_interval > interval) {
714         timer_mod(ds->gui_timer, ds->last_update + interval);
715     }
716 }
717 
718 void unregister_displaychangelistener(DisplayChangeListener *dcl)
719 {
720     DisplayState *ds = dcl->ds;
721     trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
722     if (dcl->con) {
723         dcl->con->dcls--;
724     }
725     QLIST_REMOVE(dcl, next);
726     dcl->ds = NULL;
727     gui_setup_refresh(ds);
728 }
729 
730 static void dpy_set_ui_info_timer(void *opaque)
731 {
732     QemuConsole *con = opaque;
733     uint32_t head = qemu_console_get_head(con);
734 
735     con->hw_ops->ui_info(con->hw, head, &con->ui_info);
736 }
737 
738 bool dpy_ui_info_supported(const QemuConsole *con)
739 {
740     if (con == NULL) {
741         return false;
742     }
743 
744     return con->hw_ops->ui_info != NULL;
745 }
746 
747 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
748 {
749     assert(dpy_ui_info_supported(con));
750 
751     return &con->ui_info;
752 }
753 
754 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
755 {
756     if (!dpy_ui_info_supported(con)) {
757         return -1;
758     }
759     if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
760         /* nothing changed -- ignore */
761         return 0;
762     }
763 
764     /*
765      * Typically we get a flood of these as the user resizes the window.
766      * Wait until the dust has settled (one second without updates), then
767      * go notify the guest.
768      */
769     con->ui_info = *info;
770     timer_mod(con->ui_timer,
771               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0));
772     return 0;
773 }
774 
775 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
776 {
777     DisplayState *s = con->ds;
778     DisplayChangeListener *dcl;
779     int width = qemu_console_get_width(con, x + w);
780     int height = qemu_console_get_height(con, y + h);
781 
782     x = MAX(x, 0);
783     y = MAX(y, 0);
784     x = MIN(x, width);
785     y = MIN(y, height);
786     w = MIN(w, width - x);
787     h = MIN(h, height - y);
788 
789     if (!qemu_console_is_visible(con)) {
790         return;
791     }
792     dpy_gfx_update_texture(con, con->surface, x, y, w, h);
793     QLIST_FOREACH(dcl, &s->listeners, next) {
794         if (con != dcl->con) {
795             continue;
796         }
797         if (dcl->ops->dpy_gfx_update) {
798             dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
799         }
800     }
801 }
802 
803 void dpy_gfx_update_full(QemuConsole *con)
804 {
805     int w = qemu_console_get_width(con, 0);
806     int h = qemu_console_get_height(con, 0);
807 
808     dpy_gfx_update(con, 0, 0, w, h);
809 }
810 
811 void dpy_gfx_replace_surface(QemuConsole *con,
812                              DisplaySurface *surface)
813 {
814     static const char placeholder_msg[] = "Display output is not active.";
815     DisplayState *s = con->ds;
816     DisplaySurface *old_surface = con->surface;
817     DisplaySurface *new_surface = surface;
818     DisplayChangeListener *dcl;
819     int width;
820     int height;
821 
822     if (!surface) {
823         if (old_surface) {
824             width = surface_width(old_surface);
825             height = surface_height(old_surface);
826         } else {
827             width = 640;
828             height = 480;
829         }
830 
831         new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
832     }
833 
834     assert(old_surface != new_surface);
835 
836     con->scanout.kind = SCANOUT_SURFACE;
837     con->surface = new_surface;
838     dpy_gfx_create_texture(con, new_surface);
839     QLIST_FOREACH(dcl, &s->listeners, next) {
840         if (con != dcl->con) {
841             continue;
842         }
843         displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
844     }
845     dpy_gfx_destroy_texture(con, old_surface);
846     qemu_free_displaysurface(old_surface);
847 }
848 
849 bool dpy_gfx_check_format(QemuConsole *con,
850                           pixman_format_code_t format)
851 {
852     DisplayChangeListener *dcl;
853     DisplayState *s = con->ds;
854 
855     QLIST_FOREACH(dcl, &s->listeners, next) {
856         if (dcl->con && dcl->con != con) {
857             /* dcl bound to another console -> skip */
858             continue;
859         }
860         if (dcl->ops->dpy_gfx_check_format) {
861             if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
862                 return false;
863             }
864         } else {
865             /* default is to allow native 32 bpp only */
866             if (format != qemu_default_pixman_format(32, true)) {
867                 return false;
868             }
869         }
870     }
871     return true;
872 }
873 
874 static void dpy_refresh(DisplayState *s)
875 {
876     DisplayChangeListener *dcl;
877 
878     QLIST_FOREACH(dcl, &s->listeners, next) {
879         if (dcl->ops->dpy_refresh) {
880             dcl->ops->dpy_refresh(dcl);
881         }
882     }
883 }
884 
885 void dpy_text_cursor(QemuConsole *con, int x, int y)
886 {
887     DisplayState *s = con->ds;
888     DisplayChangeListener *dcl;
889 
890     if (!qemu_console_is_visible(con)) {
891         return;
892     }
893     QLIST_FOREACH(dcl, &s->listeners, next) {
894         if (con != dcl->con) {
895             continue;
896         }
897         if (dcl->ops->dpy_text_cursor) {
898             dcl->ops->dpy_text_cursor(dcl, x, y);
899         }
900     }
901 }
902 
903 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
904 {
905     DisplayState *s = con->ds;
906     DisplayChangeListener *dcl;
907 
908     if (!qemu_console_is_visible(con)) {
909         return;
910     }
911     QLIST_FOREACH(dcl, &s->listeners, next) {
912         if (con != dcl->con) {
913             continue;
914         }
915         if (dcl->ops->dpy_text_update) {
916             dcl->ops->dpy_text_update(dcl, x, y, w, h);
917         }
918     }
919 }
920 
921 void dpy_text_resize(QemuConsole *con, int w, int h)
922 {
923     DisplayState *s = con->ds;
924     DisplayChangeListener *dcl;
925 
926     if (!qemu_console_is_visible(con)) {
927         return;
928     }
929     QLIST_FOREACH(dcl, &s->listeners, next) {
930         if (con != dcl->con) {
931             continue;
932         }
933         if (dcl->ops->dpy_text_resize) {
934             dcl->ops->dpy_text_resize(dcl, w, h);
935         }
936     }
937 }
938 
939 void dpy_mouse_set(QemuConsole *c, int x, int y, bool on)
940 {
941     QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
942     DisplayState *s = c->ds;
943     DisplayChangeListener *dcl;
944 
945     con->cursor_x = x;
946     con->cursor_y = y;
947     con->cursor_on = on;
948     if (!qemu_console_is_visible(c)) {
949         return;
950     }
951     QLIST_FOREACH(dcl, &s->listeners, next) {
952         if (c != dcl->con) {
953             continue;
954         }
955         if (dcl->ops->dpy_mouse_set) {
956             dcl->ops->dpy_mouse_set(dcl, x, y, on);
957         }
958     }
959 }
960 
961 void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor)
962 {
963     QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
964     DisplayState *s = c->ds;
965     DisplayChangeListener *dcl;
966 
967     cursor_unref(con->cursor);
968     con->cursor = cursor_ref(cursor);
969     if (!qemu_console_is_visible(c)) {
970         return;
971     }
972     QLIST_FOREACH(dcl, &s->listeners, next) {
973         if (c != dcl->con) {
974             continue;
975         }
976         if (dcl->ops->dpy_cursor_define) {
977             dcl->ops->dpy_cursor_define(dcl, cursor);
978         }
979     }
980 }
981 
982 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
983                                 struct QEMUGLParams *qparams)
984 {
985     assert(con->gl);
986     return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
987 }
988 
989 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
990 {
991     assert(con->gl);
992     con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
993 }
994 
995 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
996 {
997     assert(con->gl);
998     return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
999 }
1000 
1001 void dpy_gl_scanout_disable(QemuConsole *con)
1002 {
1003     DisplayState *s = con->ds;
1004     DisplayChangeListener *dcl;
1005 
1006     if (con->scanout.kind != SCANOUT_SURFACE) {
1007         con->scanout.kind = SCANOUT_NONE;
1008     }
1009     QLIST_FOREACH(dcl, &s->listeners, next) {
1010         if (con != dcl->con) {
1011             continue;
1012         }
1013         if (dcl->ops->dpy_gl_scanout_disable) {
1014             dcl->ops->dpy_gl_scanout_disable(dcl);
1015         }
1016     }
1017 }
1018 
1019 void dpy_gl_scanout_texture(QemuConsole *con,
1020                             uint32_t backing_id,
1021                             bool backing_y_0_top,
1022                             uint32_t backing_width,
1023                             uint32_t backing_height,
1024                             uint32_t x, uint32_t y,
1025                             uint32_t width, uint32_t height,
1026                             void *d3d_tex2d)
1027 {
1028     DisplayState *s = con->ds;
1029     DisplayChangeListener *dcl;
1030 
1031     con->scanout.kind = SCANOUT_TEXTURE;
1032     con->scanout.texture = (ScanoutTexture) {
1033         backing_id, backing_y_0_top, backing_width, backing_height,
1034         x, y, width, height, d3d_tex2d,
1035     };
1036     QLIST_FOREACH(dcl, &s->listeners, next) {
1037         if (con != dcl->con) {
1038             continue;
1039         }
1040         if (dcl->ops->dpy_gl_scanout_texture) {
1041             dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
1042                                              backing_y_0_top,
1043                                              backing_width, backing_height,
1044                                              x, y, width, height,
1045                                              d3d_tex2d);
1046         }
1047     }
1048 }
1049 
1050 void dpy_gl_scanout_dmabuf(QemuConsole *con,
1051                            QemuDmaBuf *dmabuf)
1052 {
1053     DisplayState *s = con->ds;
1054     DisplayChangeListener *dcl;
1055 
1056     con->scanout.kind = SCANOUT_DMABUF;
1057     con->scanout.dmabuf = dmabuf;
1058     QLIST_FOREACH(dcl, &s->listeners, next) {
1059         if (con != dcl->con) {
1060             continue;
1061         }
1062         if (dcl->ops->dpy_gl_scanout_dmabuf) {
1063             dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
1064         }
1065     }
1066 }
1067 
1068 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
1069                           bool have_hot, uint32_t hot_x, uint32_t hot_y)
1070 {
1071     DisplayState *s = con->ds;
1072     DisplayChangeListener *dcl;
1073 
1074     QLIST_FOREACH(dcl, &s->listeners, next) {
1075         if (con != dcl->con) {
1076             continue;
1077         }
1078         if (dcl->ops->dpy_gl_cursor_dmabuf) {
1079             dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf,
1080                                            have_hot, hot_x, hot_y);
1081         }
1082     }
1083 }
1084 
1085 void dpy_gl_cursor_position(QemuConsole *con,
1086                             uint32_t pos_x, uint32_t pos_y)
1087 {
1088     DisplayState *s = con->ds;
1089     DisplayChangeListener *dcl;
1090 
1091     QLIST_FOREACH(dcl, &s->listeners, next) {
1092         if (con != dcl->con) {
1093             continue;
1094         }
1095         if (dcl->ops->dpy_gl_cursor_position) {
1096             dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y);
1097         }
1098     }
1099 }
1100 
1101 void dpy_gl_release_dmabuf(QemuConsole *con,
1102                           QemuDmaBuf *dmabuf)
1103 {
1104     DisplayState *s = con->ds;
1105     DisplayChangeListener *dcl;
1106 
1107     QLIST_FOREACH(dcl, &s->listeners, next) {
1108         if (con != dcl->con) {
1109             continue;
1110         }
1111         if (dcl->ops->dpy_gl_release_dmabuf) {
1112             dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf);
1113         }
1114     }
1115 }
1116 
1117 void dpy_gl_update(QemuConsole *con,
1118                    uint32_t x, uint32_t y, uint32_t w, uint32_t h)
1119 {
1120     DisplayState *s = con->ds;
1121     DisplayChangeListener *dcl;
1122 
1123     assert(con->gl);
1124 
1125     graphic_hw_gl_block(con, true);
1126     QLIST_FOREACH(dcl, &s->listeners, next) {
1127         if (con != dcl->con) {
1128             continue;
1129         }
1130         if (dcl->ops->dpy_gl_update) {
1131             dcl->ops->dpy_gl_update(dcl, x, y, w, h);
1132         }
1133     }
1134     graphic_hw_gl_block(con, false);
1135 }
1136 
1137 /***********************************************************/
1138 /* register display */
1139 
1140 /* console.c internal use only */
1141 static DisplayState *get_alloc_displaystate(void)
1142 {
1143     if (!display_state) {
1144         display_state = g_new0(DisplayState, 1);
1145     }
1146     return display_state;
1147 }
1148 
1149 /*
1150  * Called by main(), after creating QemuConsoles
1151  * and before initializing ui (sdl/vnc/...).
1152  */
1153 DisplayState *init_displaystate(void)
1154 {
1155     gchar *name;
1156     QemuConsole *con;
1157 
1158     QTAILQ_FOREACH(con, &consoles, next) {
1159         /* Hook up into the qom tree here (not in object_new()), once
1160          * all QemuConsoles are created and the order / numbering
1161          * doesn't change any more */
1162         name = g_strdup_printf("console[%d]", con->index);
1163         object_property_add_child(container_get(object_get_root(), "/backend"),
1164                                   name, OBJECT(con));
1165         g_free(name);
1166     }
1167 
1168     return display_state;
1169 }
1170 
1171 void graphic_console_set_hwops(QemuConsole *con,
1172                                const GraphicHwOps *hw_ops,
1173                                void *opaque)
1174 {
1175     con->hw_ops = hw_ops;
1176     con->hw = opaque;
1177 }
1178 
1179 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
1180                                   const GraphicHwOps *hw_ops,
1181                                   void *opaque)
1182 {
1183     static const char noinit[] =
1184         "Guest has not initialized the display (yet).";
1185     int width = 640;
1186     int height = 480;
1187     QemuConsole *s;
1188     DisplaySurface *surface;
1189 
1190     s = qemu_graphic_console_lookup_unused();
1191     if (s) {
1192         trace_console_gfx_reuse(s->index);
1193         width = qemu_console_get_width(s, 0);
1194         height = qemu_console_get_height(s, 0);
1195     } else {
1196         trace_console_gfx_new();
1197         s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE);
1198     }
1199     QEMU_GRAPHIC_CONSOLE(s)->head = head;
1200     graphic_console_set_hwops(s, hw_ops, opaque);
1201     if (dev) {
1202         object_property_set_link(OBJECT(s), "device", OBJECT(dev),
1203                                  &error_abort);
1204     }
1205 
1206     surface = qemu_create_placeholder_surface(width, height, noinit);
1207     dpy_gfx_replace_surface(s, surface);
1208     s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1209                                        graphic_hw_gl_unblock_timer, s);
1210     return s;
1211 }
1212 
1213 static const GraphicHwOps unused_ops = {
1214     /* no callbacks */
1215 };
1216 
1217 void graphic_console_close(QemuConsole *con)
1218 {
1219     static const char unplugged[] =
1220         "Guest display has been unplugged";
1221     DisplaySurface *surface;
1222     int width = qemu_console_get_width(con, 640);
1223     int height = qemu_console_get_height(con, 480);
1224 
1225     trace_console_gfx_close(con->index);
1226     object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
1227     graphic_console_set_hwops(con, &unused_ops, NULL);
1228 
1229     if (con->gl) {
1230         dpy_gl_scanout_disable(con);
1231     }
1232     surface = qemu_create_placeholder_surface(width, height, unplugged);
1233     dpy_gfx_replace_surface(con, surface);
1234 }
1235 
1236 QemuConsole *qemu_console_lookup_default(void)
1237 {
1238     QemuConsole *con;
1239 
1240     QTAILQ_FOREACH(con, &consoles, next) {
1241         if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
1242             return con;
1243         }
1244     }
1245     return QTAILQ_FIRST(&consoles);
1246 }
1247 
1248 QemuConsole *qemu_console_lookup_by_index(unsigned int index)
1249 {
1250     QemuConsole *con;
1251 
1252     QTAILQ_FOREACH(con, &consoles, next) {
1253         if (con->index == index) {
1254             return con;
1255         }
1256     }
1257     return NULL;
1258 }
1259 
1260 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
1261 {
1262     QemuConsole *con;
1263     Object *obj;
1264     uint32_t h;
1265 
1266     QTAILQ_FOREACH(con, &consoles, next) {
1267         obj = object_property_get_link(OBJECT(con),
1268                                        "device", &error_abort);
1269         if (DEVICE(obj) != dev) {
1270             continue;
1271         }
1272         h = object_property_get_uint(OBJECT(con),
1273                                      "head", &error_abort);
1274         if (h != head) {
1275             continue;
1276         }
1277         return con;
1278     }
1279     return NULL;
1280 }
1281 
1282 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
1283                                                 uint32_t head, Error **errp)
1284 {
1285     DeviceState *dev;
1286     QemuConsole *con;
1287 
1288     dev = qdev_find_recursive(sysbus_get_default(), device_id);
1289     if (dev == NULL) {
1290         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
1291                   "Device '%s' not found", device_id);
1292         return NULL;
1293     }
1294 
1295     con = qemu_console_lookup_by_device(dev, head);
1296     if (con == NULL) {
1297         error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
1298                    device_id, head);
1299         return NULL;
1300     }
1301 
1302     return con;
1303 }
1304 
1305 static QemuConsole *qemu_graphic_console_lookup_unused(void)
1306 {
1307     QemuConsole *con;
1308     Object *obj;
1309 
1310     QTAILQ_FOREACH(con, &consoles, next) {
1311         if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) {
1312             continue;
1313         }
1314         obj = object_property_get_link(OBJECT(con),
1315                                        "device", &error_abort);
1316         if (obj != NULL) {
1317             continue;
1318         }
1319         return con;
1320     }
1321     return NULL;
1322 }
1323 
1324 QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
1325 {
1326     return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL;
1327 }
1328 
1329 bool qemu_console_is_visible(QemuConsole *con)
1330 {
1331     return con->dcls > 0;
1332 }
1333 
1334 bool qemu_console_is_graphic(QemuConsole *con)
1335 {
1336     return con && QEMU_IS_GRAPHIC_CONSOLE(con);
1337 }
1338 
1339 bool qemu_console_is_fixedsize(QemuConsole *con)
1340 {
1341     return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con));
1342 }
1343 
1344 bool qemu_console_is_gl_blocked(QemuConsole *con)
1345 {
1346     assert(con != NULL);
1347     return con->gl_block;
1348 }
1349 
1350 static bool qemu_graphic_console_is_multihead(QemuGraphicConsole *c)
1351 {
1352     QemuConsole *con;
1353 
1354     QTAILQ_FOREACH(con, &consoles, next) {
1355         QemuGraphicConsole *candidate;
1356 
1357         if (!QEMU_IS_GRAPHIC_CONSOLE(con)) {
1358             continue;
1359         }
1360 
1361         candidate = QEMU_GRAPHIC_CONSOLE(con);
1362         if (candidate->device != c->device) {
1363             continue;
1364         }
1365 
1366         if (candidate->head != c->head) {
1367             return true;
1368         }
1369     }
1370     return false;
1371 }
1372 
1373 char *qemu_console_get_label(QemuConsole *con)
1374 {
1375     if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
1376         QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con);
1377         if (c->device) {
1378             DeviceState *dev;
1379             bool multihead;
1380 
1381             dev = DEVICE(c->device);
1382             multihead = qemu_graphic_console_is_multihead(c);
1383             if (multihead) {
1384                 return g_strdup_printf("%s.%d", dev->id ?
1385                                        dev->id :
1386                                        object_get_typename(c->device),
1387                                        c->head);
1388             } else {
1389                 return g_strdup_printf("%s", dev->id ?
1390                                        dev->id :
1391                                        object_get_typename(c->device));
1392             }
1393         }
1394         return g_strdup("VGA");
1395     } else if (QEMU_IS_TEXT_CONSOLE(con)) {
1396         const char *label = qemu_text_console_get_label(QEMU_TEXT_CONSOLE(con));
1397         if (label) {
1398             return g_strdup(label);
1399         }
1400     }
1401 
1402     return g_strdup_printf("vc%d", con->index);
1403 }
1404 
1405 int qemu_console_get_index(QemuConsole *con)
1406 {
1407     return con ? con->index : -1;
1408 }
1409 
1410 uint32_t qemu_console_get_head(QemuConsole *con)
1411 {
1412     if (con == NULL) {
1413         return -1;
1414     }
1415     if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
1416         return QEMU_GRAPHIC_CONSOLE(con)->head;
1417     }
1418     return 0;
1419 }
1420 
1421 int qemu_console_get_width(QemuConsole *con, int fallback)
1422 {
1423     if (con == NULL) {
1424         return fallback;
1425     }
1426     switch (con->scanout.kind) {
1427     case SCANOUT_DMABUF:
1428         return qemu_dmabuf_get_width(con->scanout.dmabuf);
1429     case SCANOUT_TEXTURE:
1430         return con->scanout.texture.width;
1431     case SCANOUT_SURFACE:
1432         return surface_width(con->surface);
1433     default:
1434         return fallback;
1435     }
1436 }
1437 
1438 int qemu_console_get_height(QemuConsole *con, int fallback)
1439 {
1440     if (con == NULL) {
1441         return fallback;
1442     }
1443     switch (con->scanout.kind) {
1444     case SCANOUT_DMABUF:
1445         return qemu_dmabuf_get_height(con->scanout.dmabuf);
1446     case SCANOUT_TEXTURE:
1447         return con->scanout.texture.height;
1448     case SCANOUT_SURFACE:
1449         return surface_height(con->surface);
1450     default:
1451         return fallback;
1452     }
1453 }
1454 
1455 int qemu_invalidate_text_consoles(void)
1456 {
1457     QemuConsole *s;
1458     int count = 0;
1459 
1460     QTAILQ_FOREACH(s, &consoles, next) {
1461         if (qemu_console_is_graphic(s) ||
1462             !qemu_console_is_visible(s)) {
1463             continue;
1464         }
1465         count++;
1466         graphic_hw_invalidate(s);
1467     }
1468 
1469     return count;
1470 }
1471 
1472 void qemu_console_resize(QemuConsole *s, int width, int height)
1473 {
1474     DisplaySurface *surface = qemu_console_surface(s);
1475 
1476     assert(QEMU_IS_GRAPHIC_CONSOLE(s));
1477 
1478     if ((s->scanout.kind != SCANOUT_SURFACE ||
1479          (surface && surface_is_allocated(surface) &&
1480                      !surface_is_placeholder(surface))) &&
1481         qemu_console_get_width(s, -1) == width &&
1482         qemu_console_get_height(s, -1) == height) {
1483         return;
1484     }
1485 
1486     surface = qemu_create_displaysurface(width, height);
1487     dpy_gfx_replace_surface(s, surface);
1488 }
1489 
1490 DisplaySurface *qemu_console_surface(QemuConsole *console)
1491 {
1492     switch (console->scanout.kind) {
1493     case SCANOUT_SURFACE:
1494         return console->surface;
1495     default:
1496         return NULL;
1497     }
1498 }
1499 
1500 PixelFormat qemu_default_pixelformat(int bpp)
1501 {
1502     pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
1503     PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
1504     return pf;
1505 }
1506 
1507 static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
1508 
1509 void qemu_display_register(QemuDisplay *ui)
1510 {
1511     assert(ui->type < DISPLAY_TYPE__MAX);
1512     dpys[ui->type] = ui;
1513 }
1514 
1515 bool qemu_display_find_default(DisplayOptions *opts)
1516 {
1517     static DisplayType prio[] = {
1518 #if defined(CONFIG_GTK)
1519         DISPLAY_TYPE_GTK,
1520 #endif
1521 #if defined(CONFIG_SDL)
1522         DISPLAY_TYPE_SDL,
1523 #endif
1524 #if defined(CONFIG_COCOA)
1525         DISPLAY_TYPE_COCOA
1526 #endif
1527     };
1528     int i;
1529 
1530     for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
1531         if (dpys[prio[i]] == NULL) {
1532             Error *local_err = NULL;
1533             int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
1534             if (rv < 0) {
1535                 error_report_err(local_err);
1536             }
1537         }
1538         if (dpys[prio[i]] == NULL) {
1539             continue;
1540         }
1541         opts->type = prio[i];
1542         return true;
1543     }
1544     return false;
1545 }
1546 
1547 void qemu_display_early_init(DisplayOptions *opts)
1548 {
1549     assert(opts->type < DISPLAY_TYPE__MAX);
1550     if (opts->type == DISPLAY_TYPE_NONE) {
1551         return;
1552     }
1553     if (dpys[opts->type] == NULL) {
1554         Error *local_err = NULL;
1555         int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
1556         if (rv < 0) {
1557             error_report_err(local_err);
1558         }
1559     }
1560     if (dpys[opts->type] == NULL) {
1561         error_report("Display '%s' is not available.",
1562                      DisplayType_str(opts->type));
1563         exit(1);
1564     }
1565     if (dpys[opts->type]->early_init) {
1566         dpys[opts->type]->early_init(opts);
1567     }
1568 }
1569 
1570 void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
1571 {
1572     assert(opts->type < DISPLAY_TYPE__MAX);
1573     if (opts->type == DISPLAY_TYPE_NONE) {
1574         return;
1575     }
1576     assert(dpys[opts->type] != NULL);
1577     dpys[opts->type]->init(ds, opts);
1578 }
1579 
1580 const char *qemu_display_get_vc(DisplayOptions *opts)
1581 {
1582 #ifdef CONFIG_PIXMAN
1583     const char *vc = "vc:80Cx24C";
1584 #else
1585     const char *vc = NULL;
1586 #endif
1587 
1588     assert(opts->type < DISPLAY_TYPE__MAX);
1589     if (dpys[opts->type] && dpys[opts->type]->vc) {
1590         vc = dpys[opts->type]->vc;
1591     }
1592     return vc;
1593 }
1594 
1595 void qemu_display_help(void)
1596 {
1597     int idx;
1598 
1599     printf("Available display backend types:\n");
1600     printf("none\n");
1601     for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
1602         if (!dpys[idx]) {
1603             Error *local_err = NULL;
1604             int rv = ui_module_load(DisplayType_str(idx), &local_err);
1605             if (rv < 0) {
1606                 error_report_err(local_err);
1607             }
1608         }
1609         if (dpys[idx]) {
1610             printf("%s\n",  DisplayType_str(dpys[idx]->type));
1611         }
1612     }
1613     printf("\n"
1614            "Some display backends support suboptions, which can be set with\n"
1615            "   -display backend,option=value,option=value...\n"
1616            "For a short list of the suboptions for each display, see the "
1617            "top-level -help output; more detail is in the documentation.\n");
1618 }
1619