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