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 int 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 = qemu_spice_create_cursor_update(ssd, ssd->cursor, 0); 452 } 453 qemu_mutex_unlock(&ssd->lock); 454 } 455 456 void qemu_spice_cursor_refresh_bh(void *opaque) 457 { 458 SimpleSpiceDisplay *ssd = opaque; 459 460 qemu_mutex_lock(&ssd->lock); 461 if (ssd->cursor) { 462 QEMUCursor *c = ssd->cursor; 463 assert(ssd->dcl.con); 464 cursor_ref(c); 465 qemu_mutex_unlock(&ssd->lock); 466 dpy_cursor_define(ssd->dcl.con, c); 467 qemu_mutex_lock(&ssd->lock); 468 cursor_unref(c); 469 } 470 471 if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { 472 int x, y; 473 assert(ssd->dcl.con); 474 x = ssd->mouse_x; 475 y = ssd->mouse_y; 476 ssd->mouse_x = -1; 477 ssd->mouse_y = -1; 478 qemu_mutex_unlock(&ssd->lock); 479 dpy_mouse_set(ssd->dcl.con, x, y, 1); 480 } else { 481 qemu_mutex_unlock(&ssd->lock); 482 } 483 } 484 485 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) 486 { 487 graphic_hw_update(ssd->dcl.con); 488 489 WITH_QEMU_LOCK_GUARD(&ssd->lock) { 490 if (QTAILQ_EMPTY(&ssd->updates) && ssd->ds) { 491 qemu_spice_create_update(ssd); 492 ssd->notify++; 493 } 494 } 495 496 trace_qemu_spice_display_refresh(ssd->qxl.id, ssd->notify); 497 if (ssd->notify) { 498 ssd->notify = 0; 499 qemu_spice_wakeup(ssd); 500 } 501 } 502 503 /* spice display interface callbacks */ 504 505 #if SPICE_HAS_ATTACHED_WORKER 506 static void interface_attached_worker(QXLInstance *sin) 507 { 508 /* nothing to do */ 509 } 510 #else 511 static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) 512 { 513 /* nothing to do */ 514 } 515 #endif 516 517 static void interface_set_compression_level(QXLInstance *sin, int level) 518 { 519 /* nothing to do */ 520 } 521 522 static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) 523 { 524 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 525 526 info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; 527 info->memslot_id_bits = MEMSLOT_SLOT_BITS; 528 info->num_memslots = NUM_MEMSLOTS; 529 info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; 530 info->internal_groupslot_id = 0; 531 info->qxl_ram_size = 16 * 1024 * 1024; 532 info->n_surfaces = ssd->num_surfaces; 533 } 534 535 static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext) 536 { 537 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 538 SimpleSpiceUpdate *update; 539 int ret = false; 540 541 qemu_mutex_lock(&ssd->lock); 542 update = QTAILQ_FIRST(&ssd->updates); 543 if (update != NULL) { 544 QTAILQ_REMOVE(&ssd->updates, update, next); 545 *ext = update->ext; 546 ret = true; 547 } 548 qemu_mutex_unlock(&ssd->lock); 549 550 return ret; 551 } 552 553 static int interface_req_cmd_notification(QXLInstance *sin) 554 { 555 return 1; 556 } 557 558 static void interface_release_resource(QXLInstance *sin, 559 QXLReleaseInfoExt rext) 560 { 561 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 562 SimpleSpiceUpdate *update; 563 SimpleSpiceCursor *cursor; 564 QXLCommandExt *ext; 565 566 if (!rext.info) { 567 return; 568 } 569 570 ext = (void *)(intptr_t)(rext.info->id); 571 switch (ext->cmd.type) { 572 case QXL_CMD_DRAW: 573 update = container_of(ext, SimpleSpiceUpdate, ext); 574 qemu_spice_destroy_update(ssd, update); 575 break; 576 case QXL_CMD_CURSOR: 577 cursor = container_of(ext, SimpleSpiceCursor, ext); 578 g_free(cursor); 579 break; 580 default: 581 g_assert_not_reached(); 582 } 583 } 584 585 static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext) 586 { 587 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 588 int ret; 589 590 QEMU_LOCK_GUARD(&ssd->lock); 591 if (ssd->ptr_define) { 592 *ext = ssd->ptr_define->ext; 593 ssd->ptr_define = NULL; 594 ret = true; 595 } else if (ssd->ptr_move) { 596 *ext = ssd->ptr_move->ext; 597 ssd->ptr_move = NULL; 598 ret = true; 599 } else { 600 ret = false; 601 } 602 return ret; 603 } 604 605 static int interface_req_cursor_notification(QXLInstance *sin) 606 { 607 return 1; 608 } 609 610 static void interface_notify_update(QXLInstance *sin, uint32_t update_id) 611 { 612 fprintf(stderr, "%s: abort()\n", __func__); 613 abort(); 614 } 615 616 static int interface_flush_resources(QXLInstance *sin) 617 { 618 fprintf(stderr, "%s: abort()\n", __func__); 619 abort(); 620 return 0; 621 } 622 623 static void interface_update_area_complete(QXLInstance *sin, 624 uint32_t surface_id, 625 QXLRect *dirty, uint32_t num_updated_rects) 626 { 627 /* should never be called, used in qxl native mode only */ 628 fprintf(stderr, "%s: abort()\n", __func__); 629 abort(); 630 } 631 632 /* called from spice server thread context only */ 633 static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) 634 { 635 QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token; 636 637 switch (cookie->type) { 638 #ifdef HAVE_SPICE_GL 639 case QXL_COOKIE_TYPE_GL_DRAW_DONE: 640 { 641 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 642 qemu_bh_schedule(ssd->gl_unblock_bh); 643 break; 644 } 645 case QXL_COOKIE_TYPE_IO: 646 if (cookie->io == QXL_IO_MONITORS_CONFIG_ASYNC) { 647 g_free(cookie->u.data); 648 } 649 break; 650 #endif 651 default: 652 /* should never be called, used in qxl native mode only */ 653 fprintf(stderr, "%s: abort()\n", __func__); 654 abort(); 655 } 656 g_free(cookie); 657 } 658 659 static void interface_set_client_capabilities(QXLInstance *sin, 660 uint8_t client_present, 661 uint8_t caps[58]) 662 { 663 /* nothing to do */ 664 } 665 666 static int interface_client_monitors_config(QXLInstance *sin, 667 VDAgentMonitorsConfig *mc) 668 { 669 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); 670 QemuUIInfo info; 671 int head; 672 673 if (!dpy_ui_info_supported(ssd->dcl.con)) { 674 return 0; /* == not supported by guest */ 675 } 676 677 if (!mc) { 678 return 1; 679 } 680 681 info = *dpy_get_ui_info(ssd->dcl.con); 682 683 head = qemu_console_get_index(ssd->dcl.con); 684 if (mc->num_of_monitors > head) { 685 info.width = mc->monitors[head].width; 686 info.height = mc->monitors[head].height; 687 #if SPICE_SERVER_VERSION >= 0x000e04 /* release 0.14.4 */ 688 if (mc->flags & VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE) { 689 VDAgentMonitorMM *mm = (void *)&mc->monitors[mc->num_of_monitors]; 690 info.width_mm = mm[head].width; 691 info.height_mm = mm[head].height; 692 } 693 #endif 694 } 695 696 trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height); 697 dpy_set_ui_info(ssd->dcl.con, &info, false); 698 return 1; 699 } 700 701 static const QXLInterface dpy_interface = { 702 .base.type = SPICE_INTERFACE_QXL, 703 .base.description = "qemu simple display", 704 .base.major_version = SPICE_INTERFACE_QXL_MAJOR, 705 .base.minor_version = SPICE_INTERFACE_QXL_MINOR, 706 707 #if SPICE_HAS_ATTACHED_WORKER 708 .attached_worker = interface_attached_worker, 709 #else 710 .attache_worker = interface_attach_worker, 711 #endif 712 .set_compression_level = interface_set_compression_level, 713 .get_init_info = interface_get_init_info, 714 715 /* the callbacks below are called from spice server thread context */ 716 .get_command = interface_get_command, 717 .req_cmd_notification = interface_req_cmd_notification, 718 .release_resource = interface_release_resource, 719 .get_cursor_command = interface_get_cursor_command, 720 .req_cursor_notification = interface_req_cursor_notification, 721 .notify_update = interface_notify_update, 722 .flush_resources = interface_flush_resources, 723 .async_complete = interface_async_complete, 724 .update_area_complete = interface_update_area_complete, 725 .set_client_capabilities = interface_set_client_capabilities, 726 .client_monitors_config = interface_client_monitors_config, 727 }; 728 729 static void display_update(DisplayChangeListener *dcl, 730 int x, int y, int w, int h) 731 { 732 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 733 qemu_spice_display_update(ssd, x, y, w, h); 734 } 735 736 static void display_switch(DisplayChangeListener *dcl, 737 DisplaySurface *surface) 738 { 739 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 740 qemu_spice_display_switch(ssd, surface); 741 } 742 743 static void display_refresh(DisplayChangeListener *dcl) 744 { 745 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 746 qemu_spice_display_refresh(ssd); 747 } 748 749 static void display_mouse_set(DisplayChangeListener *dcl, 750 int x, int y, int on) 751 { 752 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 753 754 qemu_mutex_lock(&ssd->lock); 755 ssd->ptr_x = x; 756 ssd->ptr_y = y; 757 g_free(ssd->ptr_move); 758 ssd->ptr_move = qemu_spice_create_cursor_update(ssd, NULL, on); 759 qemu_mutex_unlock(&ssd->lock); 760 qemu_spice_wakeup(ssd); 761 } 762 763 static void display_mouse_define(DisplayChangeListener *dcl, 764 QEMUCursor *c) 765 { 766 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 767 768 qemu_mutex_lock(&ssd->lock); 769 cursor_ref(c); 770 cursor_unref(ssd->cursor); 771 ssd->cursor = c; 772 ssd->hot_x = c->hot_x; 773 ssd->hot_y = c->hot_y; 774 g_free(ssd->ptr_move); 775 ssd->ptr_move = NULL; 776 g_free(ssd->ptr_define); 777 ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, 0); 778 qemu_mutex_unlock(&ssd->lock); 779 qemu_spice_wakeup(ssd); 780 } 781 782 static const DisplayChangeListenerOps display_listener_ops = { 783 .dpy_name = "spice", 784 .dpy_gfx_update = display_update, 785 .dpy_gfx_switch = display_switch, 786 .dpy_gfx_check_format = qemu_pixman_check_format, 787 .dpy_refresh = display_refresh, 788 .dpy_mouse_set = display_mouse_set, 789 .dpy_cursor_define = display_mouse_define, 790 }; 791 792 #ifdef HAVE_SPICE_GL 793 794 static void qemu_spice_gl_monitor_config(SimpleSpiceDisplay *ssd, 795 int x, int y, int w, int h) 796 { 797 QXLMonitorsConfig *config; 798 QXLCookie *cookie; 799 800 config = g_malloc0(sizeof(QXLMonitorsConfig) + sizeof(QXLHead)); 801 config->count = 1; 802 config->max_allowed = 1; 803 config->heads[0].x = x; 804 config->heads[0].y = y; 805 config->heads[0].width = w; 806 config->heads[0].height = h; 807 cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, 808 QXL_IO_MONITORS_CONFIG_ASYNC); 809 cookie->u.data = config; 810 811 spice_qxl_monitors_config_async(&ssd->qxl, 812 (uintptr_t)config, 813 MEMSLOT_GROUP_HOST, 814 (uintptr_t)cookie); 815 } 816 817 static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block) 818 { 819 uint64_t timeout; 820 821 if (block) { 822 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 823 timeout += 1000; /* one sec */ 824 timer_mod(ssd->gl_unblock_timer, timeout); 825 } else { 826 timer_del(ssd->gl_unblock_timer); 827 } 828 graphic_hw_gl_block(ssd->dcl.con, block); 829 } 830 831 static void qemu_spice_gl_unblock_bh(void *opaque) 832 { 833 SimpleSpiceDisplay *ssd = opaque; 834 835 qemu_spice_gl_block(ssd, false); 836 } 837 838 static void qemu_spice_gl_block_timer(void *opaque) 839 { 840 warn_report("spice: no gl-draw-done within one second"); 841 } 842 843 static void spice_gl_refresh(DisplayChangeListener *dcl) 844 { 845 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 846 uint64_t cookie; 847 848 if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) { 849 return; 850 } 851 852 graphic_hw_update(dcl->con); 853 if (ssd->gl_updates && ssd->have_surface) { 854 qemu_spice_gl_block(ssd, true); 855 glFlush(); 856 cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); 857 spice_qxl_gl_draw_async(&ssd->qxl, 0, 0, 858 surface_width(ssd->ds), 859 surface_height(ssd->ds), 860 cookie); 861 ssd->gl_updates = 0; 862 } 863 } 864 865 static void spice_gl_update(DisplayChangeListener *dcl, 866 int x, int y, int w, int h) 867 { 868 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 869 870 surface_gl_update_texture(ssd->gls, ssd->ds, x, y, w, h); 871 ssd->gl_updates++; 872 } 873 874 static void spice_gl_switch(DisplayChangeListener *dcl, 875 struct DisplaySurface *new_surface) 876 { 877 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 878 EGLint stride, fourcc; 879 int fd; 880 881 if (ssd->ds) { 882 surface_gl_destroy_texture(ssd->gls, ssd->ds); 883 } 884 ssd->ds = new_surface; 885 if (ssd->ds) { 886 surface_gl_create_texture(ssd->gls, ssd->ds); 887 fd = egl_get_fd_for_texture(ssd->ds->texture, 888 &stride, &fourcc, 889 NULL); 890 if (fd < 0) { 891 surface_gl_destroy_texture(ssd->gls, ssd->ds); 892 return; 893 } 894 895 trace_qemu_spice_gl_surface(ssd->qxl.id, 896 surface_width(ssd->ds), 897 surface_height(ssd->ds), 898 fourcc); 899 900 /* note: spice server will close the fd */ 901 spice_qxl_gl_scanout(&ssd->qxl, fd, 902 surface_width(ssd->ds), 903 surface_height(ssd->ds), 904 stride, fourcc, false); 905 ssd->have_surface = true; 906 ssd->have_scanout = false; 907 908 qemu_spice_gl_monitor_config(ssd, 0, 0, 909 surface_width(ssd->ds), 910 surface_height(ssd->ds)); 911 } 912 } 913 914 static QEMUGLContext qemu_spice_gl_create_context(DisplayGLCtx *dgc, 915 QEMUGLParams *params) 916 { 917 eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, 918 qemu_egl_rn_ctx); 919 return qemu_egl_create_context(dgc, params); 920 } 921 922 static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) 923 { 924 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 925 926 trace_qemu_spice_gl_scanout_disable(ssd->qxl.id); 927 spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false); 928 qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0); 929 ssd->have_surface = false; 930 ssd->have_scanout = false; 931 } 932 933 static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, 934 uint32_t tex_id, 935 bool y_0_top, 936 uint32_t backing_width, 937 uint32_t backing_height, 938 uint32_t x, uint32_t y, 939 uint32_t w, uint32_t h, 940 void *d3d_tex2d) 941 { 942 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 943 EGLint stride = 0, fourcc = 0; 944 int fd = -1; 945 946 assert(tex_id); 947 fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL); 948 if (fd < 0) { 949 fprintf(stderr, "%s: failed to get fd for texture\n", __func__); 950 return; 951 } 952 trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc); 953 954 /* note: spice server will close the fd */ 955 spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, 956 stride, fourcc, y_0_top); 957 qemu_spice_gl_monitor_config(ssd, x, y, w, h); 958 ssd->have_surface = false; 959 ssd->have_scanout = true; 960 } 961 962 static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl, 963 QemuDmaBuf *dmabuf) 964 { 965 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 966 967 ssd->guest_dmabuf = dmabuf; 968 ssd->guest_dmabuf_refresh = true; 969 970 ssd->have_surface = false; 971 ssd->have_scanout = true; 972 } 973 974 static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl, 975 QemuDmaBuf *dmabuf, bool have_hot, 976 uint32_t hot_x, uint32_t hot_y) 977 { 978 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 979 uint32_t width, height, texture; 980 981 ssd->have_hot = have_hot; 982 ssd->hot_x = hot_x; 983 ssd->hot_y = hot_y; 984 985 trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot); 986 if (dmabuf) { 987 egl_dmabuf_import_texture(dmabuf); 988 texture = qemu_dmabuf_get_texture(dmabuf); 989 if (!texture) { 990 return; 991 } 992 width = qemu_dmabuf_get_width(dmabuf); 993 height = qemu_dmabuf_get_height(dmabuf); 994 egl_fb_setup_for_tex(&ssd->cursor_fb, width, height, texture, false); 995 } else { 996 egl_fb_destroy(&ssd->cursor_fb); 997 } 998 } 999 1000 static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl, 1001 uint32_t pos_x, uint32_t pos_y) 1002 { 1003 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 1004 1005 qemu_mutex_lock(&ssd->lock); 1006 ssd->ptr_x = pos_x; 1007 ssd->ptr_y = pos_y; 1008 qemu_mutex_unlock(&ssd->lock); 1009 } 1010 1011 static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl, 1012 QemuDmaBuf *dmabuf) 1013 { 1014 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 1015 1016 if (ssd->guest_dmabuf == dmabuf) { 1017 ssd->guest_dmabuf = NULL; 1018 ssd->guest_dmabuf_refresh = false; 1019 } 1020 egl_dmabuf_release_texture(dmabuf); 1021 } 1022 1023 static void qemu_spice_gl_update(DisplayChangeListener *dcl, 1024 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 1025 { 1026 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 1027 EGLint stride = 0, fourcc = 0; 1028 bool render_cursor = false; 1029 bool y_0_top = false; /* FIXME */ 1030 uint64_t cookie; 1031 int fd; 1032 uint32_t width, height, texture; 1033 1034 if (!ssd->have_scanout) { 1035 return; 1036 } 1037 1038 if (ssd->cursor_fb.texture) { 1039 render_cursor = true; 1040 } 1041 if (ssd->render_cursor != render_cursor) { 1042 ssd->render_cursor = render_cursor; 1043 ssd->guest_dmabuf_refresh = true; 1044 egl_fb_destroy(&ssd->blit_fb); 1045 } 1046 1047 if (ssd->guest_dmabuf_refresh) { 1048 QemuDmaBuf *dmabuf = ssd->guest_dmabuf; 1049 width = qemu_dmabuf_get_width(dmabuf); 1050 height = qemu_dmabuf_get_height(dmabuf); 1051 1052 if (render_cursor) { 1053 egl_dmabuf_import_texture(dmabuf); 1054 texture = qemu_dmabuf_get_texture(dmabuf); 1055 if (!texture) { 1056 return; 1057 } 1058 1059 /* source framebuffer */ 1060 egl_fb_setup_for_tex(&ssd->guest_fb, width, height, 1061 texture, false); 1062 1063 /* dest framebuffer */ 1064 if (ssd->blit_fb.width != width || 1065 ssd->blit_fb.height != height) { 1066 trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width, 1067 height); 1068 egl_fb_destroy(&ssd->blit_fb); 1069 egl_fb_setup_new_tex(&ssd->blit_fb, 1070 width, height); 1071 fd = egl_get_fd_for_texture(ssd->blit_fb.texture, 1072 &stride, &fourcc, NULL); 1073 spice_qxl_gl_scanout(&ssd->qxl, fd, width, height, 1074 stride, fourcc, false); 1075 } 1076 } else { 1077 stride = qemu_dmabuf_get_stride(dmabuf); 1078 fourcc = qemu_dmabuf_get_fourcc(dmabuf); 1079 y_0_top = qemu_dmabuf_get_y0_top(dmabuf); 1080 fd = qemu_dmabuf_dup_fd(dmabuf); 1081 1082 trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, width, height); 1083 /* note: spice server will close the fd, so hand over a dup */ 1084 spice_qxl_gl_scanout(&ssd->qxl, fd, width, height, 1085 stride, fourcc, y_0_top); 1086 } 1087 qemu_spice_gl_monitor_config(ssd, 0, 0, width, height); 1088 ssd->guest_dmabuf_refresh = false; 1089 } 1090 1091 if (render_cursor) { 1092 int ptr_x, ptr_y; 1093 1094 qemu_mutex_lock(&ssd->lock); 1095 ptr_x = ssd->ptr_x; 1096 ptr_y = ssd->ptr_y; 1097 qemu_mutex_unlock(&ssd->lock); 1098 egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb, 1099 !y_0_top); 1100 egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb, 1101 !y_0_top, ptr_x, ptr_y, 1.0, 1.0); 1102 glFlush(); 1103 } 1104 1105 trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); 1106 qemu_spice_gl_block(ssd, true); 1107 glFlush(); 1108 cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); 1109 spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); 1110 } 1111 1112 static const DisplayChangeListenerOps display_listener_gl_ops = { 1113 .dpy_name = "spice-egl", 1114 .dpy_gfx_update = spice_gl_update, 1115 .dpy_gfx_switch = spice_gl_switch, 1116 .dpy_gfx_check_format = console_gl_check_format, 1117 .dpy_refresh = spice_gl_refresh, 1118 .dpy_mouse_set = display_mouse_set, 1119 .dpy_cursor_define = display_mouse_define, 1120 1121 .dpy_gl_scanout_disable = qemu_spice_gl_scanout_disable, 1122 .dpy_gl_scanout_texture = qemu_spice_gl_scanout_texture, 1123 .dpy_gl_scanout_dmabuf = qemu_spice_gl_scanout_dmabuf, 1124 .dpy_gl_cursor_dmabuf = qemu_spice_gl_cursor_dmabuf, 1125 .dpy_gl_cursor_position = qemu_spice_gl_cursor_position, 1126 .dpy_gl_release_dmabuf = qemu_spice_gl_release_dmabuf, 1127 .dpy_gl_update = qemu_spice_gl_update, 1128 }; 1129 1130 static bool 1131 qemu_spice_is_compatible_dcl(DisplayGLCtx *dgc, 1132 DisplayChangeListener *dcl) 1133 { 1134 return dcl->ops == &display_listener_gl_ops; 1135 } 1136 1137 static const DisplayGLCtxOps gl_ctx_ops = { 1138 .dpy_gl_ctx_is_compatible_dcl = qemu_spice_is_compatible_dcl, 1139 .dpy_gl_ctx_create = qemu_spice_gl_create_context, 1140 .dpy_gl_ctx_destroy = qemu_egl_destroy_context, 1141 .dpy_gl_ctx_make_current = qemu_egl_make_context_current, 1142 }; 1143 1144 #endif /* HAVE_SPICE_GL */ 1145 1146 static void qemu_spice_display_init_one(QemuConsole *con) 1147 { 1148 SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1); 1149 1150 qemu_spice_display_init_common(ssd); 1151 1152 ssd->dcl.ops = &display_listener_ops; 1153 #ifdef HAVE_SPICE_GL 1154 if (spice_opengl) { 1155 ssd->dcl.ops = &display_listener_gl_ops; 1156 ssd->dgc.ops = &gl_ctx_ops; 1157 ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd); 1158 ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1159 qemu_spice_gl_block_timer, ssd); 1160 ssd->gls = qemu_gl_init_shader(); 1161 ssd->have_surface = false; 1162 ssd->have_scanout = false; 1163 } 1164 #endif 1165 ssd->dcl.con = con; 1166 1167 ssd->qxl.base.sif = &dpy_interface.base; 1168 qemu_spice_add_display_interface(&ssd->qxl, con); 1169 1170 #if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */ 1171 Error *err = NULL; 1172 char device_address[256] = ""; 1173 if (qemu_console_fill_device_address(con, device_address, 256, &err)) { 1174 spice_qxl_set_device_info(&ssd->qxl, 1175 device_address, 1176 qemu_console_get_head(con), 1177 1); 1178 } else { 1179 error_report_err(err); 1180 } 1181 #endif 1182 1183 qemu_spice_create_host_memslot(ssd); 1184 1185 if (spice_opengl) { 1186 qemu_console_set_display_gl_ctx(con, &ssd->dgc); 1187 } 1188 register_displaychangelistener(&ssd->dcl); 1189 } 1190 1191 void qemu_spice_display_init(void) 1192 { 1193 QemuOptsList *olist = qemu_find_opts("spice"); 1194 QemuOpts *opts = QTAILQ_FIRST(&olist->head); 1195 QemuConsole *spice_con, *con; 1196 const char *str; 1197 int i; 1198 1199 str = qemu_opt_get(opts, "display"); 1200 if (str) { 1201 int head = qemu_opt_get_number(opts, "head", 0); 1202 Error *err = NULL; 1203 1204 spice_con = qemu_console_lookup_by_device_name(str, head, &err); 1205 if (err) { 1206 error_report("Failed to lookup display/head"); 1207 exit(1); 1208 } 1209 } else { 1210 spice_con = NULL; 1211 } 1212 1213 for (i = 0;; i++) { 1214 con = qemu_console_lookup_by_index(i); 1215 if (!con || !qemu_console_is_graphic(con)) { 1216 break; 1217 } 1218 if (qemu_spice_have_display_interface(con)) { 1219 continue; 1220 } 1221 if (spice_con != NULL && spice_con != con) { 1222 continue; 1223 } 1224 qemu_spice_display_init_one(con); 1225 } 1226 1227 qemu_spice_display_init_done(); 1228 } 1229