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