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