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