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 #ifdef G_OS_UNIX 30 #include <gio/gunixfdlist.h> 31 #endif 32 #ifdef WIN32 33 #include <d3d11.h> 34 #include <dxgi1_2.h> 35 #endif 36 37 #ifdef CONFIG_OPENGL 38 #include "ui/shader.h" 39 #include "ui/egl-helpers.h" 40 #include "ui/egl-context.h" 41 #include "ui/qemu-pixman.h" 42 #endif 43 #include "trace.h" 44 45 static void dbus_gfx_switch(DisplayChangeListener *dcl, 46 struct DisplaySurface *new_surface); 47 48 enum share_kind { 49 SHARE_KIND_NONE, 50 SHARE_KIND_MAPPED, 51 SHARE_KIND_D3DTEX, 52 }; 53 54 struct _DBusDisplayListener { 55 GObject parent; 56 57 char *bus_name; 58 DBusDisplayConsole *console; 59 GDBusConnection *conn; 60 61 QemuDBusDisplay1Listener *proxy; 62 63 #ifdef CONFIG_PIXMAN 64 /* Keep track of the damage region */ 65 pixman_region32_t gl_damage; 66 #else 67 int gl_damage; 68 #endif 69 70 DisplayChangeListener dcl; 71 DisplaySurface *ds; 72 enum share_kind ds_share; 73 74 bool ds_mapped; 75 bool can_share_map; 76 77 #ifdef WIN32 78 QemuDBusDisplay1ListenerWin32Map *map_proxy; 79 QemuDBusDisplay1ListenerWin32D3d11 *d3d11_proxy; 80 HANDLE peer_process; 81 ID3D11Texture2D *d3d_texture; 82 #ifdef CONFIG_OPENGL 83 egl_fb fb; 84 #endif 85 #endif 86 }; 87 88 G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) 89 90 static void dbus_gfx_update(DisplayChangeListener *dcl, 91 int x, int y, int w, int h); 92 93 #ifdef CONFIG_OPENGL 94 static void dbus_scanout_disable(DisplayChangeListener *dcl) 95 { 96 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 97 98 qemu_dbus_display1_listener_call_disable( 99 ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 100 } 101 102 #ifdef WIN32 103 static bool d3d_texture2d_share(ID3D11Texture2D *d3d_texture, 104 HANDLE *handle, Error **errp) 105 { 106 IDXGIResource1 *dxgiResource = NULL; 107 HRESULT hr; 108 109 hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture, 110 &IID_IDXGIResource1, 111 (void **)&dxgiResource); 112 if (FAILED(hr)) { 113 goto fail; 114 } 115 116 hr = dxgiResource->lpVtbl->CreateSharedHandle( 117 dxgiResource, 118 NULL, 119 DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, 120 NULL, 121 handle 122 ); 123 124 dxgiResource->lpVtbl->Release(dxgiResource); 125 126 if (SUCCEEDED(hr)) { 127 return true; 128 } 129 130 fail: 131 error_setg_win32(errp, GetLastError(), "failed to create shared handle"); 132 return false; 133 } 134 135 static bool d3d_texture2d_acquire0(ID3D11Texture2D *d3d_texture, Error **errp) 136 { 137 IDXGIKeyedMutex *dxgiMutex = NULL; 138 HRESULT hr; 139 140 hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture, 141 &IID_IDXGIKeyedMutex, 142 (void **)&dxgiMutex); 143 if (FAILED(hr)) { 144 goto fail; 145 } 146 147 hr = dxgiMutex->lpVtbl->AcquireSync(dxgiMutex, 0, INFINITE); 148 149 dxgiMutex->lpVtbl->Release(dxgiMutex); 150 151 if (SUCCEEDED(hr)) { 152 return true; 153 } 154 155 fail: 156 error_setg_win32(errp, GetLastError(), "failed to acquire texture mutex"); 157 return false; 158 } 159 160 static bool d3d_texture2d_release0(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->ReleaseSync(dxgiMutex, 0); 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 release texture mutex"); 182 return false; 183 } 184 #endif /* WIN32 */ 185 186 #if defined(CONFIG_GBM) || defined(WIN32) 187 static void dbus_update_gl_cb(GObject *source_object, 188 GAsyncResult *res, 189 gpointer user_data) 190 { 191 g_autoptr(GError) err = NULL; 192 DBusDisplayListener *ddl = user_data; 193 bool success; 194 195 #ifdef CONFIG_GBM 196 success = qemu_dbus_display1_listener_call_update_dmabuf_finish( 197 ddl->proxy, res, &err); 198 #endif 199 200 #ifdef WIN32 201 success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish( 202 ddl->d3d11_proxy, res, &err); 203 d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn); 204 #endif 205 206 if (!success) { 207 error_report("Failed to call update: %s", err->message); 208 } 209 210 graphic_hw_gl_block(ddl->dcl.con, false); 211 g_object_unref(ddl); 212 } 213 #endif 214 215 static void dbus_call_update_gl(DisplayChangeListener *dcl, 216 int x, int y, int w, int h) 217 { 218 #if defined(CONFIG_GBM) || defined(WIN32) 219 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 220 #endif 221 222 trace_dbus_update_gl(x, y, w, h); 223 224 glFlush(); 225 #ifdef CONFIG_GBM 226 graphic_hw_gl_block(ddl->dcl.con, true); 227 qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy, 228 x, y, w, h, 229 G_DBUS_CALL_FLAGS_NONE, 230 DBUS_DEFAULT_TIMEOUT, NULL, 231 dbus_update_gl_cb, 232 g_object_ref(ddl)); 233 #endif 234 235 #ifdef WIN32 236 switch (ddl->ds_share) { 237 case SHARE_KIND_MAPPED: 238 egl_fb_read_rect(ddl->ds, &ddl->fb, x, y, w, h); 239 dbus_gfx_update(dcl, x, y, w, h); 240 break; 241 case SHARE_KIND_D3DTEX: { 242 Error *err = NULL; 243 assert(ddl->d3d_texture); 244 245 graphic_hw_gl_block(ddl->dcl.con, true); 246 if (!d3d_texture2d_release0(ddl->d3d_texture, &err)) { 247 error_report_err(err); 248 return; 249 } 250 qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d( 251 ddl->d3d11_proxy, 252 x, y, w, h, 253 G_DBUS_CALL_FLAGS_NONE, 254 DBUS_DEFAULT_TIMEOUT, NULL, 255 dbus_update_gl_cb, 256 g_object_ref(ddl)); 257 break; 258 } 259 default: 260 g_warn_if_reached(); 261 } 262 #endif 263 } 264 265 #ifdef CONFIG_GBM 266 static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, 267 QemuDmaBuf *dmabuf) 268 { 269 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 270 g_autoptr(GError) err = NULL; 271 g_autoptr(GUnixFDList) fd_list = NULL; 272 273 fd_list = g_unix_fd_list_new(); 274 if (g_unix_fd_list_append(fd_list, dmabuf->fd, &err) != 0) { 275 error_report("Failed to setup dmabuf fdlist: %s", err->message); 276 return; 277 } 278 279 /* FIXME: add missing x/y/w/h support */ 280 qemu_dbus_display1_listener_call_scanout_dmabuf( 281 ddl->proxy, 282 g_variant_new_handle(0), 283 dmabuf->width, 284 dmabuf->height, 285 dmabuf->stride, 286 dmabuf->fourcc, 287 dmabuf->modifier, 288 dmabuf->y0_top, 289 G_DBUS_CALL_FLAGS_NONE, 290 -1, 291 fd_list, 292 NULL, NULL, NULL); 293 } 294 #endif /* GBM */ 295 #endif /* OPENGL */ 296 297 #ifdef WIN32 298 static bool dbus_scanout_map(DBusDisplayListener *ddl) 299 { 300 g_autoptr(GError) err = NULL; 301 BOOL success; 302 HANDLE target_handle; 303 304 if (ddl->ds_share == SHARE_KIND_MAPPED) { 305 return true; 306 } 307 308 if (!ddl->can_share_map || !ddl->ds->handle) { 309 return false; 310 } 311 312 success = DuplicateHandle( 313 GetCurrentProcess(), 314 ddl->ds->handle, 315 ddl->peer_process, 316 &target_handle, 317 FILE_MAP_READ | SECTION_QUERY, 318 FALSE, 0); 319 if (!success) { 320 g_autofree char *msg = g_win32_error_message(GetLastError()); 321 g_debug("Failed to DuplicateHandle: %s", msg); 322 ddl->can_share_map = false; 323 return false; 324 } 325 326 if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync( 327 ddl->map_proxy, 328 GPOINTER_TO_UINT(target_handle), 329 ddl->ds->handle_offset, 330 surface_width(ddl->ds), 331 surface_height(ddl->ds), 332 surface_stride(ddl->ds), 333 surface_format(ddl->ds), 334 G_DBUS_CALL_FLAGS_NONE, 335 DBUS_DEFAULT_TIMEOUT, 336 NULL, 337 &err)) { 338 g_debug("Failed to call ScanoutMap: %s", err->message); 339 ddl->can_share_map = false; 340 return false; 341 } 342 343 ddl->ds_share = SHARE_KIND_MAPPED; 344 345 return true; 346 } 347 348 #ifdef CONFIG_OPENGL 349 static bool 350 dbus_scanout_share_d3d_texture( 351 DBusDisplayListener *ddl, 352 ID3D11Texture2D *tex, 353 bool backing_y_0_top, 354 uint32_t backing_width, 355 uint32_t backing_height, 356 uint32_t x, uint32_t y, 357 uint32_t w, uint32_t h) 358 { 359 Error *err = NULL; 360 BOOL success; 361 HANDLE share_handle, target_handle; 362 363 if (!d3d_texture2d_release0(tex, &err)) { 364 error_report_err(err); 365 return false; 366 } 367 368 if (!d3d_texture2d_share(tex, &share_handle, &err)) { 369 error_report_err(err); 370 return false; 371 } 372 373 success = DuplicateHandle( 374 GetCurrentProcess(), 375 share_handle, 376 ddl->peer_process, 377 &target_handle, 378 0, 379 FALSE, DUPLICATE_SAME_ACCESS); 380 if (!success) { 381 g_autofree char *msg = g_win32_error_message(GetLastError()); 382 g_debug("Failed to DuplicateHandle: %s", msg); 383 CloseHandle(share_handle); 384 return false; 385 } 386 387 qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d( 388 ddl->d3d11_proxy, 389 GPOINTER_TO_INT(target_handle), 390 backing_width, 391 backing_height, 392 backing_y_0_top, 393 x, y, w, h, 394 G_DBUS_CALL_FLAGS_NONE, 395 -1, 396 NULL, NULL, NULL); 397 398 CloseHandle(share_handle); 399 400 if (!d3d_texture2d_acquire0(tex, &err)) { 401 error_report_err(err); 402 return false; 403 } 404 405 ddl->d3d_texture = tex; 406 ddl->ds_share = SHARE_KIND_D3DTEX; 407 408 return true; 409 } 410 #endif /* CONFIG_OPENGL */ 411 #endif /* WIN32 */ 412 413 #ifdef CONFIG_OPENGL 414 static void dbus_scanout_texture(DisplayChangeListener *dcl, 415 uint32_t tex_id, 416 bool backing_y_0_top, 417 uint32_t backing_width, 418 uint32_t backing_height, 419 uint32_t x, uint32_t y, 420 uint32_t w, uint32_t h, 421 void *d3d_tex2d) 422 { 423 trace_dbus_scanout_texture(tex_id, backing_y_0_top, 424 backing_width, backing_height, x, y, w, h); 425 #ifdef CONFIG_GBM 426 QemuDmaBuf dmabuf = { 427 .width = w, 428 .height = h, 429 .y0_top = backing_y_0_top, 430 .x = x, 431 .y = y, 432 .backing_width = backing_width, 433 .backing_height = backing_height, 434 }; 435 436 assert(tex_id); 437 dmabuf.fd = egl_get_fd_for_texture( 438 tex_id, (EGLint *)&dmabuf.stride, 439 (EGLint *)&dmabuf.fourcc, 440 &dmabuf.modifier); 441 if (dmabuf.fd < 0) { 442 error_report("%s: failed to get fd for texture", __func__); 443 return; 444 } 445 446 dbus_scanout_dmabuf(dcl, &dmabuf); 447 close(dmabuf.fd); 448 #endif 449 450 #ifdef WIN32 451 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 452 453 /* there must be a matching gfx_switch before */ 454 assert(surface_width(ddl->ds) == w); 455 assert(surface_height(ddl->ds) == h); 456 457 if (d3d_tex2d) { 458 dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top, 459 backing_width, backing_height, x, y, w, h); 460 } else { 461 dbus_scanout_map(ddl); 462 egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false); 463 } 464 #endif 465 } 466 467 #ifdef CONFIG_GBM 468 static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, 469 QemuDmaBuf *dmabuf, bool have_hot, 470 uint32_t hot_x, uint32_t hot_y) 471 { 472 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 473 DisplaySurface *ds; 474 GVariant *v_data = NULL; 475 egl_fb cursor_fb = EGL_FB_INIT; 476 477 if (!dmabuf) { 478 qemu_dbus_display1_listener_call_mouse_set( 479 ddl->proxy, 0, 0, false, 480 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 481 return; 482 } 483 484 egl_dmabuf_import_texture(dmabuf); 485 if (!dmabuf->texture) { 486 return; 487 } 488 egl_fb_setup_for_tex(&cursor_fb, dmabuf->width, dmabuf->height, 489 dmabuf->texture, false); 490 ds = qemu_create_displaysurface(dmabuf->width, dmabuf->height); 491 egl_fb_read(ds, &cursor_fb); 492 493 v_data = g_variant_new_from_data( 494 G_VARIANT_TYPE("ay"), 495 surface_data(ds), 496 surface_width(ds) * surface_height(ds) * 4, 497 TRUE, 498 (GDestroyNotify)qemu_free_displaysurface, 499 ds); 500 qemu_dbus_display1_listener_call_cursor_define( 501 ddl->proxy, 502 surface_width(ds), 503 surface_height(ds), 504 hot_x, 505 hot_y, 506 v_data, 507 G_DBUS_CALL_FLAGS_NONE, 508 -1, 509 NULL, 510 NULL, 511 NULL); 512 } 513 514 static void dbus_release_dmabuf(DisplayChangeListener *dcl, 515 QemuDmaBuf *dmabuf) 516 { 517 dbus_scanout_disable(dcl); 518 } 519 #endif /* GBM */ 520 521 static void dbus_gl_cursor_position(DisplayChangeListener *dcl, 522 uint32_t pos_x, uint32_t pos_y) 523 { 524 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 525 526 qemu_dbus_display1_listener_call_mouse_set( 527 ddl->proxy, pos_x, pos_y, true, 528 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 529 } 530 531 static void dbus_scanout_update(DisplayChangeListener *dcl, 532 uint32_t x, uint32_t y, 533 uint32_t w, uint32_t h) 534 { 535 dbus_call_update_gl(dcl, x, y, w, h); 536 } 537 538 static void dbus_gl_refresh(DisplayChangeListener *dcl) 539 { 540 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 541 542 graphic_hw_update(dcl->con); 543 544 if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) { 545 return; 546 } 547 548 #ifdef CONFIG_PIXMAN 549 int n_rects = pixman_region32_n_rects(&ddl->gl_damage); 550 551 for (int i = 0; i < n_rects; i++) { 552 pixman_box32_t *box; 553 box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i; 554 /* TODO: Add a UpdateList call to send multiple updates at once */ 555 dbus_call_update_gl(dcl, box->x1, box->y1, 556 box->x2 - box->x1, box->y2 - box->y1); 557 } 558 pixman_region32_clear(&ddl->gl_damage); 559 #else 560 if (ddl->gl_damage) { 561 dbus_call_update_gl(dcl, 0, 0, 562 surface_width(ddl->ds), surface_height(ddl->ds)); 563 ddl->gl_damage = 0; 564 } 565 #endif 566 } 567 #endif /* OPENGL */ 568 569 static void dbus_refresh(DisplayChangeListener *dcl) 570 { 571 graphic_hw_update(dcl->con); 572 } 573 574 #ifdef CONFIG_OPENGL 575 static void dbus_gl_gfx_update(DisplayChangeListener *dcl, 576 int x, int y, int w, int h) 577 { 578 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 579 580 #ifdef CONFIG_PIXMAN 581 pixman_region32_t rect_region; 582 pixman_region32_init_rect(&rect_region, x, y, w, h); 583 pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region); 584 pixman_region32_fini(&rect_region); 585 #else 586 ddl->gl_damage++; 587 #endif 588 } 589 #endif 590 591 static void dbus_gfx_update_sub(DBusDisplayListener *ddl, 592 int x, int y, int w, int h) 593 { 594 pixman_image_t *img; 595 size_t stride; 596 GVariant *v_data; 597 598 /* make a copy, since gvariant only handles linear data */ 599 stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); 600 img = pixman_image_create_bits(surface_format(ddl->ds), 601 w, h, NULL, stride); 602 #ifdef CONFIG_PIXMAN 603 pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img, 604 x, y, 0, 0, 0, 0, w, h); 605 #else 606 { 607 uint8_t *src = (uint8_t *)pixman_image_get_data(ddl->ds->image); 608 uint8_t *dst = (uint8_t *)pixman_image_get_data(img); 609 int bp = PIXMAN_FORMAT_BPP(surface_format(ddl->ds)) / 8; 610 int hh; 611 612 for (hh = 0; hh < h; hh++) { 613 memcpy(&dst[stride * hh], 614 &src[surface_stride(ddl->ds) * (hh + y) + x * bp], 615 stride); 616 } 617 } 618 #endif 619 v_data = g_variant_new_from_data( 620 G_VARIANT_TYPE("ay"), 621 pixman_image_get_data(img), 622 pixman_image_get_stride(img) * h, 623 TRUE, 624 (GDestroyNotify)pixman_image_unref, 625 img); 626 qemu_dbus_display1_listener_call_update(ddl->proxy, 627 x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img), 628 v_data, 629 G_DBUS_CALL_FLAGS_NONE, 630 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 631 } 632 633 static void dbus_gfx_update(DisplayChangeListener *dcl, 634 int x, int y, int w, int h) 635 { 636 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 637 GVariant *v_data; 638 639 assert(ddl->ds); 640 641 trace_dbus_update(x, y, w, h); 642 643 #ifdef WIN32 644 if (dbus_scanout_map(ddl)) { 645 qemu_dbus_display1_listener_win32_map_call_update_map( 646 ddl->map_proxy, 647 x, y, w, h, 648 G_DBUS_CALL_FLAGS_NONE, 649 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 650 return; 651 } 652 #endif 653 654 if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { 655 v_data = g_variant_new_from_data( 656 G_VARIANT_TYPE("ay"), 657 surface_data(ddl->ds), 658 surface_stride(ddl->ds) * surface_height(ddl->ds), 659 TRUE, 660 (GDestroyNotify)pixman_image_unref, 661 pixman_image_ref(ddl->ds->image)); 662 qemu_dbus_display1_listener_call_scanout( 663 ddl->proxy, 664 surface_width(ddl->ds), 665 surface_height(ddl->ds), 666 surface_stride(ddl->ds), 667 surface_format(ddl->ds), 668 v_data, 669 G_DBUS_CALL_FLAGS_NONE, 670 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 671 return; 672 } 673 674 dbus_gfx_update_sub(ddl, x, y, w, h); 675 } 676 677 #ifdef CONFIG_OPENGL 678 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, 679 struct DisplaySurface *new_surface) 680 { 681 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 682 683 trace_dbus_gl_gfx_switch(new_surface); 684 685 ddl->ds = new_surface; 686 ddl->ds_share = SHARE_KIND_NONE; 687 if (ddl->ds) { 688 int width = surface_width(ddl->ds); 689 int height = surface_height(ddl->ds); 690 691 /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ 692 dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, 693 width, height, 0, 0, width, height, NULL); 694 } 695 } 696 #endif 697 698 static void dbus_gfx_switch(DisplayChangeListener *dcl, 699 struct DisplaySurface *new_surface) 700 { 701 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 702 703 ddl->ds = new_surface; 704 ddl->ds_share = SHARE_KIND_NONE; 705 } 706 707 static void dbus_mouse_set(DisplayChangeListener *dcl, 708 int x, int y, int on) 709 { 710 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 711 712 qemu_dbus_display1_listener_call_mouse_set( 713 ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 714 } 715 716 static void dbus_cursor_define(DisplayChangeListener *dcl, 717 QEMUCursor *c) 718 { 719 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 720 GVariant *v_data = NULL; 721 722 v_data = g_variant_new_from_data( 723 G_VARIANT_TYPE("ay"), 724 c->data, 725 c->width * c->height * 4, 726 TRUE, 727 (GDestroyNotify)cursor_unref, 728 cursor_ref(c)); 729 730 qemu_dbus_display1_listener_call_cursor_define( 731 ddl->proxy, 732 c->width, 733 c->height, 734 c->hot_x, 735 c->hot_y, 736 v_data, 737 G_DBUS_CALL_FLAGS_NONE, 738 -1, 739 NULL, 740 NULL, 741 NULL); 742 } 743 744 #ifdef CONFIG_OPENGL 745 const DisplayChangeListenerOps dbus_gl_dcl_ops = { 746 .dpy_name = "dbus-gl", 747 .dpy_gfx_update = dbus_gl_gfx_update, 748 .dpy_gfx_switch = dbus_gl_gfx_switch, 749 .dpy_gfx_check_format = console_gl_check_format, 750 .dpy_refresh = dbus_gl_refresh, 751 .dpy_mouse_set = dbus_mouse_set, 752 .dpy_cursor_define = dbus_cursor_define, 753 754 .dpy_gl_scanout_disable = dbus_scanout_disable, 755 .dpy_gl_scanout_texture = dbus_scanout_texture, 756 #ifdef CONFIG_GBM 757 .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf, 758 .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf, 759 .dpy_gl_release_dmabuf = dbus_release_dmabuf, 760 #endif 761 .dpy_gl_cursor_position = dbus_gl_cursor_position, 762 .dpy_gl_update = dbus_scanout_update, 763 }; 764 #endif 765 766 const DisplayChangeListenerOps dbus_dcl_ops = { 767 .dpy_name = "dbus", 768 .dpy_gfx_update = dbus_gfx_update, 769 .dpy_gfx_switch = dbus_gfx_switch, 770 .dpy_refresh = dbus_refresh, 771 .dpy_mouse_set = dbus_mouse_set, 772 .dpy_cursor_define = dbus_cursor_define, 773 }; 774 775 static void 776 dbus_display_listener_dispose(GObject *object) 777 { 778 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 779 780 unregister_displaychangelistener(&ddl->dcl); 781 g_clear_object(&ddl->conn); 782 g_clear_pointer(&ddl->bus_name, g_free); 783 g_clear_object(&ddl->proxy); 784 #ifdef WIN32 785 g_clear_object(&ddl->map_proxy); 786 g_clear_object(&ddl->d3d11_proxy); 787 g_clear_pointer(&ddl->peer_process, CloseHandle); 788 #ifdef CONFIG_PIXMAN 789 pixman_region32_fini(&ddl->gl_damage); 790 #endif 791 #ifdef CONFIG_OPENGL 792 egl_fb_destroy(&ddl->fb); 793 #endif 794 #endif 795 796 G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); 797 } 798 799 static void 800 dbus_display_listener_constructed(GObject *object) 801 { 802 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 803 804 ddl->dcl.ops = &dbus_dcl_ops; 805 #ifdef CONFIG_OPENGL 806 if (display_opengl) { 807 ddl->dcl.ops = &dbus_gl_dcl_ops; 808 } 809 #endif 810 811 G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object); 812 } 813 814 static void 815 dbus_display_listener_class_init(DBusDisplayListenerClass *klass) 816 { 817 GObjectClass *object_class = G_OBJECT_CLASS(klass); 818 819 object_class->dispose = dbus_display_listener_dispose; 820 object_class->constructed = dbus_display_listener_constructed; 821 } 822 823 static void 824 dbus_display_listener_init(DBusDisplayListener *ddl) 825 { 826 #ifdef CONFIG_PIXMAN 827 pixman_region32_init(&ddl->gl_damage); 828 #endif 829 } 830 831 const char * 832 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl) 833 { 834 return ddl->bus_name ?: "p2p"; 835 } 836 837 DBusDisplayConsole * 838 dbus_display_listener_get_console(DBusDisplayListener *ddl) 839 { 840 return ddl->console; 841 } 842 843 #ifdef WIN32 844 static bool 845 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface) 846 { 847 QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy); 848 bool implements; 849 850 implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface); 851 if (!implements) { 852 g_debug("Display listener does not implement: `%s`", iface); 853 } 854 855 return implements; 856 } 857 858 static bool 859 dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl) 860 { 861 g_autoptr(GError) err = NULL; 862 GDBusConnection *conn; 863 GIOStream *stream; 864 GSocket *sock; 865 g_autoptr(GCredentials) creds = NULL; 866 DWORD *pid; 867 868 if (ddl->peer_process) { 869 return true; 870 } 871 872 conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)); 873 stream = g_dbus_connection_get_stream(conn); 874 875 if (!G_IS_UNIX_CONNECTION(stream)) { 876 return false; 877 } 878 879 sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream)); 880 creds = g_socket_get_credentials(sock, &err); 881 882 if (!creds) { 883 g_debug("Failed to get peer credentials: %s", err->message); 884 return false; 885 } 886 887 pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID); 888 889 if (pid == NULL) { 890 g_debug("Failed to get peer PID"); 891 return false; 892 } 893 894 ddl->peer_process = OpenProcess( 895 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, 896 false, *pid); 897 898 if (!ddl->peer_process) { 899 g_autofree char *msg = g_win32_error_message(GetLastError()); 900 g_debug("Failed to OpenProcess: %s", msg); 901 return false; 902 } 903 904 return true; 905 } 906 #endif 907 908 static void 909 dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl) 910 { 911 #ifdef WIN32 912 g_autoptr(GError) err = NULL; 913 914 if (!dbus_display_listener_implements(ddl, 915 "org.qemu.Display1.Listener.Win32.D3d11")) { 916 return; 917 } 918 919 if (!dbus_display_listener_setup_peer_process(ddl)) { 920 return; 921 } 922 923 ddl->d3d11_proxy = 924 qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn, 925 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 926 NULL, 927 "/org/qemu/Display1/Listener", 928 NULL, 929 &err); 930 if (!ddl->d3d11_proxy) { 931 g_debug("Failed to setup win32 d3d11 proxy: %s", err->message); 932 return; 933 } 934 #endif 935 } 936 937 static void 938 dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) 939 { 940 #ifdef WIN32 941 g_autoptr(GError) err = NULL; 942 943 if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) { 944 return; 945 } 946 947 if (!dbus_display_listener_setup_peer_process(ddl)) { 948 return; 949 } 950 951 ddl->map_proxy = 952 qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn, 953 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 954 NULL, 955 "/org/qemu/Display1/Listener", 956 NULL, 957 &err); 958 if (!ddl->map_proxy) { 959 g_debug("Failed to setup win32 map proxy: %s", err->message); 960 return; 961 } 962 963 ddl->can_share_map = true; 964 #endif 965 } 966 967 DBusDisplayListener * 968 dbus_display_listener_new(const char *bus_name, 969 GDBusConnection *conn, 970 DBusDisplayConsole *console) 971 { 972 DBusDisplayListener *ddl; 973 QemuConsole *con; 974 g_autoptr(GError) err = NULL; 975 976 ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL); 977 ddl->proxy = 978 qemu_dbus_display1_listener_proxy_new_sync(conn, 979 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 980 NULL, 981 "/org/qemu/Display1/Listener", 982 NULL, 983 &err); 984 if (!ddl->proxy) { 985 error_report("Failed to setup proxy: %s", err->message); 986 g_object_unref(conn); 987 g_object_unref(ddl); 988 return NULL; 989 } 990 991 ddl->bus_name = g_strdup(bus_name); 992 ddl->conn = conn; 993 ddl->console = console; 994 995 dbus_display_listener_setup_shared_map(ddl); 996 dbus_display_listener_setup_d3d11(ddl); 997 998 con = qemu_console_lookup_by_index(dbus_display_console_get_index(console)); 999 assert(con); 1000 ddl->dcl.con = con; 1001 register_displaychangelistener(&ddl->dcl); 1002 1003 return ddl; 1004 } 1005