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 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, 1005 struct QEMUGLParams *qparams) 1006 { 1007 assert(con->gl); 1008 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams); 1009 } 1010 1011 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx) 1012 { 1013 assert(con->gl); 1014 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx); 1015 } 1016 1017 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) 1018 { 1019 assert(con->gl); 1020 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx); 1021 } 1022 1023 void dpy_gl_scanout_disable(QemuConsole *con) 1024 { 1025 DisplayState *s = con->ds; 1026 DisplayChangeListener *dcl; 1027 1028 if (con->scanout.kind != SCANOUT_SURFACE) { 1029 con->scanout.kind = SCANOUT_NONE; 1030 } 1031 QLIST_FOREACH(dcl, &s->listeners, next) { 1032 if (con != dcl->con) { 1033 continue; 1034 } 1035 if (dcl->ops->dpy_gl_scanout_disable) { 1036 dcl->ops->dpy_gl_scanout_disable(dcl); 1037 } 1038 } 1039 } 1040 1041 void dpy_gl_scanout_texture(QemuConsole *con, 1042 uint32_t backing_id, 1043 bool backing_y_0_top, 1044 uint32_t backing_width, 1045 uint32_t backing_height, 1046 uint32_t x, uint32_t y, 1047 uint32_t width, uint32_t height, 1048 void *d3d_tex2d) 1049 { 1050 DisplayState *s = con->ds; 1051 DisplayChangeListener *dcl; 1052 1053 con->scanout.kind = SCANOUT_TEXTURE; 1054 con->scanout.texture = (ScanoutTexture) { 1055 backing_id, backing_y_0_top, backing_width, backing_height, 1056 x, y, width, height, d3d_tex2d, 1057 }; 1058 QLIST_FOREACH(dcl, &s->listeners, next) { 1059 if (con != dcl->con) { 1060 continue; 1061 } 1062 if (dcl->ops->dpy_gl_scanout_texture) { 1063 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, 1064 backing_y_0_top, 1065 backing_width, backing_height, 1066 x, y, width, height, 1067 d3d_tex2d); 1068 } 1069 } 1070 } 1071 1072 void dpy_gl_scanout_dmabuf(QemuConsole *con, 1073 QemuDmaBuf *dmabuf) 1074 { 1075 DisplayState *s = con->ds; 1076 DisplayChangeListener *dcl; 1077 1078 con->scanout.kind = SCANOUT_DMABUF; 1079 con->scanout.dmabuf = dmabuf; 1080 QLIST_FOREACH(dcl, &s->listeners, next) { 1081 if (con != dcl->con) { 1082 continue; 1083 } 1084 if (dcl->ops->dpy_gl_scanout_dmabuf) { 1085 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); 1086 } 1087 } 1088 } 1089 1090 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, 1091 bool have_hot, uint32_t hot_x, uint32_t hot_y) 1092 { 1093 DisplayState *s = con->ds; 1094 DisplayChangeListener *dcl; 1095 1096 QLIST_FOREACH(dcl, &s->listeners, next) { 1097 if (con != dcl->con) { 1098 continue; 1099 } 1100 if (dcl->ops->dpy_gl_cursor_dmabuf) { 1101 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf, 1102 have_hot, hot_x, hot_y); 1103 } 1104 } 1105 } 1106 1107 void dpy_gl_cursor_position(QemuConsole *con, 1108 uint32_t pos_x, uint32_t pos_y) 1109 { 1110 DisplayState *s = con->ds; 1111 DisplayChangeListener *dcl; 1112 1113 QLIST_FOREACH(dcl, &s->listeners, next) { 1114 if (con != dcl->con) { 1115 continue; 1116 } 1117 if (dcl->ops->dpy_gl_cursor_position) { 1118 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y); 1119 } 1120 } 1121 } 1122 1123 void dpy_gl_release_dmabuf(QemuConsole *con, 1124 QemuDmaBuf *dmabuf) 1125 { 1126 DisplayState *s = con->ds; 1127 DisplayChangeListener *dcl; 1128 1129 QLIST_FOREACH(dcl, &s->listeners, next) { 1130 if (con != dcl->con) { 1131 continue; 1132 } 1133 if (dcl->ops->dpy_gl_release_dmabuf) { 1134 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf); 1135 } 1136 } 1137 } 1138 1139 void dpy_gl_update(QemuConsole *con, 1140 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 1141 { 1142 DisplayState *s = con->ds; 1143 DisplayChangeListener *dcl; 1144 1145 assert(con->gl); 1146 1147 graphic_hw_gl_block(con, true); 1148 QLIST_FOREACH(dcl, &s->listeners, next) { 1149 if (con != dcl->con) { 1150 continue; 1151 } 1152 if (dcl->ops->dpy_gl_update) { 1153 dcl->ops->dpy_gl_update(dcl, x, y, w, h); 1154 } 1155 } 1156 graphic_hw_gl_block(con, false); 1157 } 1158 1159 /***********************************************************/ 1160 /* register display */ 1161 1162 /* console.c internal use only */ 1163 static DisplayState *get_alloc_displaystate(void) 1164 { 1165 if (!display_state) { 1166 display_state = g_new0(DisplayState, 1); 1167 } 1168 return display_state; 1169 } 1170 1171 /* 1172 * Called by main(), after creating QemuConsoles 1173 * and before initializing ui (sdl/vnc/...). 1174 */ 1175 DisplayState *init_displaystate(void) 1176 { 1177 gchar *name; 1178 QemuConsole *con; 1179 1180 QTAILQ_FOREACH(con, &consoles, next) { 1181 /* Hook up into the qom tree here (not in object_new()), once 1182 * all QemuConsoles are created and the order / numbering 1183 * doesn't change any more */ 1184 name = g_strdup_printf("console[%d]", con->index); 1185 object_property_add_child(container_get(object_get_root(), "/backend"), 1186 name, OBJECT(con)); 1187 g_free(name); 1188 } 1189 1190 return display_state; 1191 } 1192 1193 void graphic_console_set_hwops(QemuConsole *con, 1194 const GraphicHwOps *hw_ops, 1195 void *opaque) 1196 { 1197 con->hw_ops = hw_ops; 1198 con->hw = opaque; 1199 } 1200 1201 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 1202 const GraphicHwOps *hw_ops, 1203 void *opaque) 1204 { 1205 static const char noinit[] = 1206 "Guest has not initialized the display (yet)."; 1207 int width = 640; 1208 int height = 480; 1209 QemuConsole *s; 1210 DisplaySurface *surface; 1211 1212 s = qemu_graphic_console_lookup_unused(); 1213 if (s) { 1214 trace_console_gfx_reuse(s->index); 1215 width = qemu_console_get_width(s, 0); 1216 height = qemu_console_get_height(s, 0); 1217 } else { 1218 trace_console_gfx_new(); 1219 s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE); 1220 } 1221 QEMU_GRAPHIC_CONSOLE(s)->head = head; 1222 graphic_console_set_hwops(s, hw_ops, opaque); 1223 if (dev) { 1224 object_property_set_link(OBJECT(s), "device", OBJECT(dev), 1225 &error_abort); 1226 } 1227 1228 surface = qemu_create_placeholder_surface(width, height, noinit); 1229 dpy_gfx_replace_surface(s, surface); 1230 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1231 graphic_hw_gl_unblock_timer, s); 1232 return s; 1233 } 1234 1235 static const GraphicHwOps unused_ops = { 1236 /* no callbacks */ 1237 }; 1238 1239 void graphic_console_close(QemuConsole *con) 1240 { 1241 static const char unplugged[] = 1242 "Guest display has been unplugged"; 1243 DisplaySurface *surface; 1244 int width = qemu_console_get_width(con, 640); 1245 int height = qemu_console_get_height(con, 480); 1246 1247 trace_console_gfx_close(con->index); 1248 object_property_set_link(OBJECT(con), "device", NULL, &error_abort); 1249 graphic_console_set_hwops(con, &unused_ops, NULL); 1250 1251 if (con->gl) { 1252 dpy_gl_scanout_disable(con); 1253 } 1254 surface = qemu_create_placeholder_surface(width, height, unplugged); 1255 dpy_gfx_replace_surface(con, surface); 1256 } 1257 1258 QemuConsole *qemu_console_lookup_default(void) 1259 { 1260 QemuConsole *con; 1261 1262 QTAILQ_FOREACH(con, &consoles, next) { 1263 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 1264 return con; 1265 } 1266 } 1267 return QTAILQ_FIRST(&consoles); 1268 } 1269 1270 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 1271 { 1272 QemuConsole *con; 1273 1274 QTAILQ_FOREACH(con, &consoles, next) { 1275 if (con->index == index) { 1276 return con; 1277 } 1278 } 1279 return NULL; 1280 } 1281 1282 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) 1283 { 1284 QemuConsole *con; 1285 Object *obj; 1286 uint32_t h; 1287 1288 QTAILQ_FOREACH(con, &consoles, next) { 1289 obj = object_property_get_link(OBJECT(con), 1290 "device", &error_abort); 1291 if (DEVICE(obj) != dev) { 1292 continue; 1293 } 1294 h = object_property_get_uint(OBJECT(con), 1295 "head", &error_abort); 1296 if (h != head) { 1297 continue; 1298 } 1299 return con; 1300 } 1301 return NULL; 1302 } 1303 1304 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, 1305 uint32_t head, Error **errp) 1306 { 1307 DeviceState *dev; 1308 QemuConsole *con; 1309 1310 dev = qdev_find_recursive(sysbus_get_default(), device_id); 1311 if (dev == NULL) { 1312 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, 1313 "Device '%s' not found", device_id); 1314 return NULL; 1315 } 1316 1317 con = qemu_console_lookup_by_device(dev, head); 1318 if (con == NULL) { 1319 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole", 1320 device_id, head); 1321 return NULL; 1322 } 1323 1324 return con; 1325 } 1326 1327 static QemuConsole *qemu_graphic_console_lookup_unused(void) 1328 { 1329 QemuConsole *con; 1330 Object *obj; 1331 1332 QTAILQ_FOREACH(con, &consoles, next) { 1333 if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) { 1334 continue; 1335 } 1336 obj = object_property_get_link(OBJECT(con), 1337 "device", &error_abort); 1338 if (obj != NULL) { 1339 continue; 1340 } 1341 return con; 1342 } 1343 return NULL; 1344 } 1345 1346 QEMUCursor *qemu_console_get_cursor(QemuConsole *con) 1347 { 1348 return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL; 1349 } 1350 1351 bool qemu_console_is_visible(QemuConsole *con) 1352 { 1353 return con->dcls > 0; 1354 } 1355 1356 bool qemu_console_is_graphic(QemuConsole *con) 1357 { 1358 return con && QEMU_IS_GRAPHIC_CONSOLE(con); 1359 } 1360 1361 bool qemu_console_is_fixedsize(QemuConsole *con) 1362 { 1363 return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con)); 1364 } 1365 1366 bool qemu_console_is_gl_blocked(QemuConsole *con) 1367 { 1368 assert(con != NULL); 1369 return con->gl_block; 1370 } 1371 1372 static bool qemu_graphic_console_is_multihead(QemuGraphicConsole *c) 1373 { 1374 QemuConsole *con; 1375 1376 QTAILQ_FOREACH(con, &consoles, next) { 1377 QemuGraphicConsole *candidate; 1378 1379 if (!QEMU_IS_GRAPHIC_CONSOLE(con)) { 1380 continue; 1381 } 1382 1383 candidate = QEMU_GRAPHIC_CONSOLE(con); 1384 if (candidate->device != c->device) { 1385 continue; 1386 } 1387 1388 if (candidate->head != c->head) { 1389 return true; 1390 } 1391 } 1392 return false; 1393 } 1394 1395 char *qemu_console_get_label(QemuConsole *con) 1396 { 1397 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 1398 QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con); 1399 if (c->device) { 1400 DeviceState *dev; 1401 bool multihead; 1402 1403 dev = DEVICE(c->device); 1404 multihead = qemu_graphic_console_is_multihead(c); 1405 if (multihead) { 1406 return g_strdup_printf("%s.%d", dev->id ? 1407 dev->id : 1408 object_get_typename(c->device), 1409 c->head); 1410 } else { 1411 return g_strdup_printf("%s", dev->id ? 1412 dev->id : 1413 object_get_typename(c->device)); 1414 } 1415 } 1416 return g_strdup("VGA"); 1417 } else if (QEMU_IS_TEXT_CONSOLE(con)) { 1418 const char *label = qemu_text_console_get_label(QEMU_TEXT_CONSOLE(con)); 1419 if (label) { 1420 return g_strdup(label); 1421 } 1422 } 1423 1424 return g_strdup_printf("vc%d", con->index); 1425 } 1426 1427 int qemu_console_get_index(QemuConsole *con) 1428 { 1429 return con ? con->index : -1; 1430 } 1431 1432 uint32_t qemu_console_get_head(QemuConsole *con) 1433 { 1434 if (con == NULL) { 1435 return -1; 1436 } 1437 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 1438 return QEMU_GRAPHIC_CONSOLE(con)->head; 1439 } 1440 return 0; 1441 } 1442 1443 int qemu_console_get_width(QemuConsole *con, int fallback) 1444 { 1445 if (con == NULL) { 1446 return fallback; 1447 } 1448 switch (con->scanout.kind) { 1449 case SCANOUT_DMABUF: 1450 return qemu_dmabuf_get_width(con->scanout.dmabuf); 1451 case SCANOUT_TEXTURE: 1452 return con->scanout.texture.width; 1453 case SCANOUT_SURFACE: 1454 return surface_width(con->surface); 1455 default: 1456 return fallback; 1457 } 1458 } 1459 1460 int qemu_console_get_height(QemuConsole *con, int fallback) 1461 { 1462 if (con == NULL) { 1463 return fallback; 1464 } 1465 switch (con->scanout.kind) { 1466 case SCANOUT_DMABUF: 1467 return qemu_dmabuf_get_height(con->scanout.dmabuf); 1468 case SCANOUT_TEXTURE: 1469 return con->scanout.texture.height; 1470 case SCANOUT_SURFACE: 1471 return surface_height(con->surface); 1472 default: 1473 return fallback; 1474 } 1475 } 1476 1477 int qemu_invalidate_text_consoles(void) 1478 { 1479 QemuConsole *s; 1480 int count = 0; 1481 1482 QTAILQ_FOREACH(s, &consoles, next) { 1483 if (qemu_console_is_graphic(s) || 1484 !qemu_console_is_visible(s)) { 1485 continue; 1486 } 1487 count++; 1488 graphic_hw_invalidate(s); 1489 } 1490 1491 return count; 1492 } 1493 1494 void qemu_console_resize(QemuConsole *s, int width, int height) 1495 { 1496 DisplaySurface *surface = qemu_console_surface(s); 1497 1498 assert(QEMU_IS_GRAPHIC_CONSOLE(s)); 1499 1500 if ((s->scanout.kind != SCANOUT_SURFACE || 1501 (surface && surface_is_allocated(surface) && 1502 !surface_is_placeholder(surface))) && 1503 qemu_console_get_width(s, -1) == width && 1504 qemu_console_get_height(s, -1) == height) { 1505 return; 1506 } 1507 1508 surface = qemu_create_displaysurface(width, height); 1509 dpy_gfx_replace_surface(s, surface); 1510 } 1511 1512 DisplaySurface *qemu_console_surface(QemuConsole *console) 1513 { 1514 switch (console->scanout.kind) { 1515 case SCANOUT_SURFACE: 1516 return console->surface; 1517 default: 1518 return NULL; 1519 } 1520 } 1521 1522 PixelFormat qemu_default_pixelformat(int bpp) 1523 { 1524 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); 1525 PixelFormat pf = qemu_pixelformat_from_pixman(fmt); 1526 return pf; 1527 } 1528 1529 static QemuDisplay *dpys[DISPLAY_TYPE__MAX]; 1530 1531 void qemu_display_register(QemuDisplay *ui) 1532 { 1533 assert(ui->type < DISPLAY_TYPE__MAX); 1534 dpys[ui->type] = ui; 1535 } 1536 1537 bool qemu_display_find_default(DisplayOptions *opts) 1538 { 1539 static DisplayType prio[] = { 1540 #if defined(CONFIG_GTK) 1541 DISPLAY_TYPE_GTK, 1542 #endif 1543 #if defined(CONFIG_SDL) 1544 DISPLAY_TYPE_SDL, 1545 #endif 1546 #if defined(CONFIG_COCOA) 1547 DISPLAY_TYPE_COCOA 1548 #endif 1549 }; 1550 int i; 1551 1552 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { 1553 if (dpys[prio[i]] == NULL) { 1554 Error *local_err = NULL; 1555 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err); 1556 if (rv < 0) { 1557 error_report_err(local_err); 1558 } 1559 } 1560 if (dpys[prio[i]] == NULL) { 1561 continue; 1562 } 1563 opts->type = prio[i]; 1564 return true; 1565 } 1566 return false; 1567 } 1568 1569 void qemu_display_early_init(DisplayOptions *opts) 1570 { 1571 assert(opts->type < DISPLAY_TYPE__MAX); 1572 if (opts->type == DISPLAY_TYPE_NONE) { 1573 return; 1574 } 1575 if (dpys[opts->type] == NULL) { 1576 Error *local_err = NULL; 1577 int rv = ui_module_load(DisplayType_str(opts->type), &local_err); 1578 if (rv < 0) { 1579 error_report_err(local_err); 1580 } 1581 } 1582 if (dpys[opts->type] == NULL) { 1583 error_report("Display '%s' is not available.", 1584 DisplayType_str(opts->type)); 1585 exit(1); 1586 } 1587 if (dpys[opts->type]->early_init) { 1588 dpys[opts->type]->early_init(opts); 1589 } 1590 } 1591 1592 void qemu_display_init(DisplayState *ds, DisplayOptions *opts) 1593 { 1594 assert(opts->type < DISPLAY_TYPE__MAX); 1595 if (opts->type == DISPLAY_TYPE_NONE) { 1596 return; 1597 } 1598 assert(dpys[opts->type] != NULL); 1599 dpys[opts->type]->init(ds, opts); 1600 } 1601 1602 const char *qemu_display_get_vc(DisplayOptions *opts) 1603 { 1604 #ifdef CONFIG_PIXMAN 1605 const char *vc = "vc:80Cx24C"; 1606 #else 1607 const char *vc = NULL; 1608 #endif 1609 1610 assert(opts->type < DISPLAY_TYPE__MAX); 1611 if (dpys[opts->type] && dpys[opts->type]->vc) { 1612 vc = dpys[opts->type]->vc; 1613 } 1614 return vc; 1615 } 1616 1617 void qemu_display_help(void) 1618 { 1619 int idx; 1620 1621 printf("Available display backend types:\n"); 1622 printf("none\n"); 1623 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { 1624 if (!dpys[idx]) { 1625 Error *local_err = NULL; 1626 int rv = ui_module_load(DisplayType_str(idx), &local_err); 1627 if (rv < 0) { 1628 error_report_err(local_err); 1629 } 1630 } 1631 if (dpys[idx]) { 1632 printf("%s\n", DisplayType_str(dpys[idx]->type)); 1633 } 1634 } 1635 } 1636