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