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