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 "sysemu/sysemu.h" 27 #include "dbus.h" 28 #ifdef G_OS_UNIX 29 #include <gio/gunixfdlist.h> 30 #endif 31 32 #ifdef CONFIG_OPENGL 33 #include "ui/shader.h" 34 #include "ui/egl-helpers.h" 35 #include "ui/egl-context.h" 36 #endif 37 #include "trace.h" 38 39 static void dbus_gfx_switch(DisplayChangeListener *dcl, 40 struct DisplaySurface *new_surface); 41 42 struct _DBusDisplayListener { 43 GObject parent; 44 45 char *bus_name; 46 DBusDisplayConsole *console; 47 GDBusConnection *conn; 48 49 QemuDBusDisplay1Listener *proxy; 50 51 DisplayChangeListener dcl; 52 DisplaySurface *ds; 53 int gl_updates; 54 55 bool ds_mapped; 56 bool can_share_map; 57 58 #ifdef WIN32 59 QemuDBusDisplay1ListenerWin32Map *map_proxy; 60 HANDLE peer_process; 61 #ifdef CONFIG_OPENGL 62 egl_fb fb; 63 #endif 64 #endif 65 }; 66 67 G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) 68 69 static void dbus_gfx_update(DisplayChangeListener *dcl, 70 int x, int y, int w, int h); 71 72 #ifdef CONFIG_OPENGL 73 static void dbus_scanout_disable(DisplayChangeListener *dcl) 74 { 75 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 76 77 ddl->ds = NULL; 78 qemu_dbus_display1_listener_call_disable( 79 ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 80 } 81 82 #ifdef CONFIG_GBM 83 static void dbus_update_gl_cb(GObject *source_object, 84 GAsyncResult *res, 85 gpointer user_data) 86 { 87 g_autoptr(GError) err = NULL; 88 DBusDisplayListener *ddl = user_data; 89 90 if (!qemu_dbus_display1_listener_call_update_dmabuf_finish(ddl->proxy, 91 res, &err)) { 92 error_report("Failed to call update: %s", err->message); 93 } 94 95 graphic_hw_gl_block(ddl->dcl.con, false); 96 g_object_unref(ddl); 97 } 98 #endif 99 100 static void dbus_call_update_gl(DisplayChangeListener *dcl, 101 int x, int y, int w, int h) 102 { 103 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 104 105 trace_dbus_update_gl(x, y, w, h); 106 107 glFlush(); 108 #ifdef CONFIG_GBM 109 graphic_hw_gl_block(ddl->dcl.con, true); 110 qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy, 111 x, y, w, h, 112 G_DBUS_CALL_FLAGS_NONE, 113 DBUS_DEFAULT_TIMEOUT, NULL, 114 dbus_update_gl_cb, 115 g_object_ref(ddl)); 116 #endif 117 118 #ifdef WIN32 119 egl_fb_read_rect(ddl->ds, &ddl->fb, x, y, w, h); 120 dbus_gfx_update(dcl, x, y, w, h); 121 #endif 122 } 123 124 #ifdef CONFIG_GBM 125 static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, 126 QemuDmaBuf *dmabuf) 127 { 128 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 129 g_autoptr(GError) err = NULL; 130 g_autoptr(GUnixFDList) fd_list = NULL; 131 132 fd_list = g_unix_fd_list_new(); 133 if (g_unix_fd_list_append(fd_list, dmabuf->fd, &err) != 0) { 134 error_report("Failed to setup dmabuf fdlist: %s", err->message); 135 return; 136 } 137 138 /* FIXME: add missing x/y/w/h support */ 139 qemu_dbus_display1_listener_call_scanout_dmabuf( 140 ddl->proxy, 141 g_variant_new_handle(0), 142 dmabuf->width, 143 dmabuf->height, 144 dmabuf->stride, 145 dmabuf->fourcc, 146 dmabuf->modifier, 147 dmabuf->y0_top, 148 G_DBUS_CALL_FLAGS_NONE, 149 -1, 150 fd_list, 151 NULL, NULL, NULL); 152 } 153 #endif /* GBM */ 154 #endif /* OPENGL */ 155 156 #ifdef WIN32 157 static bool dbus_scanout_map(DBusDisplayListener *ddl) 158 { 159 g_autoptr(GError) err = NULL; 160 BOOL success; 161 HANDLE target_handle; 162 163 if (ddl->ds_mapped) { 164 return true; 165 } 166 167 if (!ddl->can_share_map || !ddl->ds->handle) { 168 return false; 169 } 170 171 success = DuplicateHandle( 172 GetCurrentProcess(), 173 ddl->ds->handle, 174 ddl->peer_process, 175 &target_handle, 176 FILE_MAP_READ | SECTION_QUERY, 177 FALSE, 0); 178 if (!success) { 179 g_autofree char *msg = g_win32_error_message(GetLastError()); 180 g_debug("Failed to DuplicateHandle: %s", msg); 181 ddl->can_share_map = false; 182 return false; 183 } 184 185 if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync( 186 ddl->map_proxy, 187 GPOINTER_TO_UINT(target_handle), 188 ddl->ds->handle_offset, 189 surface_width(ddl->ds), 190 surface_height(ddl->ds), 191 surface_stride(ddl->ds), 192 surface_format(ddl->ds), 193 G_DBUS_CALL_FLAGS_NONE, 194 DBUS_DEFAULT_TIMEOUT, 195 NULL, 196 &err)) { 197 g_debug("Failed to call ScanoutMap: %s", err->message); 198 ddl->can_share_map = false; 199 return false; 200 } 201 202 ddl->ds_mapped = true; 203 204 return true; 205 } 206 #endif 207 208 #ifdef CONFIG_OPENGL 209 static void dbus_scanout_texture(DisplayChangeListener *dcl, 210 uint32_t tex_id, 211 bool backing_y_0_top, 212 uint32_t backing_width, 213 uint32_t backing_height, 214 uint32_t x, uint32_t y, 215 uint32_t w, uint32_t h) 216 { 217 trace_dbus_scanout_texture(tex_id, backing_y_0_top, 218 backing_width, backing_height, x, y, w, h); 219 #ifdef CONFIG_GBM 220 QemuDmaBuf dmabuf = { 221 .width = backing_width, 222 .height = backing_height, 223 .y0_top = backing_y_0_top, 224 .x = x, 225 .y = y, 226 .scanout_width = w, 227 .scanout_height = h, 228 }; 229 230 assert(tex_id); 231 dmabuf.fd = egl_get_fd_for_texture( 232 tex_id, (EGLint *)&dmabuf.stride, 233 (EGLint *)&dmabuf.fourcc, 234 &dmabuf.modifier); 235 if (dmabuf.fd < 0) { 236 error_report("%s: failed to get fd for texture", __func__); 237 return; 238 } 239 240 dbus_scanout_dmabuf(dcl, &dmabuf); 241 close(dmabuf.fd); 242 #endif 243 244 #ifdef WIN32 245 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 246 247 /* there must be a matching gfx_switch before */ 248 assert(surface_width(ddl->ds) == w); 249 assert(surface_height(ddl->ds) == h); 250 egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false); 251 #endif 252 } 253 254 #ifdef CONFIG_GBM 255 static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, 256 QemuDmaBuf *dmabuf, bool have_hot, 257 uint32_t hot_x, uint32_t hot_y) 258 { 259 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 260 DisplaySurface *ds; 261 GVariant *v_data = NULL; 262 egl_fb cursor_fb = EGL_FB_INIT; 263 264 if (!dmabuf) { 265 qemu_dbus_display1_listener_call_mouse_set( 266 ddl->proxy, 0, 0, false, 267 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 268 return; 269 } 270 271 egl_dmabuf_import_texture(dmabuf); 272 if (!dmabuf->texture) { 273 return; 274 } 275 egl_fb_setup_for_tex(&cursor_fb, dmabuf->width, dmabuf->height, 276 dmabuf->texture, false); 277 ds = qemu_create_displaysurface(dmabuf->width, dmabuf->height); 278 egl_fb_read(ds, &cursor_fb); 279 280 v_data = g_variant_new_from_data( 281 G_VARIANT_TYPE("ay"), 282 surface_data(ds), 283 surface_width(ds) * surface_height(ds) * 4, 284 TRUE, 285 (GDestroyNotify)qemu_free_displaysurface, 286 ds); 287 qemu_dbus_display1_listener_call_cursor_define( 288 ddl->proxy, 289 surface_width(ds), 290 surface_height(ds), 291 hot_x, 292 hot_y, 293 v_data, 294 G_DBUS_CALL_FLAGS_NONE, 295 -1, 296 NULL, 297 NULL, 298 NULL); 299 } 300 301 static void dbus_release_dmabuf(DisplayChangeListener *dcl, 302 QemuDmaBuf *dmabuf) 303 { 304 dbus_scanout_disable(dcl); 305 } 306 #endif /* GBM */ 307 308 static void dbus_gl_cursor_position(DisplayChangeListener *dcl, 309 uint32_t pos_x, uint32_t pos_y) 310 { 311 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 312 313 qemu_dbus_display1_listener_call_mouse_set( 314 ddl->proxy, pos_x, pos_y, true, 315 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 316 } 317 318 static void dbus_scanout_update(DisplayChangeListener *dcl, 319 uint32_t x, uint32_t y, 320 uint32_t w, uint32_t h) 321 { 322 dbus_call_update_gl(dcl, x, y, w, h); 323 } 324 325 static void dbus_gl_refresh(DisplayChangeListener *dcl) 326 { 327 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 328 329 graphic_hw_update(dcl->con); 330 331 if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) { 332 return; 333 } 334 335 if (ddl->gl_updates) { 336 dbus_call_update_gl(dcl, 0, 0, 337 surface_width(ddl->ds), surface_height(ddl->ds)); 338 ddl->gl_updates = 0; 339 } 340 } 341 #endif /* OPENGL */ 342 343 static void dbus_refresh(DisplayChangeListener *dcl) 344 { 345 graphic_hw_update(dcl->con); 346 } 347 348 #ifdef CONFIG_OPENGL 349 static void dbus_gl_gfx_update(DisplayChangeListener *dcl, 350 int x, int y, int w, int h) 351 { 352 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 353 354 ddl->gl_updates++; 355 } 356 #endif 357 358 static void dbus_gfx_update(DisplayChangeListener *dcl, 359 int x, int y, int w, int h) 360 { 361 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 362 pixman_image_t *img; 363 GVariant *v_data; 364 size_t stride; 365 366 assert(ddl->ds); 367 368 trace_dbus_update(x, y, w, h); 369 370 #ifdef WIN32 371 if (dbus_scanout_map(ddl)) { 372 qemu_dbus_display1_listener_win32_map_call_update_map( 373 ddl->map_proxy, 374 x, y, w, h, 375 G_DBUS_CALL_FLAGS_NONE, 376 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 377 return; 378 } 379 #endif 380 381 if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { 382 v_data = g_variant_new_from_data( 383 G_VARIANT_TYPE("ay"), 384 surface_data(ddl->ds), 385 surface_stride(ddl->ds) * surface_height(ddl->ds), 386 TRUE, 387 (GDestroyNotify)pixman_image_unref, 388 pixman_image_ref(ddl->ds->image)); 389 qemu_dbus_display1_listener_call_scanout( 390 ddl->proxy, 391 surface_width(ddl->ds), 392 surface_height(ddl->ds), 393 surface_stride(ddl->ds), 394 surface_format(ddl->ds), 395 v_data, 396 G_DBUS_CALL_FLAGS_NONE, 397 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 398 return; 399 } 400 401 /* make a copy, since gvariant only handles linear data */ 402 stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); 403 img = pixman_image_create_bits(surface_format(ddl->ds), 404 w, h, NULL, stride); 405 pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img, 406 x, y, 0, 0, 0, 0, w, h); 407 408 v_data = g_variant_new_from_data( 409 G_VARIANT_TYPE("ay"), 410 pixman_image_get_data(img), 411 pixman_image_get_stride(img) * h, 412 TRUE, 413 (GDestroyNotify)pixman_image_unref, 414 img); 415 qemu_dbus_display1_listener_call_update(ddl->proxy, 416 x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img), 417 v_data, 418 G_DBUS_CALL_FLAGS_NONE, 419 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); 420 } 421 422 #ifdef CONFIG_OPENGL 423 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, 424 struct DisplaySurface *new_surface) 425 { 426 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 427 428 trace_dbus_gl_gfx_switch(new_surface); 429 430 ddl->ds = new_surface; 431 if (ddl->ds) { 432 int width = surface_width(ddl->ds); 433 int height = surface_height(ddl->ds); 434 435 /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ 436 dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, 437 width, height, 0, 0, width, height); 438 } 439 } 440 #endif 441 442 static void dbus_gfx_switch(DisplayChangeListener *dcl, 443 struct DisplaySurface *new_surface) 444 { 445 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 446 447 ddl->ds = new_surface; 448 #ifdef WIN32 449 ddl->ds_mapped = false; 450 #endif 451 if (!ddl->ds) { 452 /* why not call disable instead? */ 453 return; 454 } 455 } 456 457 static void dbus_mouse_set(DisplayChangeListener *dcl, 458 int x, int y, int on) 459 { 460 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 461 462 qemu_dbus_display1_listener_call_mouse_set( 463 ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); 464 } 465 466 static void dbus_cursor_define(DisplayChangeListener *dcl, 467 QEMUCursor *c) 468 { 469 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); 470 GVariant *v_data = NULL; 471 472 v_data = g_variant_new_from_data( 473 G_VARIANT_TYPE("ay"), 474 c->data, 475 c->width * c->height * 4, 476 TRUE, 477 (GDestroyNotify)cursor_unref, 478 cursor_ref(c)); 479 480 qemu_dbus_display1_listener_call_cursor_define( 481 ddl->proxy, 482 c->width, 483 c->height, 484 c->hot_x, 485 c->hot_y, 486 v_data, 487 G_DBUS_CALL_FLAGS_NONE, 488 -1, 489 NULL, 490 NULL, 491 NULL); 492 } 493 494 #ifdef CONFIG_OPENGL 495 const DisplayChangeListenerOps dbus_gl_dcl_ops = { 496 .dpy_name = "dbus-gl", 497 .dpy_gfx_update = dbus_gl_gfx_update, 498 .dpy_gfx_switch = dbus_gl_gfx_switch, 499 .dpy_gfx_check_format = console_gl_check_format, 500 .dpy_refresh = dbus_gl_refresh, 501 .dpy_mouse_set = dbus_mouse_set, 502 .dpy_cursor_define = dbus_cursor_define, 503 504 .dpy_gl_scanout_disable = dbus_scanout_disable, 505 .dpy_gl_scanout_texture = dbus_scanout_texture, 506 #ifdef CONFIG_GBM 507 .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf, 508 .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf, 509 .dpy_gl_release_dmabuf = dbus_release_dmabuf, 510 #endif 511 .dpy_gl_cursor_position = dbus_gl_cursor_position, 512 .dpy_gl_update = dbus_scanout_update, 513 }; 514 #endif 515 516 const DisplayChangeListenerOps dbus_dcl_ops = { 517 .dpy_name = "dbus", 518 .dpy_gfx_update = dbus_gfx_update, 519 .dpy_gfx_switch = dbus_gfx_switch, 520 .dpy_refresh = dbus_refresh, 521 .dpy_mouse_set = dbus_mouse_set, 522 .dpy_cursor_define = dbus_cursor_define, 523 }; 524 525 static void 526 dbus_display_listener_dispose(GObject *object) 527 { 528 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 529 530 unregister_displaychangelistener(&ddl->dcl); 531 g_clear_object(&ddl->conn); 532 g_clear_pointer(&ddl->bus_name, g_free); 533 g_clear_object(&ddl->proxy); 534 #ifdef WIN32 535 g_clear_object(&ddl->map_proxy); 536 g_clear_pointer(&ddl->peer_process, CloseHandle); 537 #ifdef CONFIG_OPENGL 538 egl_fb_destroy(&ddl->fb); 539 #endif 540 #endif 541 542 G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); 543 } 544 545 static void 546 dbus_display_listener_constructed(GObject *object) 547 { 548 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); 549 550 ddl->dcl.ops = &dbus_dcl_ops; 551 #ifdef CONFIG_OPENGL 552 if (display_opengl) { 553 ddl->dcl.ops = &dbus_gl_dcl_ops; 554 } 555 #endif 556 557 G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object); 558 } 559 560 static void 561 dbus_display_listener_class_init(DBusDisplayListenerClass *klass) 562 { 563 GObjectClass *object_class = G_OBJECT_CLASS(klass); 564 565 object_class->dispose = dbus_display_listener_dispose; 566 object_class->constructed = dbus_display_listener_constructed; 567 } 568 569 static void 570 dbus_display_listener_init(DBusDisplayListener *ddl) 571 { 572 } 573 574 const char * 575 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl) 576 { 577 return ddl->bus_name ?: "p2p"; 578 } 579 580 DBusDisplayConsole * 581 dbus_display_listener_get_console(DBusDisplayListener *ddl) 582 { 583 return ddl->console; 584 } 585 586 #ifdef WIN32 587 static bool 588 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface) 589 { 590 QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy); 591 bool implements; 592 593 implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface); 594 if (!implements) { 595 g_debug("Display listener does not implement: `%s`", iface); 596 } 597 598 return implements; 599 } 600 #endif 601 602 static void 603 dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) 604 { 605 #ifdef WIN32 606 g_autoptr(GError) err = NULL; 607 GDBusConnection *conn; 608 GIOStream *stream; 609 GSocket *sock; 610 g_autoptr(GCredentials) creds = NULL; 611 DWORD *pid; 612 613 if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) { 614 return; 615 } 616 617 conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)); 618 stream = g_dbus_connection_get_stream(conn); 619 620 if (!G_IS_UNIX_CONNECTION(stream)) { 621 return; 622 } 623 624 sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream)); 625 creds = g_socket_get_credentials(sock, &err); 626 627 if (!creds) { 628 g_debug("Failed to get peer credentials: %s", err->message); 629 return; 630 } 631 632 pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID); 633 634 if (pid == NULL) { 635 g_debug("Failed to get peer PID"); 636 return; 637 } 638 639 ddl->peer_process = OpenProcess( 640 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, 641 false, *pid); 642 643 if (!ddl->peer_process) { 644 g_autofree char *msg = g_win32_error_message(GetLastError()); 645 g_debug("Failed to OpenProcess: %s", msg); 646 return; 647 } 648 649 ddl->map_proxy = 650 qemu_dbus_display1_listener_win32_map_proxy_new_sync(conn, 651 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 652 NULL, 653 "/org/qemu/Display1/Listener", 654 NULL, 655 &err); 656 if (!ddl->map_proxy) { 657 g_debug("Failed to setup win32 map proxy: %s", err->message); 658 return; 659 } 660 661 ddl->can_share_map = true; 662 #endif 663 } 664 665 DBusDisplayListener * 666 dbus_display_listener_new(const char *bus_name, 667 GDBusConnection *conn, 668 DBusDisplayConsole *console) 669 { 670 DBusDisplayListener *ddl; 671 QemuConsole *con; 672 g_autoptr(GError) err = NULL; 673 674 ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL); 675 ddl->proxy = 676 qemu_dbus_display1_listener_proxy_new_sync(conn, 677 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, 678 NULL, 679 "/org/qemu/Display1/Listener", 680 NULL, 681 &err); 682 if (!ddl->proxy) { 683 error_report("Failed to setup proxy: %s", err->message); 684 g_object_unref(conn); 685 g_object_unref(ddl); 686 return NULL; 687 } 688 689 ddl->bus_name = g_strdup(bus_name); 690 ddl->conn = conn; 691 ddl->console = console; 692 693 dbus_display_listener_setup_shared_map(ddl); 694 695 con = qemu_console_lookup_by_index(dbus_display_console_get_index(console)); 696 assert(con); 697 ddl->dcl.con = con; 698 register_displaychangelistener(&ddl->dcl); 699 700 return ddl; 701 } 702