xref: /openbmc/qemu/ui/dbus-listener.c (revision e8a2db94792ca036adbdf3656125587c3097bd83)
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