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 CONFIG_OPENGL 30 #include <pixman.h> 31 #endif 32 #ifdef G_OS_UNIX 33 #include <gio/gunixfdlist.h> 34 #endif 35 #ifdef WIN32 36 #include <d3d11.h> 37 #include <dxgi1_2.h> 38 #endif 39 40 #ifdef CONFIG_OPENGL 41 #include "ui/shader.h" 42 #include "ui/egl-helpers.h" 43 #include "ui/egl-context.h" 44 #endif 45 #include "trace.h" 46 47 static void dbus_gfx_switch(DisplayChangeListener *dcl, 48 struct DisplaySurface *new_surface); 49 50 enum share_kind { 51 SHARE_KIND_NONE, 52 SHARE_KIND_MAPPED, 53 SHARE_KIND_D3DTEX, 54 }; 55 56 struct _DBusDisplayListener { 57 GObject parent; 58 59 char *bus_name; 60 DBusDisplayConsole *console; 61 GDBusConnection *conn; 62 63 QemuDBusDisplay1Listener *proxy; 64 65 #ifdef CONFIG_OPENGL 66 /* Keep track of the damage region */ 67 pixman_region32_t 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 int n_rects = pixman_region32_n_rects(&ddl->gl_damage); 549 550 for (int i = 0; i < n_rects; i++) { 551 pixman_box32_t *box; 552 box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i; 553 /* TODO: Add a UpdateList call to send multiple updates at once */ 554 dbus_call_update_gl(dcl, box->x1, box->y1, 555 box->x2 - box->x1, box->y2 - box->y1); 556 } 557 pixman_region32_clear(&ddl->gl_damage); 558 } 559 #endif /* OPENGL */ 560 561 static void dbus_refresh(DisplayChangeListener *dcl) 562 { 563 graphic_hw_update(dcl->con); 564 } 565 566 #ifdef CONFIG_OPENGL 567 static void dbus_gl_gfx_update(DisplayChangeListener *dcl, 568 int x, int y, int w, int h) 569 { 570 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 571 572 pixman_region32_t rect_region; 573 pixman_region32_init_rect(&rect_region, x, y, w, h); 574 pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region); 575 pixman_region32_fini(&rect_region); 576 } 577 #endif 578 579 static void dbus_gfx_update(DisplayChangeListener *dcl, 580 int x, int y, int w, int h) 581 { 582 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 583 pixman_image_t *img; 584 GVariant *v_data; 585 size_t stride; 586 587 assert(ddl->ds); 588 589 trace_dbus_update(x, y, w, h); 590 591 #ifdef WIN32 592 if (dbus_scanout_map(ddl)) { 593 qemu_dbus_display1_listener_win32_map_call_update_map( 594 ddl->map_proxy, 595 x, y, w, h, 596 G_DBUS_CALL_FLAGS_NONE, 597 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 598 return; 599 } 600 #endif 601 602 if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { 603 v_data = g_variant_new_from_data( 604 G_VARIANT_TYPE("ay"), 605 surface_data(ddl->ds), 606 surface_stride(ddl->ds) * surface_height(ddl->ds), 607 TRUE, 608 (GDestroyNotify)pixman_image_unref, 609 pixman_image_ref(ddl->ds->image)); 610 qemu_dbus_display1_listener_call_scanout( 611 ddl->proxy, 612 surface_width(ddl->ds), 613 surface_height(ddl->ds), 614 surface_stride(ddl->ds), 615 surface_format(ddl->ds), 616 v_data, 617 G_DBUS_CALL_FLAGS_NONE, 618 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 619 return; 620 } 621 622 /* make a copy, since gvariant only handles linear data */ 623 stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); 624 img = pixman_image_create_bits(surface_format(ddl->ds), 625 w, h, NULL, stride); 626 pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img, 627 x, y, 0, 0, 0, 0, w, h); 628 629 v_data = g_variant_new_from_data( 630 G_VARIANT_TYPE("ay"), 631 pixman_image_get_data(img), 632 pixman_image_get_stride(img) * h, 633 TRUE, 634 (GDestroyNotify)pixman_image_unref, 635 img); 636 qemu_dbus_display1_listener_call_update(ddl->proxy, 637 x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img), 638 v_data, 639 G_DBUS_CALL_FLAGS_NONE, 640 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 641 } 642 643 #ifdef CONFIG_OPENGL 644 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, 645 struct DisplaySurface *new_surface) 646 { 647 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 648 649 trace_dbus_gl_gfx_switch(new_surface); 650 651 ddl->ds = new_surface; 652 ddl->ds_share = SHARE_KIND_NONE; 653 if (ddl->ds) { 654 int width = surface_width(ddl->ds); 655 int height = surface_height(ddl->ds); 656 657 /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ 658 dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, 659 width, height, 0, 0, width, height, NULL); 660 } 661 } 662 #endif 663 664 static void dbus_gfx_switch(DisplayChangeListener *dcl, 665 struct DisplaySurface *new_surface) 666 { 667 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 668 669 ddl->ds = new_surface; 670 ddl->ds_share = SHARE_KIND_NONE; 671 } 672 673 static void dbus_mouse_set(DisplayChangeListener *dcl, 674 int x, int y, int on) 675 { 676 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 677 678 qemu_dbus_display1_listener_call_mouse_set( 679 ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 680 } 681 682 static void dbus_cursor_define(DisplayChangeListener *dcl, 683 QEMUCursor *c) 684 { 685 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 686 GVariant *v_data = NULL; 687 688 v_data = g_variant_new_from_data( 689 G_VARIANT_TYPE("ay"), 690 c->data, 691 c->width * c->height * 4, 692 TRUE, 693 (GDestroyNotify)cursor_unref, 694 cursor_ref(c)); 695 696 qemu_dbus_display1_listener_call_cursor_define( 697 ddl->proxy, 698 c->width, 699 c->height, 700 c->hot_x, 701 c->hot_y, 702 v_data, 703 G_DBUS_CALL_FLAGS_NONE, 704 -1, 705 NULL, 706 NULL, 707 NULL); 708 } 709 710 #ifdef CONFIG_OPENGL 711 const DisplayChangeListenerOps dbus_gl_dcl_ops = { 712 .dpy_name = "dbus-gl", 713 .dpy_gfx_update = dbus_gl_gfx_update, 714 .dpy_gfx_switch = dbus_gl_gfx_switch, 715 .dpy_gfx_check_format = console_gl_check_format, 716 .dpy_refresh = dbus_gl_refresh, 717 .dpy_mouse_set = dbus_mouse_set, 718 .dpy_cursor_define = dbus_cursor_define, 719 720 .dpy_gl_scanout_disable = dbus_scanout_disable, 721 .dpy_gl_scanout_texture = dbus_scanout_texture, 722 #ifdef CONFIG_GBM 723 .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf, 724 .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf, 725 .dpy_gl_release_dmabuf = dbus_release_dmabuf, 726 #endif 727 .dpy_gl_cursor_position = dbus_gl_cursor_position, 728 .dpy_gl_update = dbus_scanout_update, 729 }; 730 #endif 731 732 const DisplayChangeListenerOps dbus_dcl_ops = { 733 .dpy_name = "dbus", 734 .dpy_gfx_update = dbus_gfx_update, 735 .dpy_gfx_switch = dbus_gfx_switch, 736 .dpy_refresh = dbus_refresh, 737 .dpy_mouse_set = dbus_mouse_set, 738 .dpy_cursor_define = dbus_cursor_define, 739 }; 740 741 static void 742 dbus_display_listener_dispose(GObject *object) 743 { 744 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 745 746 unregister_displaychangelistener(&ddl->dcl); 747 g_clear_object(&ddl->conn); 748 g_clear_pointer(&ddl->bus_name, g_free); 749 g_clear_object(&ddl->proxy); 750 #ifdef WIN32 751 g_clear_object(&ddl->map_proxy); 752 g_clear_object(&ddl->d3d11_proxy); 753 g_clear_pointer(&ddl->peer_process, CloseHandle); 754 #ifdef CONFIG_OPENGL 755 pixman_region32_fini(&ddl->gl_damage); 756 egl_fb_destroy(&ddl->fb); 757 #endif 758 #endif 759 760 G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); 761 } 762 763 static void 764 dbus_display_listener_constructed(GObject *object) 765 { 766 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 767 768 ddl->dcl.ops = &dbus_dcl_ops; 769 #ifdef CONFIG_OPENGL 770 if (display_opengl) { 771 ddl->dcl.ops = &dbus_gl_dcl_ops; 772 } 773 #endif 774 775 G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object); 776 } 777 778 static void 779 dbus_display_listener_class_init(DBusDisplayListenerClass *klass) 780 { 781 GObjectClass *object_class = G_OBJECT_CLASS(klass); 782 783 object_class->dispose = dbus_display_listener_dispose; 784 object_class->constructed = dbus_display_listener_constructed; 785 } 786 787 static void 788 dbus_display_listener_init(DBusDisplayListener *ddl) 789 { 790 #ifdef CONFIG_OPENGL 791 pixman_region32_init(&ddl->gl_damage); 792 #endif 793 } 794 795 const char * 796 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl) 797 { 798 return ddl->bus_name ?: "p2p"; 799 } 800 801 DBusDisplayConsole * 802 dbus_display_listener_get_console(DBusDisplayListener *ddl) 803 { 804 return ddl->console; 805 } 806 807 #ifdef WIN32 808 static bool 809 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface) 810 { 811 QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy); 812 bool implements; 813 814 implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface); 815 if (!implements) { 816 g_debug("Display listener does not implement: `%s`", iface); 817 } 818 819 return implements; 820 } 821 822 static bool 823 dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl) 824 { 825 g_autoptr(GError) err = NULL; 826 GDBusConnection *conn; 827 GIOStream *stream; 828 GSocket *sock; 829 g_autoptr(GCredentials) creds = NULL; 830 DWORD *pid; 831 832 if (ddl->peer_process) { 833 return true; 834 } 835 836 conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)); 837 stream = g_dbus_connection_get_stream(conn); 838 839 if (!G_IS_UNIX_CONNECTION(stream)) { 840 return false; 841 } 842 843 sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream)); 844 creds = g_socket_get_credentials(sock, &err); 845 846 if (!creds) { 847 g_debug("Failed to get peer credentials: %s", err->message); 848 return false; 849 } 850 851 pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID); 852 853 if (pid == NULL) { 854 g_debug("Failed to get peer PID"); 855 return false; 856 } 857 858 ddl->peer_process = OpenProcess( 859 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, 860 false, *pid); 861 862 if (!ddl->peer_process) { 863 g_autofree char *msg = g_win32_error_message(GetLastError()); 864 g_debug("Failed to OpenProcess: %s", msg); 865 return false; 866 } 867 868 return true; 869 } 870 #endif 871 872 static void 873 dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl) 874 { 875 #ifdef WIN32 876 g_autoptr(GError) err = NULL; 877 878 if (!dbus_display_listener_implements(ddl, 879 "org.qemu.Display1.Listener.Win32.D3d11")) { 880 return; 881 } 882 883 if (!dbus_display_listener_setup_peer_process(ddl)) { 884 return; 885 } 886 887 ddl->d3d11_proxy = 888 qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn, 889 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 890 NULL, 891 "/org/qemu/Display1/Listener", 892 NULL, 893 &err); 894 if (!ddl->d3d11_proxy) { 895 g_debug("Failed to setup win32 d3d11 proxy: %s", err->message); 896 return; 897 } 898 #endif 899 } 900 901 static void 902 dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) 903 { 904 #ifdef WIN32 905 g_autoptr(GError) err = NULL; 906 907 if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) { 908 return; 909 } 910 911 if (!dbus_display_listener_setup_peer_process(ddl)) { 912 return; 913 } 914 915 ddl->map_proxy = 916 qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn, 917 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 918 NULL, 919 "/org/qemu/Display1/Listener", 920 NULL, 921 &err); 922 if (!ddl->map_proxy) { 923 g_debug("Failed to setup win32 map proxy: %s", err->message); 924 return; 925 } 926 927 ddl->can_share_map = true; 928 #endif 929 } 930 931 DBusDisplayListener * 932 dbus_display_listener_new(const char *bus_name, 933 GDBusConnection *conn, 934 DBusDisplayConsole *console) 935 { 936 DBusDisplayListener *ddl; 937 QemuConsole *con; 938 g_autoptr(GError) err = NULL; 939 940 ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL); 941 ddl->proxy = 942 qemu_dbus_display1_listener_proxy_new_sync(conn, 943 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 944 NULL, 945 "/org/qemu/Display1/Listener", 946 NULL, 947 &err); 948 if (!ddl->proxy) { 949 error_report("Failed to setup proxy: %s", err->message); 950 g_object_unref(conn); 951 g_object_unref(ddl); 952 return NULL; 953 } 954 955 ddl->bus_name = g_strdup(bus_name); 956 ddl->conn = conn; 957 ddl->console = console; 958 959 dbus_display_listener_setup_shared_map(ddl); 960 dbus_display_listener_setup_d3d11(ddl); 961 962 con = qemu_console_lookup_by_index(dbus_display_console_get_index(console)); 963 assert(con); 964 ddl->dcl.con = con; 965 register_displaychangelistener(&ddl->dcl); 966 967 return ddl; 968 } 969