1 /* 2 * Copyright (C) 2010 Red Hat, Inc. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 or 7 * (at your option) version 3 of the License. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "qemu/osdep.h" 19 #include "ui/qemu-spice.h" 20 #include "qemu/error-report.h" 21 #include "qemu/timer.h" 22 #include "qemu/lockable.h" 23 #include "qemu/main-loop.h" 24 #include "qemu/option.h" 25 #include "qemu/queue.h" 26 #include "ui/console.h" 27 #include "trace.h" 28 29 #include "ui/spice-display.h" 30 31 #include "standard-headers/drm/drm_fourcc.h" 32 33 bool spice_opengl; 34 35 int qemu_spice_rect_is_empty(const QXLRect* r) 36 { 37 return r->top == r->bottom || r->left == r->right; 38 } 39 40 void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r) 41 { 42 if (qemu_spice_rect_is_empty(r)) { 43 return; 44 } 45 46 if (qemu_spice_rect_is_empty(dest)) { 47 *dest = *r; 48 return; 49 } 50 51 dest->top = MIN(dest->top, r->top); 52 dest->left = MIN(dest->left, r->left); 53 dest->bottom = MAX(dest->bottom, r->bottom); 54 dest->right = MAX(dest->right, r->right); 55 } 56 57 QXLCookie *qxl_cookie_new(int type, uint64_t io) 58 { 59 QXLCookie *cookie; 60 61 cookie = g_malloc0(sizeof(*cookie)); 62 cookie->type = type; 63 cookie->io = io; 64 return cookie; 65 } 66 67 void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot, 68 qxl_async_io async) 69 { 70 trace_qemu_spice_add_memslot(ssd->qxl.id, memslot->slot_id, 71 memslot->virt_start, memslot->virt_end, 72 async); 73 74 if (async != QXL_SYNC) { 75 spice_qxl_add_memslot_async(&ssd->qxl, memslot, 76 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, 77 QXL_IO_MEMSLOT_ADD_ASYNC)); 78 } else { 79 spice_qxl_add_memslot(&ssd->qxl, memslot); 80 } 81 } 82 83 void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid) 84 { 85 trace_qemu_spice_del_memslot(ssd->qxl.id, gid, sid); 86 spice_qxl_del_memslot(&ssd->qxl, gid, sid); 87 } 88 89 void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, 90 QXLDevSurfaceCreate *surface, 91 qxl_async_io async) 92 { 93 trace_qemu_spice_create_primary_surface(ssd->qxl.id, id, surface, async); 94 if (async != QXL_SYNC) { 95 spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface, 96 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, 97 QXL_IO_CREATE_PRIMARY_ASYNC)); 98 } else { 99 spice_qxl_create_primary_surface(&ssd->qxl, id, surface); 100 } 101 } 102 103 void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, 104 uint32_t id, qxl_async_io async) 105 { 106 trace_qemu_spice_destroy_primary_surface(ssd->qxl.id, id, async); 107 if (async != QXL_SYNC) { 108 spice_qxl_destroy_primary_surface_async(&ssd->qxl, id, 109 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, 110 QXL_IO_DESTROY_PRIMARY_ASYNC)); 111 } else { 112 spice_qxl_destroy_primary_surface(&ssd->qxl, id); 113 } 114 } 115 116 void qemu_spice_wakeup(SimpleSpiceDisplay *ssd) 117 { 118 trace_qemu_spice_wakeup(ssd->qxl.id); 119 spice_qxl_wakeup(&ssd->qxl); 120 } 121 122 static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd, 123 QXLRect *rect) 124 { 125 SimpleSpiceUpdate *update; 126 QXLDrawable *drawable; 127 QXLImage *image; 128 QXLCommand *cmd; 129 int bw, bh; 130 struct timespec time_space; 131 pixman_image_t *dest; 132 133 trace_qemu_spice_create_update( 134 rect->left, rect->right, 135 rect->top, rect->bottom); 136 137 update = g_malloc0(sizeof(*update)); 138 drawable = &update->drawable; 139 image = &update->image; 140 cmd = &update->ext.cmd; 141 142 bw = rect->right - rect->left; 143 bh = rect->bottom - rect->top; 144 update->bitmap = g_malloc(bw * bh * 4); 145 146 drawable->bbox = *rect; 147 drawable->clip.type = SPICE_CLIP_TYPE_NONE; 148 drawable->effect = QXL_EFFECT_OPAQUE; 149 drawable->release_info.id = (uintptr_t)(&update->ext); 150 drawable->type = QXL_DRAW_COPY; 151 drawable->surfaces_dest[0] = -1; 152 drawable->surfaces_dest[1] = -1; 153 drawable->surfaces_dest[2] = -1; 154 clock_gettime(CLOCK_MONOTONIC, &time_space); 155 /* time in milliseconds from epoch. */ 156 drawable->mm_time = time_space.tv_sec * 1000 157 + time_space.tv_nsec / 1000 / 1000; 158 159 drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; 160 drawable->u.copy.src_bitmap = (uintptr_t)image; 161 drawable->u.copy.src_area.right = bw; 162 drawable->u.copy.src_area.bottom = bh; 163 164 QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++); 165 image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; 166 image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN; 167 image->bitmap.stride = bw * 4; 168 image->descriptor.width = image->bitmap.x = bw; 169 image->descriptor.height = image->bitmap.y = bh; 170 image->bitmap.data = (uintptr_t)(update->bitmap); 171 image->bitmap.palette = 0; 172 image->bitmap.format = SPICE_BITMAP_FMT_32BIT; 173 174 dest = pixman_image_create_bits(PIXMAN_LE_x8r8g8b8, bw, bh, 175 (void *)update->bitmap, bw * 4); 176 pixman_image_composite(PIXMAN_OP_SRC, ssd->surface, NULL, ssd->mirror, 177 rect->left, rect->top, 0, 0, 178 rect->left, rect->top, bw, bh); 179 pixman_image_composite(PIXMAN_OP_SRC, ssd->mirror, NULL, dest, 180 rect->left, rect->top, 0, 0, 181 0, 0, bw, bh); 182 pixman_image_unref(dest); 183 184 cmd->type = QXL_CMD_DRAW; 185 cmd->data = (uintptr_t)drawable; 186 187 QTAILQ_INSERT_TAIL(&ssd->updates, update, next); 188 } 189 190 static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) 191 { 192 static const int blksize = 32; 193 int blocks = DIV_ROUND_UP(surface_width(ssd->ds), blksize); 194 g_autofree int *dirty_top = NULL; 195 int y, yoff1, yoff2, x, xoff, blk, bw; 196 int bpp = surface_bytes_per_pixel(ssd->ds); 197 uint8_t *guest, *mirror; 198 199 if (qemu_spice_rect_is_empty(&ssd->dirty)) { 200 return; 201 }; 202 203 dirty_top = g_new(int, blocks); 204 for (blk = 0; blk < blocks; blk++) { 205 dirty_top[blk] = -1; 206 } 207 208 guest = surface_data(ssd->ds); 209 mirror = (void *)pixman_image_get_data(ssd->mirror); 210 for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) { 211 yoff1 = y * surface_stride(ssd->ds); 212 yoff2 = y * pixman_image_get_stride(ssd->mirror); 213 for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) { 214 xoff = x * bpp; 215 blk = x / blksize; 216 bw = MIN(blksize, ssd->dirty.right - x); 217 if (memcmp(guest + yoff1 + xoff, 218 mirror + yoff2 + xoff, 219 bw * bpp) == 0) { 220 if (dirty_top[blk] != -1) { 221 QXLRect update = { 222 .top = dirty_top[blk], 223 .bottom = y, 224 .left = x, 225 .right = x + bw, 226 }; 227 qemu_spice_create_one_update(ssd, &update); 228 dirty_top[blk] = -1; 229 } 230 } else { 231 if (dirty_top[blk] == -1) { 232 dirty_top[blk] = y; 233 } 234 } 235 } 236 } 237 238 for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) { 239 blk = x / blksize; 240 bw = MIN(blksize, ssd->dirty.right - x); 241 if (dirty_top[blk] != -1) { 242 QXLRect update = { 243 .top = dirty_top[blk], 244 .bottom = ssd->dirty.bottom, 245 .left = x, 246 .right = x + bw, 247 }; 248 qemu_spice_create_one_update(ssd, &update); 249 dirty_top[blk] = -1; 250 } 251 } 252 253 memset(&ssd->dirty, 0, sizeof(ssd->dirty)); 254 } 255 256 static SimpleSpiceCursor* 257 qemu_spice_create_cursor_update(SimpleSpiceDisplay *ssd, 258 QEMUCursor *c, 259 bool on) 260 { 261 size_t size = c ? c->width * c->height * 4 : 0; 262 SimpleSpiceCursor *update; 263 QXLCursorCmd *ccmd; 264 QXLCursor *cursor; 265 QXLCommand *cmd; 266 267 update = g_malloc0(sizeof(*update) + size); 268 ccmd = &update->cmd; 269 cursor = &update->cursor; 270 cmd = &update->ext.cmd; 271 272 if (c) { 273 ccmd->type = QXL_CURSOR_SET; 274 ccmd->u.set.position.x = ssd->ptr_x + ssd->hot_x; 275 ccmd->u.set.position.y = ssd->ptr_y + ssd->hot_y; 276 ccmd->u.set.visible = true; 277 ccmd->u.set.shape = (uintptr_t)cursor; 278 cursor->header.unique = ssd->unique++; 279 cursor->header.type = SPICE_CURSOR_TYPE_ALPHA; 280 cursor->header.width = c->width; 281 cursor->header.height = c->height; 282 cursor->header.hot_spot_x = c->hot_x; 283 cursor->header.hot_spot_y = c->hot_y; 284 cursor->data_size = size; 285 cursor->chunk.data_size = size; 286 memcpy(cursor->chunk.data, c->data, size); 287 } else if (!on) { 288 ccmd->type = QXL_CURSOR_HIDE; 289 } else { 290 ccmd->type = QXL_CURSOR_MOVE; 291 ccmd->u.position.x = ssd->ptr_x + ssd->hot_x; 292 ccmd->u.position.y = ssd->ptr_y + ssd->hot_y; 293 } 294 ccmd->release_info.id = (uintptr_t)(&update->ext); 295 296 cmd->type = QXL_CMD_CURSOR; 297 cmd->data = (uintptr_t)ccmd; 298 299 return update; 300 } 301 302 /* 303 * Called from spice server thread context (via interface_release_resource) 304 * We do *not* hold the global qemu mutex here, so extra care is needed 305 * when calling qemu functions. QEMU interfaces used: 306 * - g_free (underlying glibc free is re-entrant). 307 */ 308 void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update) 309 { 310 g_free(update->bitmap); 311 g_free(update); 312 } 313 314 void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd) 315 { 316 QXLDevMemSlot memslot; 317 318 memset(&memslot, 0, sizeof(memslot)); 319 memslot.slot_group_id = MEMSLOT_GROUP_HOST; 320 memslot.virt_end = ~0; 321 qemu_spice_add_memslot(ssd, &memslot, QXL_SYNC); 322 } 323 324 void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) 325 { 326 QXLDevSurfaceCreate surface; 327 uint64_t surface_size; 328 329 memset(&surface, 0, sizeof(surface)); 330 331 surface_size = (uint64_t) surface_width(ssd->ds) * 332 surface_height(ssd->ds) * 4; 333 assert(surface_size > 0); 334 assert(surface_size < INT_MAX); 335 if (ssd->bufsize < surface_size) { 336 ssd->bufsize = surface_size; 337 g_free(ssd->buf); 338 ssd->buf = g_malloc(ssd->bufsize); 339 } 340 341 surface.format = SPICE_SURFACE_FMT_32_xRGB; 342 surface.width = surface_width(ssd->ds); 343 surface.height = surface_height(ssd->ds); 344 surface.stride = -surface.width * 4; 345 surface.mouse_mode = true; 346 surface.flags = 0; 347 surface.type = 0; 348 surface.mem = (uintptr_t)ssd->buf; 349 surface.group_id = MEMSLOT_GROUP_HOST; 350 351 qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC); 352 } 353 354 void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) 355 { 356 qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC); 357 } 358 359 void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd) 360 { 361 qemu_mutex_init(&ssd->lock); 362 QTAILQ_INIT(&ssd->updates); 363 ssd->mouse_x = -1; 364 ssd->mouse_y = -1; 365 if (ssd->num_surfaces == 0) { 366 ssd->num_surfaces = 1024; 367 } 368 } 369 370 /* display listener callbacks */ 371 372 void qemu_spice_display_update(SimpleSpiceDisplay *ssd, 373 int x, int y, int w, int h) 374 { 375 QXLRect update_area; 376 377 trace_qemu_spice_display_update(ssd->qxl.id, x, y, w, h); 378 update_area.left = x, 379 update_area.right = x + w; 380 update_area.top = y; 381 update_area.bottom = y + h; 382 383 if (qemu_spice_rect_is_empty(&ssd->dirty)) { 384 ssd->notify++; 385 } 386 qemu_spice_rect_union(&ssd->dirty, &update_area); 387 } 388 389 void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, 390 DisplaySurface *surface) 391 { 392 SimpleSpiceUpdate *update; 393 bool need_destroy; 394 395 if (ssd->surface && 396 surface_width(surface) == pixman_image_get_width(ssd->surface) && 397 surface_height(surface) == pixman_image_get_height(ssd->surface) && 398 surface_format(surface) == pixman_image_get_format(ssd->surface)) { 399 /* no-resize fast path: just swap backing store */ 400 trace_qemu_spice_display_surface(ssd->qxl.id, 401 surface_width(surface), 402 surface_height(surface), 403 true); 404 qemu_mutex_lock(&ssd->lock); 405 ssd->ds = surface; 406 pixman_image_unref(ssd->surface); 407 ssd->surface = pixman_image_ref(ssd->ds->image); 408 qemu_mutex_unlock(&ssd->lock); 409 qemu_spice_display_update(ssd, 0, 0, 410 surface_width(surface), 411 surface_height(surface)); 412 return; 413 } 414 415 /* full mode switch */ 416 trace_qemu_spice_display_surface(ssd->qxl.id, 417 surface_width(surface), 418 surface_height(surface), 419 false); 420 421 memset(&ssd->dirty, 0, sizeof(ssd->dirty)); 422 if (ssd->surface) { 423 pixman_image_unref(ssd->surface); 424 ssd->surface = NULL; 425 pixman_image_unref(ssd->mirror); 426 ssd->mirror = NULL; 427 } 428 429 qemu_mutex_lock(&ssd->lock); 430 need_destroy = (ssd->ds != NULL); 431 ssd->ds = surface; 432 while ((update = QTAILQ_FIRST(&ssd->updates)) != NULL) { 433 QTAILQ_REMOVE(&ssd->updates, update, next); 434 qemu_spice_destroy_update(ssd, update); 435 } 436 qemu_mutex_unlock(&ssd->lock); 437 if (need_destroy) { 438 qemu_spice_destroy_host_primary(ssd); 439 } 440 if (ssd->ds) { 441 ssd->surface = pixman_image_ref(ssd->ds->image); 442 ssd->mirror = qemu_pixman_mirror_create(surface_format(ssd->ds), 443 ssd->ds->image); 444 qemu_spice_create_host_primary(ssd); 445 } 446 447 memset(&ssd->dirty, 0, sizeof(ssd->dirty)); 448 ssd->notify++; 449 450 qemu_mutex_lock(&ssd->lock); 451 if (ssd->cursor) { 452 g_free(ssd->ptr_define); 453 ssd->ptr_define = 454 qemu_spice_create_cursor_update(ssd, ssd->cursor, false); 455 } 456 qemu_mutex_unlock(&ssd->lock); 457 } 458 459 void qemu_spice_cursor_refresh_bh(void *opaque) 460 { 461 SimpleSpiceDisplay *ssd = opaque; 462 463 qemu_mutex_lock(&ssd->lock); 464 if (ssd->cursor) { 465 QEMUCursor *c = ssd->cursor; 466 assert(ssd->dcl.con); 467 cursor_ref(c); 468 qemu_mutex_unlock(&ssd->lock); 469 dpy_cursor_define(ssd->dcl.con, c); 470 qemu_mutex_lock(&ssd->lock); 471 cursor_unref(c); 472 } 473 474 if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { 475 int x, y; 476 assert(ssd->dcl.con); 477 x = ssd->mouse_x; 478 y = ssd->mouse_y; 479 ssd->mouse_x = -1; 480 ssd->mouse_y = -1; 481 qemu_mutex_unlock(&ssd->lock); 482 dpy_mouse_set(ssd->dcl.con, x, y, true); 483 } else { 484 qemu_mutex_unlock(&ssd->lock); 485 } 486 } 487 488 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) 489 { 490 graphic_hw_update(ssd->dcl.con); 491 492 WITH_QEMU_LOCK_GUARD(&ssd->lock) { 493 if (QTAILQ_EMPTY(&ssd->updates) && ssd->ds) { 494 qemu_spice_create_update(ssd); 495 ssd->notify++; 496 } 497 } 498 499 trace_qemu_spice_display_refresh(ssd->qxl.id, ssd->notify); 500 if (ssd->notify) { 501 ssd->notify = 0; 502 qemu_spice_wakeup(ssd); 503 } 504 } 505 506 /* spice display interface callbacks */ 507 508 #if SPICE_HAS_ATTACHED_WORKER 509 static void interface_attached_worker(QXLInstance *sin) 510 { 511 /* nothing to do */ 512 } 513 #else 514 static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) 515 { 516 /* nothing to do */ 517 } 518 #endif 519 520 static void interface_set_compression_level(QXLInstance *sin, int level) 521 { 522 /* nothing to do */ 523 } 524 525 static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) 526 { 527 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 528 529 info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; 530 info->memslot_id_bits = MEMSLOT_SLOT_BITS; 531 info->num_memslots = NUM_MEMSLOTS; 532 info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; 533 info->internal_groupslot_id = 0; 534 info->qxl_ram_size = 16 * 1024 * 1024; 535 info->n_surfaces = ssd->num_surfaces; 536 } 537 538 static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext) 539 { 540 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 541 SimpleSpiceUpdate *update; 542 int ret = false; 543 544 qemu_mutex_lock(&ssd->lock); 545 update = QTAILQ_FIRST(&ssd->updates); 546 if (update != NULL) { 547 QTAILQ_REMOVE(&ssd->updates, update, next); 548 *ext = update->ext; 549 ret = true; 550 } 551 qemu_mutex_unlock(&ssd->lock); 552 553 return ret; 554 } 555 556 static int interface_req_cmd_notification(QXLInstance *sin) 557 { 558 return 1; 559 } 560 561 static void interface_release_resource(QXLInstance *sin, 562 QXLReleaseInfoExt rext) 563 { 564 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 565 SimpleSpiceUpdate *update; 566 SimpleSpiceCursor *cursor; 567 QXLCommandExt *ext; 568 569 if (!rext.info) { 570 return; 571 } 572 573 ext = (void *)(intptr_t)(rext.info->id); 574 switch (ext->cmd.type) { 575 case QXL_CMD_DRAW: 576 update = container_of(ext, SimpleSpiceUpdate, ext); 577 qemu_spice_destroy_update(ssd, update); 578 break; 579 case QXL_CMD_CURSOR: 580 cursor = container_of(ext, SimpleSpiceCursor, ext); 581 g_free(cursor); 582 break; 583 default: 584 g_assert_not_reached(); 585 } 586 } 587 588 static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext) 589 { 590 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 591 int ret; 592 593 QEMU_LOCK_GUARD(&ssd->lock); 594 if (ssd->ptr_define) { 595 *ext = ssd->ptr_define->ext; 596 ssd->ptr_define = NULL; 597 ret = true; 598 } else if (ssd->ptr_move) { 599 *ext = ssd->ptr_move->ext; 600 ssd->ptr_move = NULL; 601 ret = true; 602 } else { 603 ret = false; 604 } 605 return ret; 606 } 607 608 static int interface_req_cursor_notification(QXLInstance *sin) 609 { 610 return 1; 611 } 612 613 static void interface_notify_update(QXLInstance *sin, uint32_t update_id) 614 { 615 fprintf(stderr, "%s: abort()\n", __func__); 616 abort(); 617 } 618 619 static int interface_flush_resources(QXLInstance *sin) 620 { 621 fprintf(stderr, "%s: abort()\n", __func__); 622 abort(); 623 return 0; 624 } 625 626 static void interface_update_area_complete(QXLInstance *sin, 627 uint32_t surface_id, 628 QXLRect *dirty, uint32_t num_updated_rects) 629 { 630 /* should never be called, used in qxl native mode only */ 631 fprintf(stderr, "%s: abort()\n", __func__); 632 abort(); 633 } 634 635 /* called from spice server thread context only */ 636 static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) 637 { 638 QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token; 639 640 switch (cookie->type) { 641 #ifdef HAVE_SPICE_GL 642 case QXL_COOKIE_TYPE_GL_DRAW_DONE: 643 { 644 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 645 qemu_bh_schedule(ssd->gl_unblock_bh); 646 break; 647 } 648 case QXL_COOKIE_TYPE_IO: 649 if (cookie->io == QXL_IO_MONITORS_CONFIG_ASYNC) { 650 g_free(cookie->u.data); 651 } 652 break; 653 #endif 654 default: 655 /* should never be called, used in qxl native mode only */ 656 fprintf(stderr, "%s: abort()\n", __func__); 657 abort(); 658 } 659 g_free(cookie); 660 } 661 662 static void interface_set_client_capabilities(QXLInstance *sin, 663 uint8_t client_present, 664 uint8_t caps[58]) 665 { 666 /* nothing to do */ 667 } 668 669 static int interface_client_monitors_config(QXLInstance *sin, 670 VDAgentMonitorsConfig *mc) 671 { 672 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 673 QemuUIInfo info; 674 int head; 675 676 if (!dpy_ui_info_supported(ssd->dcl.con)) { 677 return 0; /* == not supported by guest */ 678 } 679 680 if (!mc) { 681 return 1; 682 } 683 684 info = *dpy_get_ui_info(ssd->dcl.con); 685 686 head = qemu_console_get_index(ssd->dcl.con); 687 if (mc->num_of_monitors > head) { 688 info.width = mc->monitors[head].width; 689 info.height = mc->monitors[head].height; 690 #if SPICE_SERVER_VERSION >= 0x000e04 /* release 0.14.4 */ 691 if (mc->flags & VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE) { 692 VDAgentMonitorMM *mm = (void *)&mc->monitors[mc->num_of_monitors]; 693 info.width_mm = mm[head].width; 694 info.height_mm = mm[head].height; 695 } 696 #endif 697 } 698 699 trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height); 700 dpy_set_ui_info(ssd->dcl.con, &info, false); 701 return 1; 702 } 703 704 static const QXLInterface dpy_interface = { 705 .base.type = SPICE_INTERFACE_QXL, 706 .base.description = "qemu simple display", 707 .base.major_version = SPICE_INTERFACE_QXL_MAJOR, 708 .base.minor_version = SPICE_INTERFACE_QXL_MINOR, 709 710 #if SPICE_HAS_ATTACHED_WORKER 711 .attached_worker = interface_attached_worker, 712 #else 713 .attache_worker = interface_attach_worker, 714 #endif 715 .set_compression_level = interface_set_compression_level, 716 .get_init_info = interface_get_init_info, 717 718 /* the callbacks below are called from spice server thread context */ 719 .get_command = interface_get_command, 720 .req_cmd_notification = interface_req_cmd_notification, 721 .release_resource = interface_release_resource, 722 .get_cursor_command = interface_get_cursor_command, 723 .req_cursor_notification = interface_req_cursor_notification, 724 .notify_update = interface_notify_update, 725 .flush_resources = interface_flush_resources, 726 .async_complete = interface_async_complete, 727 .update_area_complete = interface_update_area_complete, 728 .set_client_capabilities = interface_set_client_capabilities, 729 .client_monitors_config = interface_client_monitors_config, 730 }; 731 732 static void display_update(DisplayChangeListener *dcl, 733 int x, int y, int w, int h) 734 { 735 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 736 qemu_spice_display_update(ssd, x, y, w, h); 737 } 738 739 static void display_switch(DisplayChangeListener *dcl, 740 DisplaySurface *surface) 741 { 742 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 743 qemu_spice_display_switch(ssd, surface); 744 } 745 746 static void display_refresh(DisplayChangeListener *dcl) 747 { 748 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 749 qemu_spice_display_refresh(ssd); 750 } 751 752 static void display_mouse_set(DisplayChangeListener *dcl, 753 int x, int y, bool on) 754 { 755 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 756 757 qemu_mutex_lock(&ssd->lock); 758 ssd->ptr_x = x; 759 ssd->ptr_y = y; 760 g_free(ssd->ptr_move); 761 ssd->ptr_move = qemu_spice_create_cursor_update(ssd, NULL, on); 762 qemu_mutex_unlock(&ssd->lock); 763 qemu_spice_wakeup(ssd); 764 } 765 766 static void display_mouse_define(DisplayChangeListener *dcl, 767 QEMUCursor *c) 768 { 769 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 770 771 qemu_mutex_lock(&ssd->lock); 772 cursor_ref(c); 773 cursor_unref(ssd->cursor); 774 ssd->cursor = c; 775 ssd->hot_x = c->hot_x; 776 ssd->hot_y = c->hot_y; 777 g_free(ssd->ptr_move); 778 ssd->ptr_move = NULL; 779 g_free(ssd->ptr_define); 780 ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, false); 781 qemu_mutex_unlock(&ssd->lock); 782 qemu_spice_wakeup(ssd); 783 } 784 785 static const DisplayChangeListenerOps display_listener_ops = { 786 .dpy_name = "spice", 787 .dpy_gfx_update = display_update, 788 .dpy_gfx_switch = display_switch, 789 .dpy_gfx_check_format = qemu_pixman_check_format, 790 .dpy_refresh = display_refresh, 791 .dpy_mouse_set = display_mouse_set, 792 .dpy_cursor_define = display_mouse_define, 793 }; 794 795 #ifdef HAVE_SPICE_GL 796 797 static void qemu_spice_gl_monitor_config(SimpleSpiceDisplay *ssd, 798 int x, int y, int w, int h) 799 { 800 QXLMonitorsConfig *config; 801 QXLCookie *cookie; 802 803 config = g_malloc0(sizeof(QXLMonitorsConfig) + sizeof(QXLHead)); 804 config->count = 1; 805 config->max_allowed = 1; 806 config->heads[0].x = x; 807 config->heads[0].y = y; 808 config->heads[0].width = w; 809 config->heads[0].height = h; 810 cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, 811 QXL_IO_MONITORS_CONFIG_ASYNC); 812 cookie->u.data = config; 813 814 spice_qxl_monitors_config_async(&ssd->qxl, 815 (uintptr_t)config, 816 MEMSLOT_GROUP_HOST, 817 (uintptr_t)cookie); 818 } 819 820 static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block) 821 { 822 uint64_t timeout; 823 824 if (block) { 825 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 826 timeout += 1000; /* one sec */ 827 timer_mod(ssd->gl_unblock_timer, timeout); 828 } else { 829 timer_del(ssd->gl_unblock_timer); 830 } 831 graphic_hw_gl_block(ssd->dcl.con, block); 832 } 833 834 static void qemu_spice_gl_unblock_bh(void *opaque) 835 { 836 SimpleSpiceDisplay *ssd = opaque; 837 838 qemu_spice_gl_block(ssd, false); 839 } 840 841 static void qemu_spice_gl_block_timer(void *opaque) 842 { 843 warn_report("spice: no gl-draw-done within one second"); 844 } 845 846 static void spice_gl_refresh(DisplayChangeListener *dcl) 847 { 848 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 849 uint64_t cookie; 850 851 if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) { 852 return; 853 } 854 855 graphic_hw_update(dcl->con); 856 if (ssd->gl_updates && ssd->have_surface) { 857 qemu_spice_gl_block(ssd, true); 858 glFlush(); 859 cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); 860 spice_qxl_gl_draw_async(&ssd->qxl, 0, 0, 861 surface_width(ssd->ds), 862 surface_height(ssd->ds), 863 cookie); 864 ssd->gl_updates = 0; 865 } 866 } 867 868 static void spice_gl_update(DisplayChangeListener *dcl, 869 int x, int y, int w, int h) 870 { 871 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 872 873 surface_gl_update_texture(ssd->gls, ssd->ds, x, y, w, h); 874 ssd->gl_updates++; 875 } 876 877 static void spice_server_gl_scanout(QXLInstance *qxl, 878 const int *fd, 879 uint32_t width, uint32_t height, 880 const uint32_t *offset, 881 const uint32_t *stride, 882 uint32_t num_planes, uint32_t format, 883 uint64_t modifier, int y_0_top) 884 { 885 #ifdef HAVE_SPICE_QXL_GL_SCANOUT2 886 spice_qxl_gl_scanout2(qxl, fd, width, height, offset, stride, 887 num_planes, format, modifier, y_0_top); 888 #else 889 if (num_planes <= 1) { 890 spice_qxl_gl_scanout(qxl, fd[0], width, height, stride[0], format, y_0_top); 891 } else { 892 error_report("SPICE server does not support multi plane GL scanout"); 893 } 894 #endif 895 } 896 897 static void spice_gl_switch(DisplayChangeListener *dcl, 898 struct DisplaySurface *new_surface) 899 { 900 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 901 902 if (ssd->ds) { 903 surface_gl_destroy_texture(ssd->gls, ssd->ds); 904 } 905 ssd->ds = new_surface; 906 if (ssd->ds) { 907 uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES]; 908 int fd[DMABUF_MAX_PLANES], num_planes, fourcc; 909 uint64_t modifier; 910 911 surface_gl_create_texture(ssd->gls, ssd->ds); 912 if (!egl_dmabuf_export_texture(ssd->ds->texture, 913 fd, 914 (EGLint *)offset, 915 (EGLint *)stride, 916 &fourcc, 917 &num_planes, 918 &modifier)) { 919 surface_gl_destroy_texture(ssd->gls, ssd->ds); 920 return; 921 } 922 923 trace_qemu_spice_gl_surface(ssd->qxl.id, 924 surface_width(ssd->ds), 925 surface_height(ssd->ds), 926 fourcc); 927 928 /* note: spice server will close the fd */ 929 spice_server_gl_scanout(&ssd->qxl, fd, 930 surface_width(ssd->ds), 931 surface_height(ssd->ds), 932 offset, stride, num_planes, 933 fourcc, modifier, false); 934 ssd->have_surface = true; 935 ssd->have_scanout = false; 936 937 qemu_spice_gl_monitor_config(ssd, 0, 0, 938 surface_width(ssd->ds), 939 surface_height(ssd->ds)); 940 } 941 } 942 943 static QEMUGLContext qemu_spice_gl_create_context(DisplayGLCtx *dgc, 944 QEMUGLParams *params) 945 { 946 eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, 947 qemu_egl_rn_ctx); 948 return qemu_egl_create_context(dgc, params); 949 } 950 951 static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) 952 { 953 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 954 955 trace_qemu_spice_gl_scanout_disable(ssd->qxl.id); 956 spice_server_gl_scanout(&ssd->qxl, NULL, 0, 0, NULL, NULL, 0, DRM_FORMAT_INVALID, 957 DRM_FORMAT_MOD_INVALID, false); 958 qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0); 959 ssd->have_surface = false; 960 ssd->have_scanout = false; 961 } 962 963 static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, 964 uint32_t tex_id, 965 bool y_0_top, 966 uint32_t backing_width, 967 uint32_t backing_height, 968 uint32_t x, uint32_t y, 969 uint32_t w, uint32_t h, 970 void *d3d_tex2d) 971 { 972 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 973 EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0; 974 int fd[DMABUF_MAX_PLANES], num_planes; 975 uint64_t modifier; 976 977 assert(tex_id); 978 if (!egl_dmabuf_export_texture(tex_id, fd, offset, stride, &fourcc, 979 &num_planes, &modifier)) { 980 fprintf(stderr, "%s: failed to export dmabuf for texture\n", __func__); 981 return; 982 } 983 984 trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc); 985 986 /* note: spice server will close the fd */ 987 spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, 988 (uint32_t *)offset, (uint32_t *)stride, num_planes, 989 fourcc, modifier, y_0_top); 990 qemu_spice_gl_monitor_config(ssd, x, y, w, h); 991 ssd->have_surface = false; 992 ssd->have_scanout = true; 993 } 994 995 static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl, 996 QemuDmaBuf *dmabuf) 997 { 998 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 999 1000 ssd->guest_dmabuf = dmabuf; 1001 ssd->guest_dmabuf_refresh = true; 1002 1003 ssd->have_surface = false; 1004 ssd->have_scanout = true; 1005 } 1006 1007 static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl, 1008 QemuDmaBuf *dmabuf, bool have_hot, 1009 uint32_t hot_x, uint32_t hot_y) 1010 { 1011 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 1012 uint32_t width, height, texture; 1013 1014 ssd->have_hot = have_hot; 1015 ssd->hot_x = hot_x; 1016 ssd->hot_y = hot_y; 1017 1018 trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot); 1019 if (dmabuf) { 1020 egl_dmabuf_import_texture(dmabuf); 1021 texture = qemu_dmabuf_get_texture(dmabuf); 1022 if (!texture) { 1023 return; 1024 } 1025 width = qemu_dmabuf_get_width(dmabuf); 1026 height = qemu_dmabuf_get_height(dmabuf); 1027 egl_fb_setup_for_tex(&ssd->cursor_fb, width, height, texture, false); 1028 } else { 1029 egl_fb_destroy(&ssd->cursor_fb); 1030 } 1031 } 1032 1033 static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl, 1034 uint32_t pos_x, uint32_t pos_y) 1035 { 1036 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 1037 1038 qemu_mutex_lock(&ssd->lock); 1039 ssd->ptr_x = pos_x; 1040 ssd->ptr_y = pos_y; 1041 qemu_mutex_unlock(&ssd->lock); 1042 } 1043 1044 static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl, 1045 QemuDmaBuf *dmabuf) 1046 { 1047 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 1048 1049 if (ssd->guest_dmabuf == dmabuf) { 1050 ssd->guest_dmabuf = NULL; 1051 ssd->guest_dmabuf_refresh = false; 1052 } 1053 egl_dmabuf_release_texture(dmabuf); 1054 } 1055 1056 static void qemu_spice_gl_update(DisplayChangeListener *dcl, 1057 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 1058 { 1059 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 1060 EGLint fourcc = 0; 1061 bool render_cursor = false; 1062 bool y_0_top = false; /* FIXME */ 1063 uint64_t cookie; 1064 uint32_t width, height, texture; 1065 1066 if (!ssd->have_scanout) { 1067 return; 1068 } 1069 1070 if (ssd->cursor_fb.texture) { 1071 render_cursor = true; 1072 } 1073 if (ssd->render_cursor != render_cursor) { 1074 ssd->render_cursor = render_cursor; 1075 ssd->guest_dmabuf_refresh = true; 1076 egl_fb_destroy(&ssd->blit_fb); 1077 } 1078 1079 if (ssd->guest_dmabuf_refresh) { 1080 QemuDmaBuf *dmabuf = ssd->guest_dmabuf; 1081 width = qemu_dmabuf_get_width(dmabuf); 1082 height = qemu_dmabuf_get_height(dmabuf); 1083 1084 if (render_cursor) { 1085 egl_dmabuf_import_texture(dmabuf); 1086 texture = qemu_dmabuf_get_texture(dmabuf); 1087 if (!texture) { 1088 return; 1089 } 1090 1091 /* source framebuffer */ 1092 egl_fb_setup_for_tex(&ssd->guest_fb, width, height, 1093 texture, false); 1094 1095 /* dest framebuffer */ 1096 if (ssd->blit_fb.width != width || 1097 ssd->blit_fb.height != height) { 1098 int fds[DMABUF_MAX_PLANES], num_planes; 1099 uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; 1100 uint64_t modifier; 1101 1102 trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width, 1103 height); 1104 egl_fb_destroy(&ssd->blit_fb); 1105 egl_fb_setup_new_tex(&ssd->blit_fb, 1106 width, height); 1107 if (!egl_dmabuf_export_texture(ssd->blit_fb.texture, fds, 1108 (EGLint *)offsets, (EGLint *)strides, 1109 &fourcc, &num_planes, &modifier)) { 1110 fprintf(stderr, 1111 "%s: failed to export dmabuf for texture\n", __func__); 1112 return; 1113 } 1114 1115 spice_server_gl_scanout(&ssd->qxl, fds, width, height, offsets, strides, 1116 num_planes, fourcc, modifier, false); 1117 } 1118 } else { 1119 int fds[DMABUF_MAX_PLANES]; 1120 int noffsets, nstrides; 1121 const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets); 1122 const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides); 1123 uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf); 1124 1125 assert(noffsets >= num_planes); 1126 assert(nstrides >= num_planes); 1127 1128 fourcc = qemu_dmabuf_get_fourcc(dmabuf); 1129 y_0_top = qemu_dmabuf_get_y0_top(dmabuf); 1130 qemu_dmabuf_dup_fds(dmabuf, fds, DMABUF_MAX_PLANES); 1131 1132 trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, width, height); 1133 /* note: spice server will close the fd, so hand over a dup */ 1134 spice_server_gl_scanout(&ssd->qxl, fds, width, height, 1135 offsets, strides, num_planes, 1136 fourcc, 1137 qemu_dmabuf_get_modifier(dmabuf), 1138 y_0_top); 1139 } 1140 qemu_spice_gl_monitor_config(ssd, 0, 0, width, height); 1141 ssd->guest_dmabuf_refresh = false; 1142 } 1143 1144 if (render_cursor) { 1145 int ptr_x, ptr_y; 1146 1147 qemu_mutex_lock(&ssd->lock); 1148 ptr_x = ssd->ptr_x; 1149 ptr_y = ssd->ptr_y; 1150 qemu_mutex_unlock(&ssd->lock); 1151 egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb, 1152 !y_0_top); 1153 egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb, 1154 !y_0_top, ptr_x, ptr_y, 1.0, 1.0); 1155 glFlush(); 1156 } 1157 1158 trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); 1159 qemu_spice_gl_block(ssd, true); 1160 glFlush(); 1161 cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); 1162 spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); 1163 } 1164 1165 static const DisplayChangeListenerOps display_listener_gl_ops = { 1166 .dpy_name = "spice-egl", 1167 .dpy_gfx_update = spice_gl_update, 1168 .dpy_gfx_switch = spice_gl_switch, 1169 .dpy_gfx_check_format = console_gl_check_format, 1170 .dpy_refresh = spice_gl_refresh, 1171 .dpy_mouse_set = display_mouse_set, 1172 .dpy_cursor_define = display_mouse_define, 1173 1174 .dpy_gl_scanout_disable = qemu_spice_gl_scanout_disable, 1175 .dpy_gl_scanout_texture = qemu_spice_gl_scanout_texture, 1176 .dpy_gl_scanout_dmabuf = qemu_spice_gl_scanout_dmabuf, 1177 .dpy_gl_cursor_dmabuf = qemu_spice_gl_cursor_dmabuf, 1178 .dpy_gl_cursor_position = qemu_spice_gl_cursor_position, 1179 .dpy_gl_release_dmabuf = qemu_spice_gl_release_dmabuf, 1180 .dpy_gl_update = qemu_spice_gl_update, 1181 }; 1182 1183 static bool 1184 qemu_spice_is_compatible_dcl(DisplayGLCtx *dgc, 1185 DisplayChangeListener *dcl) 1186 { 1187 return dcl->ops == &display_listener_gl_ops; 1188 } 1189 1190 static const DisplayGLCtxOps gl_ctx_ops = { 1191 .dpy_gl_ctx_is_compatible_dcl = qemu_spice_is_compatible_dcl, 1192 .dpy_gl_ctx_create = qemu_spice_gl_create_context, 1193 .dpy_gl_ctx_destroy = qemu_egl_destroy_context, 1194 .dpy_gl_ctx_make_current = qemu_egl_make_context_current, 1195 }; 1196 1197 #endif /* HAVE_SPICE_GL */ 1198 1199 static void qemu_spice_display_init_one(QemuConsole *con) 1200 { 1201 SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1); 1202 1203 qemu_spice_display_init_common(ssd); 1204 1205 ssd->dcl.ops = &display_listener_ops; 1206 #ifdef HAVE_SPICE_GL 1207 if (spice_opengl) { 1208 ssd->dcl.ops = &display_listener_gl_ops; 1209 ssd->dgc.ops = &gl_ctx_ops; 1210 ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd); 1211 ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1212 qemu_spice_gl_block_timer, ssd); 1213 ssd->gls = qemu_gl_init_shader(); 1214 ssd->have_surface = false; 1215 ssd->have_scanout = false; 1216 } 1217 #endif 1218 ssd->dcl.con = con; 1219 1220 ssd->qxl.base.sif = &dpy_interface.base; 1221 qemu_spice_add_display_interface(&ssd->qxl, con); 1222 1223 #if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */ 1224 Error *err = NULL; 1225 char device_address[256] = ""; 1226 if (qemu_console_fill_device_address(con, device_address, 256, &err)) { 1227 spice_qxl_set_device_info(&ssd->qxl, 1228 device_address, 1229 qemu_console_get_head(con), 1230 1); 1231 } else { 1232 error_report_err(err); 1233 } 1234 #endif 1235 1236 qemu_spice_create_host_memslot(ssd); 1237 1238 if (spice_opengl) { 1239 qemu_console_set_display_gl_ctx(con, &ssd->dgc); 1240 } 1241 register_displaychangelistener(&ssd->dcl); 1242 } 1243 1244 void qemu_spice_display_init(void) 1245 { 1246 QemuOptsList *olist = qemu_find_opts("spice"); 1247 QemuOpts *opts = QTAILQ_FIRST(&olist->head); 1248 QemuConsole *spice_con, *con; 1249 const char *str; 1250 int i; 1251 1252 str = qemu_opt_get(opts, "display"); 1253 if (str) { 1254 int head = qemu_opt_get_number(opts, "head", 0); 1255 Error *err = NULL; 1256 1257 spice_con = qemu_console_lookup_by_device_name(str, head, &err); 1258 if (err) { 1259 error_report("Failed to lookup display/head"); 1260 exit(1); 1261 } 1262 } else { 1263 spice_con = NULL; 1264 } 1265 1266 for (i = 0;; i++) { 1267 con = qemu_console_lookup_by_index(i); 1268 if (!con || !qemu_console_is_graphic(con)) { 1269 break; 1270 } 1271 if (qemu_spice_have_display_interface(con)) { 1272 continue; 1273 } 1274 if (spice_con != NULL && spice_con != con) { 1275 continue; 1276 } 1277 qemu_spice_display_init_one(con); 1278 } 1279 1280 qemu_spice_display_init_done(); 1281 } 1282