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 g_autoptr(QemuDmaBuf) dmabuf = NULL; 446 int fd; 447 uint32_t stride, fourcc; 448 uint64_t modifier; 449 450 assert(tex_id); 451 fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc, 452 &modifier); 453 if (fd < 0) { 454 error_report("%s: failed to get fd for texture", __func__); 455 return; 456 } 457 dmabuf = qemu_dmabuf_new(w, h, stride, x, y, backing_width, 458 backing_height, fourcc, modifier, fd, 459 false, backing_y_0_top); 460 461 dbus_scanout_dmabuf(dcl, dmabuf); 462 qemu_dmabuf_close(dmabuf); 463 #endif 464 465 #ifdef WIN32 466 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 467 468 /* there must be a matching gfx_switch before */ 469 assert(surface_width(ddl->ds) == w); 470 assert(surface_height(ddl->ds) == h); 471 472 if (d3d_tex2d) { 473 dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top, 474 backing_width, backing_height, x, y, w, h); 475 } else { 476 dbus_scanout_map(ddl); 477 egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false); 478 } 479 #endif 480 } 481 482 #ifdef CONFIG_GBM 483 static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, 484 QemuDmaBuf *dmabuf, bool have_hot, 485 uint32_t hot_x, uint32_t hot_y) 486 { 487 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 488 DisplaySurface *ds; 489 GVariant *v_data = NULL; 490 egl_fb cursor_fb = EGL_FB_INIT; 491 uint32_t width, height, texture; 492 493 if (!dmabuf) { 494 qemu_dbus_display1_listener_call_mouse_set( 495 ddl->proxy, 0, 0, false, 496 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 497 return; 498 } 499 500 egl_dmabuf_import_texture(dmabuf); 501 texture = qemu_dmabuf_get_texture(dmabuf); 502 if (!texture) { 503 return; 504 } 505 506 width = qemu_dmabuf_get_width(dmabuf); 507 height = qemu_dmabuf_get_height(dmabuf); 508 509 egl_fb_setup_for_tex(&cursor_fb, width, height, texture, false); 510 ds = qemu_create_displaysurface(width, height); 511 egl_fb_read(ds, &cursor_fb); 512 513 v_data = g_variant_new_from_data( 514 G_VARIANT_TYPE("ay"), 515 surface_data(ds), 516 surface_width(ds) * surface_height(ds) * 4, 517 TRUE, 518 (GDestroyNotify)qemu_free_displaysurface, 519 ds); 520 qemu_dbus_display1_listener_call_cursor_define( 521 ddl->proxy, 522 surface_width(ds), 523 surface_height(ds), 524 hot_x, 525 hot_y, 526 v_data, 527 G_DBUS_CALL_FLAGS_NONE, 528 -1, 529 NULL, 530 NULL, 531 NULL); 532 } 533 534 static void dbus_release_dmabuf(DisplayChangeListener *dcl, 535 QemuDmaBuf *dmabuf) 536 { 537 dbus_scanout_disable(dcl); 538 } 539 #endif /* GBM */ 540 541 static void dbus_gl_cursor_position(DisplayChangeListener *dcl, 542 uint32_t pos_x, uint32_t pos_y) 543 { 544 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 545 546 qemu_dbus_display1_listener_call_mouse_set( 547 ddl->proxy, pos_x, pos_y, true, 548 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 549 } 550 551 static void dbus_scanout_update(DisplayChangeListener *dcl, 552 uint32_t x, uint32_t y, 553 uint32_t w, uint32_t h) 554 { 555 dbus_call_update_gl(dcl, x, y, w, h); 556 } 557 558 static void dbus_gl_refresh(DisplayChangeListener *dcl) 559 { 560 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 561 562 graphic_hw_update(dcl->con); 563 564 if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) { 565 return; 566 } 567 568 #ifdef CONFIG_PIXMAN 569 int n_rects = pixman_region32_n_rects(&ddl->gl_damage); 570 571 for (int i = 0; i < n_rects; i++) { 572 pixman_box32_t *box; 573 box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i; 574 /* TODO: Add a UpdateList call to send multiple updates at once */ 575 dbus_call_update_gl(dcl, box->x1, box->y1, 576 box->x2 - box->x1, box->y2 - box->y1); 577 } 578 pixman_region32_clear(&ddl->gl_damage); 579 #else 580 if (ddl->gl_damage) { 581 dbus_call_update_gl(dcl, 0, 0, 582 surface_width(ddl->ds), surface_height(ddl->ds)); 583 ddl->gl_damage = 0; 584 } 585 #endif 586 } 587 #endif /* OPENGL */ 588 589 static void dbus_refresh(DisplayChangeListener *dcl) 590 { 591 graphic_hw_update(dcl->con); 592 } 593 594 #ifdef CONFIG_OPENGL 595 static void dbus_gl_gfx_update(DisplayChangeListener *dcl, 596 int x, int y, int w, int h) 597 { 598 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 599 600 #ifdef CONFIG_PIXMAN 601 pixman_region32_t rect_region; 602 pixman_region32_init_rect(&rect_region, x, y, w, h); 603 pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region); 604 pixman_region32_fini(&rect_region); 605 #else 606 ddl->gl_damage++; 607 #endif 608 } 609 #endif 610 611 static void dbus_gfx_update_sub(DBusDisplayListener *ddl, 612 int x, int y, int w, int h) 613 { 614 pixman_image_t *img; 615 size_t stride; 616 GVariant *v_data; 617 618 /* make a copy, since gvariant only handles linear data */ 619 stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); 620 img = pixman_image_create_bits(surface_format(ddl->ds), 621 w, h, NULL, stride); 622 #ifdef CONFIG_PIXMAN 623 pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img, 624 x, y, 0, 0, 0, 0, w, h); 625 #else 626 { 627 uint8_t *src = (uint8_t *)pixman_image_get_data(ddl->ds->image); 628 uint8_t *dst = (uint8_t *)pixman_image_get_data(img); 629 int bp = PIXMAN_FORMAT_BPP(surface_format(ddl->ds)) / 8; 630 int hh; 631 632 for (hh = 0; hh < h; hh++) { 633 memcpy(&dst[stride * hh], 634 &src[surface_stride(ddl->ds) * (hh + y) + x * bp], 635 stride); 636 } 637 } 638 #endif 639 v_data = g_variant_new_from_data( 640 G_VARIANT_TYPE("ay"), 641 pixman_image_get_data(img), 642 pixman_image_get_stride(img) * h, 643 TRUE, 644 (GDestroyNotify)pixman_image_unref, 645 img); 646 qemu_dbus_display1_listener_call_update(ddl->proxy, 647 x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img), 648 v_data, 649 G_DBUS_CALL_FLAGS_NONE, 650 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 651 } 652 653 static void ddl_scanout(DBusDisplayListener *ddl) 654 { 655 GVariant *v_data; 656 657 v_data = g_variant_new_from_data( 658 G_VARIANT_TYPE("ay"), surface_data(ddl->ds), 659 surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE, 660 (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image)); 661 662 ddl_discard_pending_messages(ddl); 663 664 qemu_dbus_display1_listener_call_scanout( 665 ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds), 666 surface_stride(ddl->ds), surface_format(ddl->ds), v_data, 667 G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL, 668 g_object_ref(ddl)); 669 } 670 671 static void dbus_gfx_update(DisplayChangeListener *dcl, 672 int x, int y, int w, int h) 673 { 674 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 675 676 assert(ddl->ds); 677 678 trace_dbus_update(x, y, w, h); 679 680 #ifdef WIN32 681 if (dbus_scanout_map(ddl)) { 682 qemu_dbus_display1_listener_win32_map_call_update_map( 683 ddl->map_proxy, 684 x, y, w, h, 685 G_DBUS_CALL_FLAGS_NONE, 686 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 687 return; 688 } 689 #endif 690 691 if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { 692 return ddl_scanout(ddl); 693 } 694 695 dbus_gfx_update_sub(ddl, x, y, w, h); 696 } 697 698 #ifdef CONFIG_OPENGL 699 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, 700 struct DisplaySurface *new_surface) 701 { 702 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 703 704 trace_dbus_gl_gfx_switch(new_surface); 705 706 ddl->ds = new_surface; 707 ddl->ds_share = SHARE_KIND_NONE; 708 if (ddl->ds) { 709 int width = surface_width(ddl->ds); 710 int height = surface_height(ddl->ds); 711 712 /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ 713 dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, 714 width, height, 0, 0, width, height, NULL); 715 } 716 } 717 #endif 718 719 static void dbus_gfx_switch(DisplayChangeListener *dcl, 720 struct DisplaySurface *new_surface) 721 { 722 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 723 724 ddl->ds = new_surface; 725 ddl->ds_share = SHARE_KIND_NONE; 726 } 727 728 static void dbus_mouse_set(DisplayChangeListener *dcl, 729 int x, int y, bool on) 730 { 731 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 732 733 qemu_dbus_display1_listener_call_mouse_set( 734 ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 735 } 736 737 static void dbus_cursor_define(DisplayChangeListener *dcl, 738 QEMUCursor *c) 739 { 740 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 741 GVariant *v_data = NULL; 742 743 v_data = g_variant_new_from_data( 744 G_VARIANT_TYPE("ay"), 745 c->data, 746 c->width * c->height * 4, 747 TRUE, 748 (GDestroyNotify)cursor_unref, 749 cursor_ref(c)); 750 751 qemu_dbus_display1_listener_call_cursor_define( 752 ddl->proxy, 753 c->width, 754 c->height, 755 c->hot_x, 756 c->hot_y, 757 v_data, 758 G_DBUS_CALL_FLAGS_NONE, 759 -1, 760 NULL, 761 NULL, 762 NULL); 763 } 764 765 #ifdef CONFIG_OPENGL 766 const DisplayChangeListenerOps dbus_gl_dcl_ops = { 767 .dpy_name = "dbus-gl", 768 .dpy_gfx_update = dbus_gl_gfx_update, 769 .dpy_gfx_switch = dbus_gl_gfx_switch, 770 .dpy_gfx_check_format = console_gl_check_format, 771 .dpy_refresh = dbus_gl_refresh, 772 .dpy_mouse_set = dbus_mouse_set, 773 .dpy_cursor_define = dbus_cursor_define, 774 775 .dpy_gl_scanout_disable = dbus_scanout_disable, 776 .dpy_gl_scanout_texture = dbus_scanout_texture, 777 #ifdef CONFIG_GBM 778 .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf, 779 .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf, 780 .dpy_gl_release_dmabuf = dbus_release_dmabuf, 781 #endif 782 .dpy_gl_cursor_position = dbus_gl_cursor_position, 783 .dpy_gl_update = dbus_scanout_update, 784 }; 785 #endif 786 787 const DisplayChangeListenerOps dbus_dcl_ops = { 788 .dpy_name = "dbus", 789 .dpy_gfx_update = dbus_gfx_update, 790 .dpy_gfx_switch = dbus_gfx_switch, 791 .dpy_refresh = dbus_refresh, 792 .dpy_mouse_set = dbus_mouse_set, 793 .dpy_cursor_define = dbus_cursor_define, 794 }; 795 796 static void 797 dbus_display_listener_dispose(GObject *object) 798 { 799 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 800 801 unregister_displaychangelistener(&ddl->dcl); 802 g_clear_object(&ddl->conn); 803 g_clear_pointer(&ddl->bus_name, g_free); 804 g_clear_object(&ddl->proxy); 805 #ifdef WIN32 806 g_clear_object(&ddl->map_proxy); 807 g_clear_object(&ddl->d3d11_proxy); 808 g_clear_pointer(&ddl->peer_process, CloseHandle); 809 #ifdef CONFIG_PIXMAN 810 pixman_region32_fini(&ddl->gl_damage); 811 #endif 812 #ifdef CONFIG_OPENGL 813 egl_fb_destroy(&ddl->fb); 814 #endif 815 #endif 816 817 G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); 818 } 819 820 static void 821 dbus_display_listener_constructed(GObject *object) 822 { 823 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 824 825 ddl->dcl.ops = &dbus_dcl_ops; 826 #ifdef CONFIG_OPENGL 827 if (display_opengl) { 828 ddl->dcl.ops = &dbus_gl_dcl_ops; 829 } 830 #endif 831 832 G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object); 833 } 834 835 static void 836 dbus_display_listener_class_init(DBusDisplayListenerClass *klass) 837 { 838 GObjectClass *object_class = G_OBJECT_CLASS(klass); 839 840 object_class->dispose = dbus_display_listener_dispose; 841 object_class->constructed = dbus_display_listener_constructed; 842 } 843 844 static void 845 dbus_display_listener_init(DBusDisplayListener *ddl) 846 { 847 #ifdef CONFIG_PIXMAN 848 pixman_region32_init(&ddl->gl_damage); 849 #endif 850 } 851 852 const char * 853 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl) 854 { 855 return ddl->bus_name ?: "p2p"; 856 } 857 858 DBusDisplayConsole * 859 dbus_display_listener_get_console(DBusDisplayListener *ddl) 860 { 861 return ddl->console; 862 } 863 864 #ifdef WIN32 865 static bool 866 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface) 867 { 868 QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy); 869 bool implements; 870 871 implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface); 872 if (!implements) { 873 g_debug("Display listener does not implement: `%s`", iface); 874 } 875 876 return implements; 877 } 878 879 static bool 880 dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl) 881 { 882 g_autoptr(GError) err = NULL; 883 GDBusConnection *conn; 884 GIOStream *stream; 885 GSocket *sock; 886 g_autoptr(GCredentials) creds = NULL; 887 DWORD *pid; 888 889 if (ddl->peer_process) { 890 return true; 891 } 892 893 conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)); 894 stream = g_dbus_connection_get_stream(conn); 895 896 if (!G_IS_UNIX_CONNECTION(stream)) { 897 return false; 898 } 899 900 sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream)); 901 creds = g_socket_get_credentials(sock, &err); 902 903 if (!creds) { 904 g_debug("Failed to get peer credentials: %s", err->message); 905 return false; 906 } 907 908 pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID); 909 910 if (pid == NULL) { 911 g_debug("Failed to get peer PID"); 912 return false; 913 } 914 915 ddl->peer_process = OpenProcess( 916 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, 917 false, *pid); 918 919 if (!ddl->peer_process) { 920 g_autofree char *msg = g_win32_error_message(GetLastError()); 921 g_debug("Failed to OpenProcess: %s", msg); 922 return false; 923 } 924 925 return true; 926 } 927 #endif 928 929 static void 930 dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl) 931 { 932 #ifdef WIN32 933 g_autoptr(GError) err = NULL; 934 935 if (!dbus_display_listener_implements(ddl, 936 "org.qemu.Display1.Listener.Win32.D3d11")) { 937 return; 938 } 939 940 if (!dbus_display_listener_setup_peer_process(ddl)) { 941 return; 942 } 943 944 ddl->d3d11_proxy = 945 qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn, 946 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 947 NULL, 948 "/org/qemu/Display1/Listener", 949 NULL, 950 &err); 951 if (!ddl->d3d11_proxy) { 952 g_debug("Failed to setup win32 d3d11 proxy: %s", err->message); 953 return; 954 } 955 #endif 956 } 957 958 static void 959 dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) 960 { 961 #ifdef WIN32 962 g_autoptr(GError) err = NULL; 963 964 if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) { 965 return; 966 } 967 968 if (!dbus_display_listener_setup_peer_process(ddl)) { 969 return; 970 } 971 972 ddl->map_proxy = 973 qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn, 974 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 975 NULL, 976 "/org/qemu/Display1/Listener", 977 NULL, 978 &err); 979 if (!ddl->map_proxy) { 980 g_debug("Failed to setup win32 map proxy: %s", err->message); 981 return; 982 } 983 984 ddl->can_share_map = true; 985 #endif 986 } 987 988 static GDBusMessage * 989 dbus_filter(GDBusConnection *connection, 990 GDBusMessage *message, 991 gboolean incoming, 992 gpointer user_data) 993 { 994 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data); 995 guint32 serial; 996 997 if (incoming) { 998 return message; 999 } 1000 1001 serial = g_dbus_message_get_serial(message); 1002 if (serial <= ddl->out_serial_to_discard) { 1003 trace_dbus_filter(serial, ddl->out_serial_to_discard); 1004 return NULL; 1005 } 1006 1007 return message; 1008 } 1009 1010 DBusDisplayListener * 1011 dbus_display_listener_new(const char *bus_name, 1012 GDBusConnection *conn, 1013 DBusDisplayConsole *console) 1014 { 1015 DBusDisplayListener *ddl; 1016 QemuConsole *con; 1017 g_autoptr(GError) err = NULL; 1018 1019 ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL); 1020 ddl->proxy = 1021 qemu_dbus_display1_listener_proxy_new_sync(conn, 1022 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 1023 NULL, 1024 "/org/qemu/Display1/Listener", 1025 NULL, 1026 &err); 1027 if (!ddl->proxy) { 1028 error_report("Failed to setup proxy: %s", err->message); 1029 g_object_unref(conn); 1030 g_object_unref(ddl); 1031 return NULL; 1032 } 1033 1034 ddl->dbus_filter = g_dbus_connection_add_filter(conn, dbus_filter, g_object_ref(ddl), g_object_unref); 1035 ddl->bus_name = g_strdup(bus_name); 1036 ddl->conn = conn; 1037 ddl->console = console; 1038 1039 dbus_display_listener_setup_shared_map(ddl); 1040 dbus_display_listener_setup_d3d11(ddl); 1041 1042 con = qemu_console_lookup_by_index(dbus_display_console_get_index(console)); 1043 assert(con); 1044 ddl->dcl.con = con; 1045 register_displaychangelistener(&ddl->dcl); 1046 1047 return ddl; 1048 } 1049