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