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