1 /* 2 * QEMU DBus display console 3 * 4 * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 #include "qemu/osdep.h" 25 #include "qemu/error-report.h" 26 #include "qapi/error.h" 27 #include "sysemu/sysemu.h" 28 #include "dbus.h" 29 #include "glib.h" 30 #ifdef G_OS_UNIX 31 #include <gio/gunixfdlist.h> 32 #endif 33 #ifdef WIN32 34 #include <d3d11.h> 35 #include <dxgi1_2.h> 36 #endif 37 38 #ifdef CONFIG_OPENGL 39 #include "ui/shader.h" 40 #include "ui/egl-helpers.h" 41 #include "ui/egl-context.h" 42 #include "ui/qemu-pixman.h" 43 #endif 44 #include "trace.h" 45 46 static void dbus_gfx_switch(DisplayChangeListener *dcl, 47 struct DisplaySurface *new_surface); 48 49 enum share_kind { 50 SHARE_KIND_NONE, 51 SHARE_KIND_MAPPED, 52 SHARE_KIND_D3DTEX, 53 }; 54 55 struct _DBusDisplayListener { 56 GObject parent; 57 58 char *bus_name; 59 DBusDisplayConsole *console; 60 GDBusConnection *conn; 61 62 QemuDBusDisplay1Listener *proxy; 63 64 #ifdef CONFIG_PIXMAN 65 /* Keep track of the damage region */ 66 pixman_region32_t gl_damage; 67 #else 68 int gl_damage; 69 #endif 70 71 DisplayChangeListener dcl; 72 DisplaySurface *ds; 73 enum share_kind ds_share; 74 75 bool ds_mapped; 76 bool can_share_map; 77 78 #ifdef WIN32 79 QemuDBusDisplay1ListenerWin32Map *map_proxy; 80 QemuDBusDisplay1ListenerWin32D3d11 *d3d11_proxy; 81 HANDLE peer_process; 82 ID3D11Texture2D *d3d_texture; 83 #ifdef CONFIG_OPENGL 84 egl_fb fb; 85 #endif 86 #else /* !WIN32 */ 87 QemuDBusDisplay1ListenerUnixMap *map_proxy; 88 #endif 89 90 guint dbus_filter; 91 guint32 display_serial_to_discard; 92 guint32 cursor_serial_to_discard; 93 }; 94 95 G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) 96 97 static void dbus_gfx_update(DisplayChangeListener *dcl, 98 int x, int y, int w, int h); 99 100 static void ddl_discard_display_messages(DBusDisplayListener *ddl) 101 { 102 guint32 serial = g_dbus_connection_get_last_serial( 103 g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); 104 105 g_atomic_int_set(&ddl->display_serial_to_discard, serial); 106 } 107 108 static void ddl_discard_cursor_messages(DBusDisplayListener *ddl) 109 { 110 guint32 serial = g_dbus_connection_get_last_serial( 111 g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); 112 113 g_atomic_int_set(&ddl->cursor_serial_to_discard, serial); 114 } 115 116 #ifdef CONFIG_OPENGL 117 static void dbus_scanout_disable(DisplayChangeListener *dcl) 118 { 119 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 120 121 ddl_discard_display_messages(ddl); 122 123 qemu_dbus_display1_listener_call_disable( 124 ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 125 } 126 127 #ifdef WIN32 128 static bool d3d_texture2d_share(ID3D11Texture2D *d3d_texture, 129 HANDLE *handle, Error **errp) 130 { 131 IDXGIResource1 *dxgiResource = NULL; 132 HRESULT hr; 133 134 hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture, 135 &IID_IDXGIResource1, 136 (void **)&dxgiResource); 137 if (FAILED(hr)) { 138 goto fail; 139 } 140 141 hr = dxgiResource->lpVtbl->CreateSharedHandle( 142 dxgiResource, 143 NULL, 144 DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, 145 NULL, 146 handle 147 ); 148 149 dxgiResource->lpVtbl->Release(dxgiResource); 150 151 if (SUCCEEDED(hr)) { 152 return true; 153 } 154 155 fail: 156 error_setg_win32(errp, GetLastError(), "failed to create shared handle"); 157 return false; 158 } 159 160 static bool d3d_texture2d_acquire0(ID3D11Texture2D *d3d_texture, Error **errp) 161 { 162 IDXGIKeyedMutex *dxgiMutex = NULL; 163 HRESULT hr; 164 165 hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture, 166 &IID_IDXGIKeyedMutex, 167 (void **)&dxgiMutex); 168 if (FAILED(hr)) { 169 goto fail; 170 } 171 172 hr = dxgiMutex->lpVtbl->AcquireSync(dxgiMutex, 0, INFINITE); 173 174 dxgiMutex->lpVtbl->Release(dxgiMutex); 175 176 if (SUCCEEDED(hr)) { 177 return true; 178 } 179 180 fail: 181 error_setg_win32(errp, GetLastError(), "failed to acquire texture mutex"); 182 return false; 183 } 184 185 static bool d3d_texture2d_release0(ID3D11Texture2D *d3d_texture, Error **errp) 186 { 187 IDXGIKeyedMutex *dxgiMutex = NULL; 188 HRESULT hr; 189 190 hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture, 191 &IID_IDXGIKeyedMutex, 192 (void **)&dxgiMutex); 193 if (FAILED(hr)) { 194 goto fail; 195 } 196 197 hr = dxgiMutex->lpVtbl->ReleaseSync(dxgiMutex, 0); 198 199 dxgiMutex->lpVtbl->Release(dxgiMutex); 200 201 if (SUCCEEDED(hr)) { 202 return true; 203 } 204 205 fail: 206 error_setg_win32(errp, GetLastError(), "failed to release texture mutex"); 207 return false; 208 } 209 #endif /* WIN32 */ 210 211 #if defined(CONFIG_GBM) || defined(WIN32) 212 static void dbus_update_gl_cb(GObject *source_object, 213 GAsyncResult *res, 214 gpointer user_data) 215 { 216 g_autoptr(GError) err = NULL; 217 DBusDisplayListener *ddl = user_data; 218 bool success; 219 220 #ifdef CONFIG_GBM 221 success = qemu_dbus_display1_listener_call_update_dmabuf_finish( 222 ddl->proxy, res, &err); 223 #endif 224 225 #ifdef WIN32 226 success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish( 227 ddl->d3d11_proxy, res, &err); 228 d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn); 229 #endif 230 231 if (!success) { 232 error_report("Failed to call update: %s", err->message); 233 } 234 235 graphic_hw_gl_block(ddl->dcl.con, false); 236 g_object_unref(ddl); 237 } 238 #endif 239 240 static void dbus_call_update_gl(DisplayChangeListener *dcl, 241 int x, int y, int w, int h) 242 { 243 #if defined(CONFIG_GBM) || defined(WIN32) 244 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 245 #endif 246 247 trace_dbus_update_gl(x, y, w, h); 248 249 glFlush(); 250 #ifdef CONFIG_GBM 251 graphic_hw_gl_block(ddl->dcl.con, true); 252 qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy, 253 x, y, w, h, 254 G_DBUS_CALL_FLAGS_NONE, 255 DBUS_DEFAULT_TIMEOUT, NULL, 256 dbus_update_gl_cb, 257 g_object_ref(ddl)); 258 #endif 259 260 #ifdef WIN32 261 switch (ddl->ds_share) { 262 case SHARE_KIND_MAPPED: 263 egl_fb_read_rect(ddl->ds, &ddl->fb, x, y, w, h); 264 dbus_gfx_update(dcl, x, y, w, h); 265 break; 266 case SHARE_KIND_D3DTEX: { 267 Error *err = NULL; 268 assert(ddl->d3d_texture); 269 270 graphic_hw_gl_block(ddl->dcl.con, true); 271 if (!d3d_texture2d_release0(ddl->d3d_texture, &err)) { 272 error_report_err(err); 273 return; 274 } 275 qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d( 276 ddl->d3d11_proxy, 277 x, y, w, h, 278 G_DBUS_CALL_FLAGS_NONE, 279 DBUS_DEFAULT_TIMEOUT, NULL, 280 dbus_update_gl_cb, 281 g_object_ref(ddl)); 282 break; 283 } 284 default: 285 g_warn_if_reached(); 286 } 287 #endif 288 } 289 290 #ifdef CONFIG_GBM 291 static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, 292 QemuDmaBuf *dmabuf) 293 { 294 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 295 g_autoptr(GError) err = NULL; 296 g_autoptr(GUnixFDList) fd_list = NULL; 297 int fd; 298 uint32_t width, height, stride, fourcc; 299 uint64_t modifier; 300 bool y0_top; 301 302 fd = qemu_dmabuf_get_fd(dmabuf); 303 fd_list = g_unix_fd_list_new(); 304 if (g_unix_fd_list_append(fd_list, fd, &err) != 0) { 305 error_report("Failed to setup dmabuf fdlist: %s", err->message); 306 return; 307 } 308 309 ddl_discard_display_messages(ddl); 310 311 width = qemu_dmabuf_get_width(dmabuf); 312 height = qemu_dmabuf_get_height(dmabuf); 313 stride = qemu_dmabuf_get_stride(dmabuf); 314 fourcc = qemu_dmabuf_get_fourcc(dmabuf); 315 modifier = qemu_dmabuf_get_modifier(dmabuf); 316 y0_top = qemu_dmabuf_get_y0_top(dmabuf); 317 318 /* FIXME: add missing x/y/w/h support */ 319 qemu_dbus_display1_listener_call_scanout_dmabuf( 320 ddl->proxy, g_variant_new_handle(0), 321 width, height, stride, fourcc, modifier, 322 y0_top, G_DBUS_CALL_FLAGS_NONE, 323 -1, fd_list, NULL, NULL, NULL); 324 } 325 #endif /* GBM */ 326 #endif /* OPENGL */ 327 328 #ifdef WIN32 329 static bool dbus_scanout_map(DBusDisplayListener *ddl) 330 { 331 g_autoptr(GError) err = NULL; 332 BOOL success; 333 HANDLE target_handle; 334 335 if (ddl->ds_share == SHARE_KIND_MAPPED) { 336 return true; 337 } 338 339 if (!ddl->can_share_map || !ddl->ds->share_handle) { 340 return false; 341 } 342 343 success = DuplicateHandle( 344 GetCurrentProcess(), 345 ddl->ds->share_handle, 346 ddl->peer_process, 347 &target_handle, 348 FILE_MAP_READ | SECTION_QUERY, 349 FALSE, 0); 350 if (!success) { 351 g_autofree char *msg = g_win32_error_message(GetLastError()); 352 g_debug("Failed to DuplicateHandle: %s", msg); 353 ddl->can_share_map = false; 354 return false; 355 } 356 357 ddl_discard_display_messages(ddl); 358 359 if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync( 360 ddl->map_proxy, 361 GPOINTER_TO_UINT(target_handle), 362 ddl->ds->share_handle_offset, 363 surface_width(ddl->ds), 364 surface_height(ddl->ds), 365 surface_stride(ddl->ds), 366 surface_format(ddl->ds), 367 G_DBUS_CALL_FLAGS_NONE, 368 DBUS_DEFAULT_TIMEOUT, 369 NULL, 370 &err)) { 371 g_debug("Failed to call ScanoutMap: %s", err->message); 372 ddl->can_share_map = false; 373 return false; 374 } 375 376 ddl->ds_share = SHARE_KIND_MAPPED; 377 378 return true; 379 } 380 381 #ifdef CONFIG_OPENGL 382 static bool 383 dbus_scanout_share_d3d_texture( 384 DBusDisplayListener *ddl, 385 ID3D11Texture2D *tex, 386 bool backing_y_0_top, 387 uint32_t backing_width, 388 uint32_t backing_height, 389 uint32_t x, uint32_t y, 390 uint32_t w, uint32_t h) 391 { 392 Error *err = NULL; 393 BOOL success; 394 HANDLE share_handle, target_handle; 395 396 if (!d3d_texture2d_release0(tex, &err)) { 397 error_report_err(err); 398 return false; 399 } 400 401 if (!d3d_texture2d_share(tex, &share_handle, &err)) { 402 error_report_err(err); 403 return false; 404 } 405 406 success = DuplicateHandle( 407 GetCurrentProcess(), 408 share_handle, 409 ddl->peer_process, 410 &target_handle, 411 0, 412 FALSE, DUPLICATE_SAME_ACCESS); 413 if (!success) { 414 g_autofree char *msg = g_win32_error_message(GetLastError()); 415 g_debug("Failed to DuplicateHandle: %s", msg); 416 CloseHandle(share_handle); 417 return false; 418 } 419 420 ddl_discard_display_messages(ddl); 421 422 qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d( 423 ddl->d3d11_proxy, 424 GPOINTER_TO_INT(target_handle), 425 backing_width, 426 backing_height, 427 backing_y_0_top, 428 x, y, w, h, 429 G_DBUS_CALL_FLAGS_NONE, 430 -1, 431 NULL, NULL, NULL); 432 433 CloseHandle(share_handle); 434 435 if (!d3d_texture2d_acquire0(tex, &err)) { 436 error_report_err(err); 437 return false; 438 } 439 440 ddl->d3d_texture = tex; 441 ddl->ds_share = SHARE_KIND_D3DTEX; 442 443 return true; 444 } 445 #endif /* CONFIG_OPENGL */ 446 #else /* !WIN32 */ 447 static bool dbus_scanout_map(DBusDisplayListener *ddl) 448 { 449 g_autoptr(GError) err = NULL; 450 g_autoptr(GUnixFDList) fd_list = NULL; 451 452 if (ddl->ds_share == SHARE_KIND_MAPPED) { 453 return true; 454 } 455 456 if (!ddl->can_share_map || ddl->ds->share_handle == SHAREABLE_NONE) { 457 return false; 458 } 459 460 ddl_discard_display_messages(ddl); 461 fd_list = g_unix_fd_list_new(); 462 if (g_unix_fd_list_append(fd_list, ddl->ds->share_handle, &err) != 0) { 463 g_debug("Failed to setup scanout map fdlist: %s", err->message); 464 ddl->can_share_map = false; 465 return false; 466 } 467 468 if (!qemu_dbus_display1_listener_unix_map_call_scanout_map_sync( 469 ddl->map_proxy, 470 g_variant_new_handle(0), 471 ddl->ds->share_handle_offset, 472 surface_width(ddl->ds), 473 surface_height(ddl->ds), 474 surface_stride(ddl->ds), 475 surface_format(ddl->ds), 476 G_DBUS_CALL_FLAGS_NONE, 477 DBUS_DEFAULT_TIMEOUT, 478 fd_list, 479 NULL, 480 NULL, 481 &err)) { 482 g_debug("Failed to call ScanoutMap: %s", err->message); 483 ddl->can_share_map = false; 484 return false; 485 } 486 487 ddl->ds_share = SHARE_KIND_MAPPED; 488 489 return true; 490 } 491 #endif /* WIN32 */ 492 493 #ifdef CONFIG_OPENGL 494 static void dbus_scanout_texture(DisplayChangeListener *dcl, 495 uint32_t tex_id, 496 bool backing_y_0_top, 497 uint32_t backing_width, 498 uint32_t backing_height, 499 uint32_t x, uint32_t y, 500 uint32_t w, uint32_t h, 501 void *d3d_tex2d) 502 { 503 trace_dbus_scanout_texture(tex_id, backing_y_0_top, 504 backing_width, backing_height, x, y, w, h); 505 #ifdef CONFIG_GBM 506 g_autoptr(QemuDmaBuf) dmabuf = NULL; 507 int fd; 508 uint32_t stride, fourcc; 509 uint64_t modifier; 510 511 assert(tex_id); 512 fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc, 513 &modifier); 514 if (fd < 0) { 515 error_report("%s: failed to get fd for texture", __func__); 516 return; 517 } 518 dmabuf = qemu_dmabuf_new(w, h, stride, x, y, backing_width, 519 backing_height, fourcc, modifier, fd, 520 false, backing_y_0_top); 521 522 dbus_scanout_dmabuf(dcl, dmabuf); 523 qemu_dmabuf_close(dmabuf); 524 #endif 525 526 #ifdef WIN32 527 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 528 529 /* there must be a matching gfx_switch before */ 530 assert(surface_width(ddl->ds) == w); 531 assert(surface_height(ddl->ds) == h); 532 533 if (d3d_tex2d) { 534 dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top, 535 backing_width, backing_height, x, y, w, h); 536 } else { 537 dbus_scanout_map(ddl); 538 egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false); 539 } 540 #endif 541 } 542 543 #ifdef CONFIG_GBM 544 static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, 545 QemuDmaBuf *dmabuf, bool have_hot, 546 uint32_t hot_x, uint32_t hot_y) 547 { 548 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 549 DisplaySurface *ds; 550 GVariant *v_data = NULL; 551 egl_fb cursor_fb = EGL_FB_INIT; 552 uint32_t width, height, texture; 553 554 if (!dmabuf) { 555 qemu_dbus_display1_listener_call_mouse_set( 556 ddl->proxy, 0, 0, false, 557 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 558 return; 559 } 560 561 ddl_discard_cursor_messages(ddl); 562 563 egl_dmabuf_import_texture(dmabuf); 564 texture = qemu_dmabuf_get_texture(dmabuf); 565 if (!texture) { 566 return; 567 } 568 569 width = qemu_dmabuf_get_width(dmabuf); 570 height = qemu_dmabuf_get_height(dmabuf); 571 572 egl_fb_setup_for_tex(&cursor_fb, width, height, texture, false); 573 ds = qemu_create_displaysurface(width, height); 574 egl_fb_read(ds, &cursor_fb); 575 576 v_data = g_variant_new_from_data( 577 G_VARIANT_TYPE("ay"), 578 surface_data(ds), 579 surface_width(ds) * surface_height(ds) * 4, 580 TRUE, 581 (GDestroyNotify)qemu_free_displaysurface, 582 ds); 583 qemu_dbus_display1_listener_call_cursor_define( 584 ddl->proxy, 585 surface_width(ds), 586 surface_height(ds), 587 hot_x, 588 hot_y, 589 v_data, 590 G_DBUS_CALL_FLAGS_NONE, 591 -1, 592 NULL, 593 NULL, 594 NULL); 595 } 596 597 static void dbus_release_dmabuf(DisplayChangeListener *dcl, 598 QemuDmaBuf *dmabuf) 599 { 600 dbus_scanout_disable(dcl); 601 } 602 #endif /* GBM */ 603 604 static void dbus_gl_cursor_position(DisplayChangeListener *dcl, 605 uint32_t pos_x, uint32_t pos_y) 606 { 607 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 608 609 qemu_dbus_display1_listener_call_mouse_set( 610 ddl->proxy, pos_x, pos_y, true, 611 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 612 } 613 614 static void dbus_scanout_update(DisplayChangeListener *dcl, 615 uint32_t x, uint32_t y, 616 uint32_t w, uint32_t h) 617 { 618 dbus_call_update_gl(dcl, x, y, w, h); 619 } 620 621 static void dbus_gl_refresh(DisplayChangeListener *dcl) 622 { 623 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 624 625 graphic_hw_update(dcl->con); 626 627 if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) { 628 return; 629 } 630 631 #ifdef CONFIG_PIXMAN 632 int n_rects = pixman_region32_n_rects(&ddl->gl_damage); 633 634 for (int i = 0; i < n_rects; i++) { 635 pixman_box32_t *box; 636 box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i; 637 /* TODO: Add a UpdateList call to send multiple updates at once */ 638 dbus_call_update_gl(dcl, box->x1, box->y1, 639 box->x2 - box->x1, box->y2 - box->y1); 640 } 641 pixman_region32_clear(&ddl->gl_damage); 642 #else 643 if (ddl->gl_damage) { 644 dbus_call_update_gl(dcl, 0, 0, 645 surface_width(ddl->ds), surface_height(ddl->ds)); 646 ddl->gl_damage = 0; 647 } 648 #endif 649 } 650 #endif /* OPENGL */ 651 652 static void dbus_refresh(DisplayChangeListener *dcl) 653 { 654 graphic_hw_update(dcl->con); 655 } 656 657 #ifdef CONFIG_OPENGL 658 static void dbus_gl_gfx_update(DisplayChangeListener *dcl, 659 int x, int y, int w, int h) 660 { 661 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 662 663 #ifdef CONFIG_PIXMAN 664 pixman_region32_t rect_region; 665 pixman_region32_init_rect(&rect_region, x, y, w, h); 666 pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region); 667 pixman_region32_fini(&rect_region); 668 #else 669 ddl->gl_damage++; 670 #endif 671 } 672 #endif 673 674 static void dbus_gfx_update_sub(DBusDisplayListener *ddl, 675 int x, int y, int w, int h) 676 { 677 pixman_image_t *img; 678 size_t stride; 679 GVariant *v_data; 680 681 /* make a copy, since gvariant only handles linear data */ 682 stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); 683 img = pixman_image_create_bits(surface_format(ddl->ds), 684 w, h, NULL, stride); 685 #ifdef CONFIG_PIXMAN 686 pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img, 687 x, y, 0, 0, 0, 0, w, h); 688 #else 689 { 690 uint8_t *src = (uint8_t *)pixman_image_get_data(ddl->ds->image); 691 uint8_t *dst = (uint8_t *)pixman_image_get_data(img); 692 int bp = PIXMAN_FORMAT_BPP(surface_format(ddl->ds)) / 8; 693 int hh; 694 695 for (hh = 0; hh < h; hh++) { 696 memcpy(&dst[stride * hh], 697 &src[surface_stride(ddl->ds) * (hh + y) + x * bp], 698 stride); 699 } 700 } 701 #endif 702 v_data = g_variant_new_from_data( 703 G_VARIANT_TYPE("ay"), 704 pixman_image_get_data(img), 705 pixman_image_get_stride(img) * h, 706 TRUE, 707 (GDestroyNotify)pixman_image_unref, 708 img); 709 qemu_dbus_display1_listener_call_update(ddl->proxy, 710 x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img), 711 v_data, 712 G_DBUS_CALL_FLAGS_NONE, 713 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 714 } 715 716 static void ddl_scanout(DBusDisplayListener *ddl) 717 { 718 GVariant *v_data; 719 720 v_data = g_variant_new_from_data( 721 G_VARIANT_TYPE("ay"), surface_data(ddl->ds), 722 surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE, 723 (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image)); 724 725 ddl_discard_display_messages(ddl); 726 727 qemu_dbus_display1_listener_call_scanout( 728 ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds), 729 surface_stride(ddl->ds), surface_format(ddl->ds), v_data, 730 G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL, 731 g_object_ref(ddl)); 732 } 733 734 static void dbus_gfx_update(DisplayChangeListener *dcl, 735 int x, int y, int w, int h) 736 { 737 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 738 739 assert(ddl->ds); 740 741 trace_dbus_update(x, y, w, h); 742 743 if (dbus_scanout_map(ddl)) { 744 #ifdef WIN32 745 qemu_dbus_display1_listener_win32_map_call_update_map( 746 ddl->map_proxy, 747 x, y, w, h, 748 G_DBUS_CALL_FLAGS_NONE, 749 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 750 #else 751 qemu_dbus_display1_listener_unix_map_call_update_map( 752 ddl->map_proxy, 753 x, y, w, h, 754 G_DBUS_CALL_FLAGS_NONE, 755 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 756 #endif 757 return; 758 } 759 760 if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { 761 return ddl_scanout(ddl); 762 } 763 764 dbus_gfx_update_sub(ddl, x, y, w, h); 765 } 766 767 #ifdef CONFIG_OPENGL 768 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, 769 struct DisplaySurface *new_surface) 770 { 771 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 772 773 trace_dbus_gl_gfx_switch(new_surface); 774 775 ddl->ds = new_surface; 776 ddl->ds_share = SHARE_KIND_NONE; 777 if (ddl->ds) { 778 int width = surface_width(ddl->ds); 779 int height = surface_height(ddl->ds); 780 781 /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ 782 dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, 783 width, height, 0, 0, width, height, NULL); 784 } 785 } 786 #endif 787 788 static void dbus_gfx_switch(DisplayChangeListener *dcl, 789 struct DisplaySurface *new_surface) 790 { 791 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 792 793 ddl->ds = new_surface; 794 ddl->ds_share = SHARE_KIND_NONE; 795 } 796 797 static void dbus_mouse_set(DisplayChangeListener *dcl, 798 int x, int y, bool on) 799 { 800 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 801 802 qemu_dbus_display1_listener_call_mouse_set( 803 ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 804 } 805 806 static void dbus_cursor_define(DisplayChangeListener *dcl, 807 QEMUCursor *c) 808 { 809 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 810 GVariant *v_data = NULL; 811 812 ddl_discard_cursor_messages(ddl); 813 814 v_data = g_variant_new_from_data( 815 G_VARIANT_TYPE("ay"), 816 c->data, 817 c->width * c->height * 4, 818 TRUE, 819 (GDestroyNotify)cursor_unref, 820 cursor_ref(c)); 821 822 qemu_dbus_display1_listener_call_cursor_define( 823 ddl->proxy, 824 c->width, 825 c->height, 826 c->hot_x, 827 c->hot_y, 828 v_data, 829 G_DBUS_CALL_FLAGS_NONE, 830 -1, 831 NULL, 832 NULL, 833 NULL); 834 } 835 836 #ifdef CONFIG_OPENGL 837 const DisplayChangeListenerOps dbus_gl_dcl_ops = { 838 .dpy_name = "dbus-gl", 839 .dpy_gfx_update = dbus_gl_gfx_update, 840 .dpy_gfx_switch = dbus_gl_gfx_switch, 841 .dpy_gfx_check_format = console_gl_check_format, 842 .dpy_refresh = dbus_gl_refresh, 843 .dpy_mouse_set = dbus_mouse_set, 844 .dpy_cursor_define = dbus_cursor_define, 845 846 .dpy_gl_scanout_disable = dbus_scanout_disable, 847 .dpy_gl_scanout_texture = dbus_scanout_texture, 848 #ifdef CONFIG_GBM 849 .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf, 850 .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf, 851 .dpy_gl_release_dmabuf = dbus_release_dmabuf, 852 #endif 853 .dpy_gl_cursor_position = dbus_gl_cursor_position, 854 .dpy_gl_update = dbus_scanout_update, 855 }; 856 #endif 857 858 const DisplayChangeListenerOps dbus_dcl_ops = { 859 .dpy_name = "dbus", 860 .dpy_gfx_update = dbus_gfx_update, 861 .dpy_gfx_switch = dbus_gfx_switch, 862 .dpy_refresh = dbus_refresh, 863 .dpy_mouse_set = dbus_mouse_set, 864 .dpy_cursor_define = dbus_cursor_define, 865 }; 866 867 static void 868 dbus_display_listener_dispose(GObject *object) 869 { 870 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 871 872 unregister_displaychangelistener(&ddl->dcl); 873 g_clear_object(&ddl->conn); 874 g_clear_pointer(&ddl->bus_name, g_free); 875 g_clear_object(&ddl->proxy); 876 #ifdef WIN32 877 g_clear_object(&ddl->map_proxy); 878 g_clear_object(&ddl->d3d11_proxy); 879 g_clear_pointer(&ddl->peer_process, CloseHandle); 880 #ifdef CONFIG_PIXMAN 881 pixman_region32_fini(&ddl->gl_damage); 882 #endif 883 #ifdef CONFIG_OPENGL 884 egl_fb_destroy(&ddl->fb); 885 #endif 886 #endif 887 888 G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); 889 } 890 891 static void 892 dbus_display_listener_constructed(GObject *object) 893 { 894 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 895 896 ddl->dcl.ops = &dbus_dcl_ops; 897 #ifdef CONFIG_OPENGL 898 if (display_opengl) { 899 ddl->dcl.ops = &dbus_gl_dcl_ops; 900 } 901 #endif 902 903 G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object); 904 } 905 906 static void 907 dbus_display_listener_class_init(DBusDisplayListenerClass *klass) 908 { 909 GObjectClass *object_class = G_OBJECT_CLASS(klass); 910 911 object_class->dispose = dbus_display_listener_dispose; 912 object_class->constructed = dbus_display_listener_constructed; 913 } 914 915 static void 916 dbus_display_listener_init(DBusDisplayListener *ddl) 917 { 918 #ifdef CONFIG_PIXMAN 919 pixman_region32_init(&ddl->gl_damage); 920 #endif 921 } 922 923 const char * 924 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl) 925 { 926 return ddl->bus_name ?: "p2p"; 927 } 928 929 DBusDisplayConsole * 930 dbus_display_listener_get_console(DBusDisplayListener *ddl) 931 { 932 return ddl->console; 933 } 934 935 static bool 936 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface) 937 { 938 QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy); 939 bool implements; 940 941 implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface); 942 if (!implements) { 943 g_debug("Display listener does not implement: `%s`", iface); 944 } 945 946 return implements; 947 } 948 949 #ifdef WIN32 950 static bool 951 dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl) 952 { 953 g_autoptr(GError) err = NULL; 954 GDBusConnection *conn; 955 GIOStream *stream; 956 GSocket *sock; 957 g_autoptr(GCredentials) creds = NULL; 958 DWORD *pid; 959 960 if (ddl->peer_process) { 961 return true; 962 } 963 964 conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)); 965 stream = g_dbus_connection_get_stream(conn); 966 967 if (!G_IS_UNIX_CONNECTION(stream)) { 968 return false; 969 } 970 971 sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream)); 972 creds = g_socket_get_credentials(sock, &err); 973 974 if (!creds) { 975 g_debug("Failed to get peer credentials: %s", err->message); 976 return false; 977 } 978 979 pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID); 980 981 if (pid == NULL) { 982 g_debug("Failed to get peer PID"); 983 return false; 984 } 985 986 ddl->peer_process = OpenProcess( 987 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, 988 false, *pid); 989 990 if (!ddl->peer_process) { 991 g_autofree char *msg = g_win32_error_message(GetLastError()); 992 g_debug("Failed to OpenProcess: %s", msg); 993 return false; 994 } 995 996 return true; 997 } 998 #endif 999 1000 static void 1001 dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl) 1002 { 1003 #ifdef WIN32 1004 g_autoptr(GError) err = NULL; 1005 1006 if (!dbus_display_listener_implements(ddl, 1007 "org.qemu.Display1.Listener.Win32.D3d11")) { 1008 return; 1009 } 1010 1011 if (!dbus_display_listener_setup_peer_process(ddl)) { 1012 return; 1013 } 1014 1015 ddl->d3d11_proxy = 1016 qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn, 1017 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 1018 NULL, 1019 "/org/qemu/Display1/Listener", 1020 NULL, 1021 &err); 1022 if (!ddl->d3d11_proxy) { 1023 g_debug("Failed to setup win32 d3d11 proxy: %s", err->message); 1024 return; 1025 } 1026 #endif 1027 } 1028 1029 static void 1030 dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) 1031 { 1032 g_autoptr(GError) err = NULL; 1033 1034 #ifdef WIN32 1035 if (!dbus_display_listener_implements( 1036 ddl, "org.qemu.Display1.Listener.Win32.Map")) { 1037 return; 1038 } 1039 1040 if (!dbus_display_listener_setup_peer_process(ddl)) { 1041 return; 1042 } 1043 1044 ddl->map_proxy = 1045 qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn, 1046 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 1047 NULL, 1048 "/org/qemu/Display1/Listener", 1049 NULL, 1050 &err); 1051 if (!ddl->map_proxy) { 1052 g_debug("Failed to setup win32 map proxy: %s", err->message); 1053 return; 1054 } 1055 1056 ddl->can_share_map = true; 1057 #else /* !WIN32 */ 1058 if (!dbus_display_listener_implements( 1059 ddl, "org.qemu.Display1.Listener.Unix.Map")) { 1060 return; 1061 } 1062 ddl->map_proxy = qemu_dbus_display1_listener_unix_map_proxy_new_sync( 1063 ddl->conn, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, 1064 "/org/qemu/Display1/Listener", NULL, &err); 1065 if (!ddl->map_proxy) { 1066 g_debug("Failed to setup Unix map proxy: %s", err->message); 1067 return; 1068 } 1069 1070 ddl->can_share_map = true; 1071 #endif 1072 } 1073 1074 static GDBusMessage * 1075 dbus_filter(GDBusConnection *connection, 1076 GDBusMessage *message, 1077 gboolean incoming, 1078 gpointer user_data) 1079 { 1080 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data); 1081 guint32 serial, discard_serial; 1082 1083 if (incoming) { 1084 return message; 1085 } 1086 1087 serial = g_dbus_message_get_serial(message); 1088 1089 discard_serial = g_atomic_int_get(&ddl->display_serial_to_discard); 1090 if (serial <= discard_serial) { 1091 const char *member = g_dbus_message_get_member(message); 1092 static const char *const display_messages[] = { 1093 "Scanout", 1094 "Update", 1095 #ifdef CONFIG_GBM 1096 "ScanoutDMABUF", 1097 "UpdateDMABUF", 1098 #endif 1099 "ScanoutMap", 1100 "UpdateMap", 1101 "Disable", 1102 NULL, 1103 }; 1104 1105 if (g_strv_contains(display_messages, member)) { 1106 trace_dbus_filter(serial, discard_serial); 1107 g_object_unref(message); 1108 return NULL; 1109 } 1110 } 1111 1112 discard_serial = g_atomic_int_get(&ddl->cursor_serial_to_discard); 1113 if (serial <= discard_serial) { 1114 const gchar *member = g_dbus_message_get_member(message); 1115 static const char *const cursor_messages[] = { 1116 "CursorDefine", 1117 NULL 1118 }; 1119 1120 if (g_strv_contains(cursor_messages, member)) { 1121 trace_dbus_filter(serial, discard_serial); 1122 g_object_unref(message); 1123 return NULL; 1124 } 1125 } 1126 1127 return message; 1128 } 1129 1130 DBusDisplayListener * 1131 dbus_display_listener_new(const char *bus_name, 1132 GDBusConnection *conn, 1133 DBusDisplayConsole *console) 1134 { 1135 DBusDisplayListener *ddl; 1136 QemuConsole *con; 1137 g_autoptr(GError) err = NULL; 1138 1139 ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL); 1140 ddl->proxy = 1141 qemu_dbus_display1_listener_proxy_new_sync(conn, 1142 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 1143 NULL, 1144 "/org/qemu/Display1/Listener", 1145 NULL, 1146 &err); 1147 if (!ddl->proxy) { 1148 error_report("Failed to setup proxy: %s", err->message); 1149 g_object_unref(conn); 1150 g_object_unref(ddl); 1151 return NULL; 1152 } 1153 1154 ddl->dbus_filter = g_dbus_connection_add_filter(conn, dbus_filter, g_object_ref(ddl), g_object_unref); 1155 ddl->bus_name = g_strdup(bus_name); 1156 ddl->conn = conn; 1157 ddl->console = console; 1158 1159 dbus_display_listener_setup_shared_map(ddl); 1160 trace_dbus_can_share_map(ddl->can_share_map); 1161 dbus_display_listener_setup_d3d11(ddl); 1162 1163 con = qemu_console_lookup_by_index(dbus_display_console_get_index(console)); 1164 assert(con); 1165 ddl->dcl.con = con; 1166 register_displaychangelistener(&ddl->dcl); 1167 1168 return ddl; 1169 } 1170