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