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