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