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