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