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