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