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