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