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