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