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 #endif 465 466 DisplaySurface *qemu_create_displaysurface(int width, int height) 467 { 468 DisplaySurface *surface; 469 void *bits = NULL; 470 #ifdef WIN32 471 HANDLE handle = NULL; 472 #endif 473 474 trace_displaysurface_create(width, height); 475 476 #ifdef WIN32 477 bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort); 478 #endif 479 480 surface = qemu_create_displaysurface_from( 481 width, height, 482 PIXMAN_x8r8g8b8, 483 width * 4, bits 484 ); 485 surface->flags = QEMU_ALLOCATED_FLAG; 486 487 #ifdef WIN32 488 qemu_displaysurface_win32_set_handle(surface, handle, 0); 489 pixman_image_set_destroy_function(surface->image, 490 qemu_pixman_win32_image_destroy, handle); 491 #endif 492 return surface; 493 } 494 495 DisplaySurface *qemu_create_displaysurface_from(int width, int height, 496 pixman_format_code_t format, 497 int linesize, uint8_t *data) 498 { 499 DisplaySurface *surface = g_new0(DisplaySurface, 1); 500 501 trace_displaysurface_create_from(surface, width, height, format); 502 surface->image = pixman_image_create_bits(format, 503 width, height, 504 (void *)data, linesize); 505 assert(surface->image != NULL); 506 507 return surface; 508 } 509 510 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) 511 { 512 DisplaySurface *surface = g_new0(DisplaySurface, 1); 513 514 trace_displaysurface_create_pixman(surface); 515 surface->image = pixman_image_ref(image); 516 517 return surface; 518 } 519 520 DisplaySurface *qemu_create_placeholder_surface(int w, int h, 521 const char *msg) 522 { 523 DisplaySurface *surface = qemu_create_displaysurface(w, h); 524 #ifdef CONFIG_PIXMAN 525 pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK; 526 pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY; 527 pixman_image_t *glyph; 528 int len, x, y, i; 529 530 len = strlen(msg); 531 x = (w / FONT_WIDTH - len) / 2; 532 y = (h / FONT_HEIGHT - 1) / 2; 533 for (i = 0; i < len; i++) { 534 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]); 535 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg, 536 x+i, y, FONT_WIDTH, FONT_HEIGHT); 537 qemu_pixman_image_unref(glyph); 538 } 539 #endif 540 surface->flags |= QEMU_PLACEHOLDER_FLAG; 541 return surface; 542 } 543 544 void qemu_free_displaysurface(DisplaySurface *surface) 545 { 546 if (surface == NULL) { 547 return; 548 } 549 trace_displaysurface_free(surface); 550 qemu_pixman_image_unref(surface->image); 551 g_free(surface); 552 } 553 554 bool console_has_gl(QemuConsole *con) 555 { 556 return con->gl != NULL; 557 } 558 559 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) 560 { 561 if (dcl->ops->dpy_has_dmabuf) { 562 return dcl->ops->dpy_has_dmabuf(dcl); 563 } 564 565 if (dcl->ops->dpy_gl_scanout_dmabuf) { 566 return true; 567 } 568 569 return false; 570 } 571 572 static bool console_compatible_with(QemuConsole *con, 573 DisplayChangeListener *dcl, Error **errp) 574 { 575 int flags; 576 577 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; 578 579 if (console_has_gl(con) && 580 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) { 581 error_setg(errp, "Display %s is incompatible with the GL context", 582 dcl->ops->dpy_name); 583 return false; 584 } 585 586 if (flags & GRAPHIC_FLAGS_GL && 587 !console_has_gl(con)) { 588 error_setg(errp, "The console requires a GL context."); 589 return false; 590 591 } 592 593 if (flags & GRAPHIC_FLAGS_DMABUF && 594 !displaychangelistener_has_dmabuf(dcl)) { 595 error_setg(errp, "The console requires display DMABUF support."); 596 return false; 597 } 598 599 return true; 600 } 601 602 void console_handle_touch_event(QemuConsole *con, 603 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX], 604 uint64_t num_slot, 605 int width, int height, 606 double x, double y, 607 InputMultiTouchType type, 608 Error **errp) 609 { 610 struct touch_slot *slot; 611 bool needs_sync = false; 612 int update; 613 int i; 614 615 if (num_slot >= INPUT_EVENT_SLOTS_MAX) { 616 error_setg(errp, 617 "Unexpected touch slot number: % " PRId64" >= %d", 618 num_slot, INPUT_EVENT_SLOTS_MAX); 619 return; 620 } 621 622 slot = &touch_slots[num_slot]; 623 slot->x = x; 624 slot->y = y; 625 626 if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) { 627 slot->tracking_id = num_slot; 628 } 629 630 for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) { 631 if (i == num_slot) { 632 update = type; 633 } else { 634 update = INPUT_MULTI_TOUCH_TYPE_UPDATE; 635 } 636 637 slot = &touch_slots[i]; 638 639 if (slot->tracking_id == -1) { 640 continue; 641 } 642 643 if (update == INPUT_MULTI_TOUCH_TYPE_END) { 644 slot->tracking_id = -1; 645 qemu_input_queue_mtt(con, update, i, slot->tracking_id); 646 needs_sync = true; 647 } else { 648 qemu_input_queue_mtt(con, update, i, slot->tracking_id); 649 qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true); 650 qemu_input_queue_mtt_abs(con, 651 INPUT_AXIS_X, (int) slot->x, 652 0, width, 653 i, slot->tracking_id); 654 qemu_input_queue_mtt_abs(con, 655 INPUT_AXIS_Y, (int) slot->y, 656 0, height, 657 i, slot->tracking_id); 658 needs_sync = true; 659 } 660 } 661 662 if (needs_sync) { 663 qemu_input_event_sync(); 664 } 665 } 666 667 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) 668 { 669 /* display has opengl support */ 670 assert(con); 671 if (con->gl) { 672 error_report("The console already has an OpenGL context."); 673 exit(1); 674 } 675 con->gl = gl; 676 } 677 678 static void 679 dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con) 680 { 681 if (con && con->cursor && dcl->ops->dpy_cursor_define) { 682 dcl->ops->dpy_cursor_define(dcl, con->cursor); 683 } 684 if (con && dcl->ops->dpy_mouse_set) { 685 dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on); 686 } 687 } 688 689 void register_displaychangelistener(DisplayChangeListener *dcl) 690 { 691 assert(!dcl->ds); 692 693 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); 694 dcl->ds = get_alloc_displaystate(); 695 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); 696 gui_setup_refresh(dcl->ds); 697 if (dcl->con) { 698 dcl->con->dcls++; 699 } 700 displaychangelistener_display_console(dcl, &error_fatal); 701 if (QEMU_IS_GRAPHIC_CONSOLE(dcl->con)) { 702 dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(dcl->con)); 703 } else if (QEMU_IS_TEXT_CONSOLE(dcl->con)) { 704 qemu_text_console_update_size(QEMU_TEXT_CONSOLE(dcl->con)); 705 } 706 qemu_text_console_update_cursor(); 707 } 708 709 void update_displaychangelistener(DisplayChangeListener *dcl, 710 uint64_t interval) 711 { 712 DisplayState *ds = dcl->ds; 713 714 dcl->update_interval = interval; 715 if (!ds->refreshing && ds->update_interval > interval) { 716 timer_mod(ds->gui_timer, ds->last_update + interval); 717 } 718 } 719 720 void unregister_displaychangelistener(DisplayChangeListener *dcl) 721 { 722 DisplayState *ds = dcl->ds; 723 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name); 724 if (dcl->con) { 725 dcl->con->dcls--; 726 } 727 QLIST_REMOVE(dcl, next); 728 dcl->ds = NULL; 729 gui_setup_refresh(ds); 730 } 731 732 static void dpy_set_ui_info_timer(void *opaque) 733 { 734 QemuConsole *con = opaque; 735 uint32_t head = qemu_console_get_head(con); 736 737 con->hw_ops->ui_info(con->hw, head, &con->ui_info); 738 } 739 740 bool dpy_ui_info_supported(const QemuConsole *con) 741 { 742 if (con == NULL) { 743 return false; 744 } 745 746 return con->hw_ops->ui_info != NULL; 747 } 748 749 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con) 750 { 751 assert(dpy_ui_info_supported(con)); 752 753 return &con->ui_info; 754 } 755 756 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay) 757 { 758 if (!dpy_ui_info_supported(con)) { 759 return -1; 760 } 761 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) { 762 /* nothing changed -- ignore */ 763 return 0; 764 } 765 766 /* 767 * Typically we get a flood of these as the user resizes the window. 768 * Wait until the dust has settled (one second without updates), then 769 * go notify the guest. 770 */ 771 con->ui_info = *info; 772 timer_mod(con->ui_timer, 773 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0)); 774 return 0; 775 } 776 777 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) 778 { 779 DisplayState *s = con->ds; 780 DisplayChangeListener *dcl; 781 int width = qemu_console_get_width(con, x + w); 782 int height = qemu_console_get_height(con, y + h); 783 784 x = MAX(x, 0); 785 y = MAX(y, 0); 786 x = MIN(x, width); 787 y = MIN(y, height); 788 w = MIN(w, width - x); 789 h = MIN(h, height - y); 790 791 if (!qemu_console_is_visible(con)) { 792 return; 793 } 794 dpy_gfx_update_texture(con, con->surface, x, y, w, h); 795 QLIST_FOREACH(dcl, &s->listeners, next) { 796 if (con != dcl->con) { 797 continue; 798 } 799 if (dcl->ops->dpy_gfx_update) { 800 dcl->ops->dpy_gfx_update(dcl, x, y, w, h); 801 } 802 } 803 } 804 805 void dpy_gfx_update_full(QemuConsole *con) 806 { 807 int w = qemu_console_get_width(con, 0); 808 int h = qemu_console_get_height(con, 0); 809 810 dpy_gfx_update(con, 0, 0, w, h); 811 } 812 813 void dpy_gfx_replace_surface(QemuConsole *con, 814 DisplaySurface *surface) 815 { 816 static const char placeholder_msg[] = "Display output is not active."; 817 DisplayState *s = con->ds; 818 DisplaySurface *old_surface = con->surface; 819 DisplaySurface *new_surface = surface; 820 DisplayChangeListener *dcl; 821 int width; 822 int height; 823 824 if (!surface) { 825 if (old_surface) { 826 width = surface_width(old_surface); 827 height = surface_height(old_surface); 828 } else { 829 width = 640; 830 height = 480; 831 } 832 833 new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg); 834 } 835 836 assert(old_surface != new_surface); 837 838 con->scanout.kind = SCANOUT_SURFACE; 839 con->surface = new_surface; 840 dpy_gfx_create_texture(con, new_surface); 841 QLIST_FOREACH(dcl, &s->listeners, next) { 842 if (con != dcl->con) { 843 continue; 844 } 845 displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE); 846 } 847 dpy_gfx_destroy_texture(con, old_surface); 848 qemu_free_displaysurface(old_surface); 849 } 850 851 bool dpy_gfx_check_format(QemuConsole *con, 852 pixman_format_code_t format) 853 { 854 DisplayChangeListener *dcl; 855 DisplayState *s = con->ds; 856 857 QLIST_FOREACH(dcl, &s->listeners, next) { 858 if (dcl->con && dcl->con != con) { 859 /* dcl bound to another console -> skip */ 860 continue; 861 } 862 if (dcl->ops->dpy_gfx_check_format) { 863 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) { 864 return false; 865 } 866 } else { 867 /* default is to allow native 32 bpp only */ 868 if (format != qemu_default_pixman_format(32, true)) { 869 return false; 870 } 871 } 872 } 873 return true; 874 } 875 876 static void dpy_refresh(DisplayState *s) 877 { 878 DisplayChangeListener *dcl; 879 880 QLIST_FOREACH(dcl, &s->listeners, next) { 881 if (dcl->ops->dpy_refresh) { 882 dcl->ops->dpy_refresh(dcl); 883 } 884 } 885 } 886 887 void dpy_text_cursor(QemuConsole *con, int x, int y) 888 { 889 DisplayState *s = con->ds; 890 DisplayChangeListener *dcl; 891 892 if (!qemu_console_is_visible(con)) { 893 return; 894 } 895 QLIST_FOREACH(dcl, &s->listeners, next) { 896 if (con != dcl->con) { 897 continue; 898 } 899 if (dcl->ops->dpy_text_cursor) { 900 dcl->ops->dpy_text_cursor(dcl, x, y); 901 } 902 } 903 } 904 905 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) 906 { 907 DisplayState *s = con->ds; 908 DisplayChangeListener *dcl; 909 910 if (!qemu_console_is_visible(con)) { 911 return; 912 } 913 QLIST_FOREACH(dcl, &s->listeners, next) { 914 if (con != dcl->con) { 915 continue; 916 } 917 if (dcl->ops->dpy_text_update) { 918 dcl->ops->dpy_text_update(dcl, x, y, w, h); 919 } 920 } 921 } 922 923 void dpy_text_resize(QemuConsole *con, int w, int h) 924 { 925 DisplayState *s = con->ds; 926 DisplayChangeListener *dcl; 927 928 if (!qemu_console_is_visible(con)) { 929 return; 930 } 931 QLIST_FOREACH(dcl, &s->listeners, next) { 932 if (con != dcl->con) { 933 continue; 934 } 935 if (dcl->ops->dpy_text_resize) { 936 dcl->ops->dpy_text_resize(dcl, w, h); 937 } 938 } 939 } 940 941 void dpy_mouse_set(QemuConsole *c, int x, int y, bool on) 942 { 943 QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); 944 DisplayState *s = c->ds; 945 DisplayChangeListener *dcl; 946 947 con->cursor_x = x; 948 con->cursor_y = y; 949 con->cursor_on = on; 950 if (!qemu_console_is_visible(c)) { 951 return; 952 } 953 QLIST_FOREACH(dcl, &s->listeners, next) { 954 if (c != dcl->con) { 955 continue; 956 } 957 if (dcl->ops->dpy_mouse_set) { 958 dcl->ops->dpy_mouse_set(dcl, x, y, on); 959 } 960 } 961 } 962 963 void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor) 964 { 965 QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); 966 DisplayState *s = c->ds; 967 DisplayChangeListener *dcl; 968 969 cursor_unref(con->cursor); 970 con->cursor = cursor_ref(cursor); 971 if (!qemu_console_is_visible(c)) { 972 return; 973 } 974 QLIST_FOREACH(dcl, &s->listeners, next) { 975 if (c != dcl->con) { 976 continue; 977 } 978 if (dcl->ops->dpy_cursor_define) { 979 dcl->ops->dpy_cursor_define(dcl, cursor); 980 } 981 } 982 } 983 984 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, 985 struct QEMUGLParams *qparams) 986 { 987 assert(con->gl); 988 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams); 989 } 990 991 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx) 992 { 993 assert(con->gl); 994 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx); 995 } 996 997 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) 998 { 999 assert(con->gl); 1000 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx); 1001 } 1002 1003 void dpy_gl_scanout_disable(QemuConsole *con) 1004 { 1005 DisplayState *s = con->ds; 1006 DisplayChangeListener *dcl; 1007 1008 if (con->scanout.kind != SCANOUT_SURFACE) { 1009 con->scanout.kind = SCANOUT_NONE; 1010 } 1011 QLIST_FOREACH(dcl, &s->listeners, next) { 1012 if (con != dcl->con) { 1013 continue; 1014 } 1015 if (dcl->ops->dpy_gl_scanout_disable) { 1016 dcl->ops->dpy_gl_scanout_disable(dcl); 1017 } 1018 } 1019 } 1020 1021 void dpy_gl_scanout_texture(QemuConsole *con, 1022 uint32_t backing_id, 1023 bool backing_y_0_top, 1024 uint32_t backing_width, 1025 uint32_t backing_height, 1026 uint32_t x, uint32_t y, 1027 uint32_t width, uint32_t height, 1028 void *d3d_tex2d) 1029 { 1030 DisplayState *s = con->ds; 1031 DisplayChangeListener *dcl; 1032 1033 con->scanout.kind = SCANOUT_TEXTURE; 1034 con->scanout.texture = (ScanoutTexture) { 1035 backing_id, backing_y_0_top, backing_width, backing_height, 1036 x, y, width, height, d3d_tex2d, 1037 }; 1038 QLIST_FOREACH(dcl, &s->listeners, next) { 1039 if (con != dcl->con) { 1040 continue; 1041 } 1042 if (dcl->ops->dpy_gl_scanout_texture) { 1043 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, 1044 backing_y_0_top, 1045 backing_width, backing_height, 1046 x, y, width, height, 1047 d3d_tex2d); 1048 } 1049 } 1050 } 1051 1052 void dpy_gl_scanout_dmabuf(QemuConsole *con, 1053 QemuDmaBuf *dmabuf) 1054 { 1055 DisplayState *s = con->ds; 1056 DisplayChangeListener *dcl; 1057 1058 con->scanout.kind = SCANOUT_DMABUF; 1059 con->scanout.dmabuf = dmabuf; 1060 QLIST_FOREACH(dcl, &s->listeners, next) { 1061 if (con != dcl->con) { 1062 continue; 1063 } 1064 if (dcl->ops->dpy_gl_scanout_dmabuf) { 1065 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); 1066 } 1067 } 1068 } 1069 1070 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, 1071 bool have_hot, uint32_t hot_x, uint32_t hot_y) 1072 { 1073 DisplayState *s = con->ds; 1074 DisplayChangeListener *dcl; 1075 1076 QLIST_FOREACH(dcl, &s->listeners, next) { 1077 if (con != dcl->con) { 1078 continue; 1079 } 1080 if (dcl->ops->dpy_gl_cursor_dmabuf) { 1081 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf, 1082 have_hot, hot_x, hot_y); 1083 } 1084 } 1085 } 1086 1087 void dpy_gl_cursor_position(QemuConsole *con, 1088 uint32_t pos_x, uint32_t pos_y) 1089 { 1090 DisplayState *s = con->ds; 1091 DisplayChangeListener *dcl; 1092 1093 QLIST_FOREACH(dcl, &s->listeners, next) { 1094 if (con != dcl->con) { 1095 continue; 1096 } 1097 if (dcl->ops->dpy_gl_cursor_position) { 1098 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y); 1099 } 1100 } 1101 } 1102 1103 void dpy_gl_release_dmabuf(QemuConsole *con, 1104 QemuDmaBuf *dmabuf) 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_release_dmabuf) { 1114 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf); 1115 } 1116 } 1117 } 1118 1119 void dpy_gl_update(QemuConsole *con, 1120 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 1121 { 1122 DisplayState *s = con->ds; 1123 DisplayChangeListener *dcl; 1124 1125 assert(con->gl); 1126 1127 graphic_hw_gl_block(con, true); 1128 QLIST_FOREACH(dcl, &s->listeners, next) { 1129 if (con != dcl->con) { 1130 continue; 1131 } 1132 if (dcl->ops->dpy_gl_update) { 1133 dcl->ops->dpy_gl_update(dcl, x, y, w, h); 1134 } 1135 } 1136 graphic_hw_gl_block(con, false); 1137 } 1138 1139 /***********************************************************/ 1140 /* register display */ 1141 1142 /* console.c internal use only */ 1143 static DisplayState *get_alloc_displaystate(void) 1144 { 1145 if (!display_state) { 1146 display_state = g_new0(DisplayState, 1); 1147 } 1148 return display_state; 1149 } 1150 1151 /* 1152 * Called by main(), after creating QemuConsoles 1153 * and before initializing ui (sdl/vnc/...). 1154 */ 1155 DisplayState *init_displaystate(void) 1156 { 1157 gchar *name; 1158 QemuConsole *con; 1159 1160 QTAILQ_FOREACH(con, &consoles, next) { 1161 /* Hook up into the qom tree here (not in object_new()), once 1162 * all QemuConsoles are created and the order / numbering 1163 * doesn't change any more */ 1164 name = g_strdup_printf("console[%d]", con->index); 1165 object_property_add_child(container_get(object_get_root(), "/backend"), 1166 name, OBJECT(con)); 1167 g_free(name); 1168 } 1169 1170 return display_state; 1171 } 1172 1173 void graphic_console_set_hwops(QemuConsole *con, 1174 const GraphicHwOps *hw_ops, 1175 void *opaque) 1176 { 1177 con->hw_ops = hw_ops; 1178 con->hw = opaque; 1179 } 1180 1181 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 1182 const GraphicHwOps *hw_ops, 1183 void *opaque) 1184 { 1185 static const char noinit[] = 1186 "Guest has not initialized the display (yet)."; 1187 int width = 640; 1188 int height = 480; 1189 QemuConsole *s; 1190 DisplaySurface *surface; 1191 1192 s = qemu_graphic_console_lookup_unused(); 1193 if (s) { 1194 trace_console_gfx_reuse(s->index); 1195 width = qemu_console_get_width(s, 0); 1196 height = qemu_console_get_height(s, 0); 1197 } else { 1198 trace_console_gfx_new(); 1199 s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE); 1200 } 1201 QEMU_GRAPHIC_CONSOLE(s)->head = head; 1202 graphic_console_set_hwops(s, hw_ops, opaque); 1203 if (dev) { 1204 object_property_set_link(OBJECT(s), "device", OBJECT(dev), 1205 &error_abort); 1206 } 1207 1208 surface = qemu_create_placeholder_surface(width, height, noinit); 1209 dpy_gfx_replace_surface(s, surface); 1210 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1211 graphic_hw_gl_unblock_timer, s); 1212 return s; 1213 } 1214 1215 static const GraphicHwOps unused_ops = { 1216 /* no callbacks */ 1217 }; 1218 1219 void graphic_console_close(QemuConsole *con) 1220 { 1221 static const char unplugged[] = 1222 "Guest display has been unplugged"; 1223 DisplaySurface *surface; 1224 int width = qemu_console_get_width(con, 640); 1225 int height = qemu_console_get_height(con, 480); 1226 1227 trace_console_gfx_close(con->index); 1228 object_property_set_link(OBJECT(con), "device", NULL, &error_abort); 1229 graphic_console_set_hwops(con, &unused_ops, NULL); 1230 1231 if (con->gl) { 1232 dpy_gl_scanout_disable(con); 1233 } 1234 surface = qemu_create_placeholder_surface(width, height, unplugged); 1235 dpy_gfx_replace_surface(con, surface); 1236 } 1237 1238 QemuConsole *qemu_console_lookup_default(void) 1239 { 1240 QemuConsole *con; 1241 1242 QTAILQ_FOREACH(con, &consoles, next) { 1243 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 1244 return con; 1245 } 1246 } 1247 return QTAILQ_FIRST(&consoles); 1248 } 1249 1250 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 1251 { 1252 QemuConsole *con; 1253 1254 QTAILQ_FOREACH(con, &consoles, next) { 1255 if (con->index == index) { 1256 return con; 1257 } 1258 } 1259 return NULL; 1260 } 1261 1262 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) 1263 { 1264 QemuConsole *con; 1265 Object *obj; 1266 uint32_t h; 1267 1268 QTAILQ_FOREACH(con, &consoles, next) { 1269 obj = object_property_get_link(OBJECT(con), 1270 "device", &error_abort); 1271 if (DEVICE(obj) != dev) { 1272 continue; 1273 } 1274 h = object_property_get_uint(OBJECT(con), 1275 "head", &error_abort); 1276 if (h != head) { 1277 continue; 1278 } 1279 return con; 1280 } 1281 return NULL; 1282 } 1283 1284 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, 1285 uint32_t head, Error **errp) 1286 { 1287 DeviceState *dev; 1288 QemuConsole *con; 1289 1290 dev = qdev_find_recursive(sysbus_get_default(), device_id); 1291 if (dev == NULL) { 1292 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, 1293 "Device '%s' not found", device_id); 1294 return NULL; 1295 } 1296 1297 con = qemu_console_lookup_by_device(dev, head); 1298 if (con == NULL) { 1299 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole", 1300 device_id, head); 1301 return NULL; 1302 } 1303 1304 return con; 1305 } 1306 1307 static QemuConsole *qemu_graphic_console_lookup_unused(void) 1308 { 1309 QemuConsole *con; 1310 Object *obj; 1311 1312 QTAILQ_FOREACH(con, &consoles, next) { 1313 if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) { 1314 continue; 1315 } 1316 obj = object_property_get_link(OBJECT(con), 1317 "device", &error_abort); 1318 if (obj != NULL) { 1319 continue; 1320 } 1321 return con; 1322 } 1323 return NULL; 1324 } 1325 1326 QEMUCursor *qemu_console_get_cursor(QemuConsole *con) 1327 { 1328 return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL; 1329 } 1330 1331 bool qemu_console_is_visible(QemuConsole *con) 1332 { 1333 return con->dcls > 0; 1334 } 1335 1336 bool qemu_console_is_graphic(QemuConsole *con) 1337 { 1338 return con && QEMU_IS_GRAPHIC_CONSOLE(con); 1339 } 1340 1341 bool qemu_console_is_fixedsize(QemuConsole *con) 1342 { 1343 return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con)); 1344 } 1345 1346 bool qemu_console_is_gl_blocked(QemuConsole *con) 1347 { 1348 assert(con != NULL); 1349 return con->gl_block; 1350 } 1351 1352 static bool qemu_graphic_console_is_multihead(QemuGraphicConsole *c) 1353 { 1354 QemuConsole *con; 1355 1356 QTAILQ_FOREACH(con, &consoles, next) { 1357 QemuGraphicConsole *candidate; 1358 1359 if (!QEMU_IS_GRAPHIC_CONSOLE(con)) { 1360 continue; 1361 } 1362 1363 candidate = QEMU_GRAPHIC_CONSOLE(con); 1364 if (candidate->device != c->device) { 1365 continue; 1366 } 1367 1368 if (candidate->head != c->head) { 1369 return true; 1370 } 1371 } 1372 return false; 1373 } 1374 1375 char *qemu_console_get_label(QemuConsole *con) 1376 { 1377 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 1378 QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con); 1379 if (c->device) { 1380 DeviceState *dev; 1381 bool multihead; 1382 1383 dev = DEVICE(c->device); 1384 multihead = qemu_graphic_console_is_multihead(c); 1385 if (multihead) { 1386 return g_strdup_printf("%s.%d", dev->id ? 1387 dev->id : 1388 object_get_typename(c->device), 1389 c->head); 1390 } else { 1391 return g_strdup_printf("%s", dev->id ? 1392 dev->id : 1393 object_get_typename(c->device)); 1394 } 1395 } 1396 return g_strdup("VGA"); 1397 } else if (QEMU_IS_TEXT_CONSOLE(con)) { 1398 const char *label = qemu_text_console_get_label(QEMU_TEXT_CONSOLE(con)); 1399 if (label) { 1400 return g_strdup(label); 1401 } 1402 } 1403 1404 return g_strdup_printf("vc%d", con->index); 1405 } 1406 1407 int qemu_console_get_index(QemuConsole *con) 1408 { 1409 return con ? con->index : -1; 1410 } 1411 1412 uint32_t qemu_console_get_head(QemuConsole *con) 1413 { 1414 if (con == NULL) { 1415 return -1; 1416 } 1417 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 1418 return QEMU_GRAPHIC_CONSOLE(con)->head; 1419 } 1420 return 0; 1421 } 1422 1423 int qemu_console_get_width(QemuConsole *con, int fallback) 1424 { 1425 if (con == NULL) { 1426 return fallback; 1427 } 1428 switch (con->scanout.kind) { 1429 case SCANOUT_DMABUF: 1430 return qemu_dmabuf_get_width(con->scanout.dmabuf); 1431 case SCANOUT_TEXTURE: 1432 return con->scanout.texture.width; 1433 case SCANOUT_SURFACE: 1434 return surface_width(con->surface); 1435 default: 1436 return fallback; 1437 } 1438 } 1439 1440 int qemu_console_get_height(QemuConsole *con, int fallback) 1441 { 1442 if (con == NULL) { 1443 return fallback; 1444 } 1445 switch (con->scanout.kind) { 1446 case SCANOUT_DMABUF: 1447 return qemu_dmabuf_get_height(con->scanout.dmabuf); 1448 case SCANOUT_TEXTURE: 1449 return con->scanout.texture.height; 1450 case SCANOUT_SURFACE: 1451 return surface_height(con->surface); 1452 default: 1453 return fallback; 1454 } 1455 } 1456 1457 int qemu_invalidate_text_consoles(void) 1458 { 1459 QemuConsole *s; 1460 int count = 0; 1461 1462 QTAILQ_FOREACH(s, &consoles, next) { 1463 if (qemu_console_is_graphic(s) || 1464 !qemu_console_is_visible(s)) { 1465 continue; 1466 } 1467 count++; 1468 graphic_hw_invalidate(s); 1469 } 1470 1471 return count; 1472 } 1473 1474 void qemu_console_resize(QemuConsole *s, int width, int height) 1475 { 1476 DisplaySurface *surface = qemu_console_surface(s); 1477 1478 assert(QEMU_IS_GRAPHIC_CONSOLE(s)); 1479 1480 if ((s->scanout.kind != SCANOUT_SURFACE || 1481 (surface && surface_is_allocated(surface) && 1482 !surface_is_placeholder(surface))) && 1483 qemu_console_get_width(s, -1) == width && 1484 qemu_console_get_height(s, -1) == height) { 1485 return; 1486 } 1487 1488 surface = qemu_create_displaysurface(width, height); 1489 dpy_gfx_replace_surface(s, surface); 1490 } 1491 1492 DisplaySurface *qemu_console_surface(QemuConsole *console) 1493 { 1494 switch (console->scanout.kind) { 1495 case SCANOUT_SURFACE: 1496 return console->surface; 1497 default: 1498 return NULL; 1499 } 1500 } 1501 1502 PixelFormat qemu_default_pixelformat(int bpp) 1503 { 1504 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); 1505 PixelFormat pf = qemu_pixelformat_from_pixman(fmt); 1506 return pf; 1507 } 1508 1509 static QemuDisplay *dpys[DISPLAY_TYPE__MAX]; 1510 1511 void qemu_display_register(QemuDisplay *ui) 1512 { 1513 assert(ui->type < DISPLAY_TYPE__MAX); 1514 dpys[ui->type] = ui; 1515 } 1516 1517 bool qemu_display_find_default(DisplayOptions *opts) 1518 { 1519 static DisplayType prio[] = { 1520 #if defined(CONFIG_GTK) 1521 DISPLAY_TYPE_GTK, 1522 #endif 1523 #if defined(CONFIG_SDL) 1524 DISPLAY_TYPE_SDL, 1525 #endif 1526 #if defined(CONFIG_COCOA) 1527 DISPLAY_TYPE_COCOA 1528 #endif 1529 }; 1530 int i; 1531 1532 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { 1533 if (dpys[prio[i]] == NULL) { 1534 Error *local_err = NULL; 1535 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err); 1536 if (rv < 0) { 1537 error_report_err(local_err); 1538 } 1539 } 1540 if (dpys[prio[i]] == NULL) { 1541 continue; 1542 } 1543 opts->type = prio[i]; 1544 return true; 1545 } 1546 return false; 1547 } 1548 1549 void qemu_display_early_init(DisplayOptions *opts) 1550 { 1551 assert(opts->type < DISPLAY_TYPE__MAX); 1552 if (opts->type == DISPLAY_TYPE_NONE) { 1553 return; 1554 } 1555 if (dpys[opts->type] == NULL) { 1556 Error *local_err = NULL; 1557 int rv = ui_module_load(DisplayType_str(opts->type), &local_err); 1558 if (rv < 0) { 1559 error_report_err(local_err); 1560 } 1561 } 1562 if (dpys[opts->type] == NULL) { 1563 error_report("Display '%s' is not available.", 1564 DisplayType_str(opts->type)); 1565 exit(1); 1566 } 1567 if (dpys[opts->type]->early_init) { 1568 dpys[opts->type]->early_init(opts); 1569 } 1570 } 1571 1572 void qemu_display_init(DisplayState *ds, DisplayOptions *opts) 1573 { 1574 assert(opts->type < DISPLAY_TYPE__MAX); 1575 if (opts->type == DISPLAY_TYPE_NONE) { 1576 return; 1577 } 1578 assert(dpys[opts->type] != NULL); 1579 dpys[opts->type]->init(ds, opts); 1580 } 1581 1582 const char *qemu_display_get_vc(DisplayOptions *opts) 1583 { 1584 #ifdef CONFIG_PIXMAN 1585 const char *vc = "vc:80Cx24C"; 1586 #else 1587 const char *vc = NULL; 1588 #endif 1589 1590 assert(opts->type < DISPLAY_TYPE__MAX); 1591 if (dpys[opts->type] && dpys[opts->type]->vc) { 1592 vc = dpys[opts->type]->vc; 1593 } 1594 return vc; 1595 } 1596 1597 void qemu_display_help(void) 1598 { 1599 int idx; 1600 1601 printf("Available display backend types:\n"); 1602 printf("none\n"); 1603 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { 1604 if (!dpys[idx]) { 1605 Error *local_err = NULL; 1606 int rv = ui_module_load(DisplayType_str(idx), &local_err); 1607 if (rv < 0) { 1608 error_report_err(local_err); 1609 } 1610 } 1611 if (dpys[idx]) { 1612 printf("%s\n", DisplayType_str(dpys[idx]->type)); 1613 } 1614 } 1615 printf("\n" 1616 "Some display backends support suboptions, which can be set with\n" 1617 " -display backend,option=value,option=value...\n" 1618 "For a short list of the suboptions for each display, see the " 1619 "top-level -help output; more detail is in the documentation.\n"); 1620 } 1621