xref: /openbmc/qemu/ui/dbus-listener.c (revision 42fe7499)
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 CONFIG_OPENGL
30 #include <pixman.h>
31 #endif
32 #ifdef G_OS_UNIX
33 #include <gio/gunixfdlist.h>
34 #endif
35 #ifdef WIN32
36 #include <d3d11.h>
37 #include <dxgi1_2.h>
38 #endif
39 
40 #ifdef CONFIG_OPENGL
41 #include "ui/shader.h"
42 #include "ui/egl-helpers.h"
43 #include "ui/egl-context.h"
44 #endif
45 #include "trace.h"
46 
47 static void dbus_gfx_switch(DisplayChangeListener *dcl,
48                             struct DisplaySurface *new_surface);
49 
50 enum share_kind {
51     SHARE_KIND_NONE,
52     SHARE_KIND_MAPPED,
53     SHARE_KIND_D3DTEX,
54 };
55 
56 struct _DBusDisplayListener {
57     GObject parent;
58 
59     char *bus_name;
60     DBusDisplayConsole *console;
61     GDBusConnection *conn;
62 
63     QemuDBusDisplay1Listener *proxy;
64 
65 #ifdef CONFIG_OPENGL
66     /* Keep track of the damage region */
67     pixman_region32_t 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     int n_rects = pixman_region32_n_rects(&ddl->gl_damage);
549 
550     for (int i = 0; i < n_rects; i++) {
551         pixman_box32_t *box;
552         box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i;
553         /* TODO: Add a UpdateList call to send multiple updates at once */
554         dbus_call_update_gl(dcl, box->x1, box->y1,
555                             box->x2 - box->x1, box->y2 - box->y1);
556     }
557     pixman_region32_clear(&ddl->gl_damage);
558 }
559 #endif /* OPENGL */
560 
561 static void dbus_refresh(DisplayChangeListener *dcl)
562 {
563     graphic_hw_update(dcl->con);
564 }
565 
566 #ifdef CONFIG_OPENGL
567 static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
568                                int x, int y, int w, int h)
569 {
570     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
571 
572     pixman_region32_t rect_region;
573     pixman_region32_init_rect(&rect_region, x, y, w, h);
574     pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region);
575     pixman_region32_fini(&rect_region);
576 }
577 #endif
578 
579 static void dbus_gfx_update(DisplayChangeListener *dcl,
580                             int x, int y, int w, int h)
581 {
582     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
583     pixman_image_t *img;
584     GVariant *v_data;
585     size_t stride;
586 
587     assert(ddl->ds);
588 
589     trace_dbus_update(x, y, w, h);
590 
591 #ifdef WIN32
592     if (dbus_scanout_map(ddl)) {
593         qemu_dbus_display1_listener_win32_map_call_update_map(
594             ddl->map_proxy,
595             x, y, w, h,
596             G_DBUS_CALL_FLAGS_NONE,
597             DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
598         return;
599     }
600 #endif
601 
602     if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
603         v_data = g_variant_new_from_data(
604             G_VARIANT_TYPE("ay"),
605             surface_data(ddl->ds),
606             surface_stride(ddl->ds) * surface_height(ddl->ds),
607             TRUE,
608             (GDestroyNotify)pixman_image_unref,
609             pixman_image_ref(ddl->ds->image));
610         qemu_dbus_display1_listener_call_scanout(
611             ddl->proxy,
612             surface_width(ddl->ds),
613             surface_height(ddl->ds),
614             surface_stride(ddl->ds),
615             surface_format(ddl->ds),
616             v_data,
617             G_DBUS_CALL_FLAGS_NONE,
618             DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
619         return;
620     }
621 
622     /* make a copy, since gvariant only handles linear data */
623     stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
624     img = pixman_image_create_bits(surface_format(ddl->ds),
625                                    w, h, NULL, stride);
626     pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img,
627                            x, y, 0, 0, 0, 0, w, h);
628 
629     v_data = g_variant_new_from_data(
630         G_VARIANT_TYPE("ay"),
631         pixman_image_get_data(img),
632         pixman_image_get_stride(img) * h,
633         TRUE,
634         (GDestroyNotify)pixman_image_unref,
635         img);
636     qemu_dbus_display1_listener_call_update(ddl->proxy,
637         x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img),
638         v_data,
639         G_DBUS_CALL_FLAGS_NONE,
640         DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
641 }
642 
643 #ifdef CONFIG_OPENGL
644 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
645                                struct DisplaySurface *new_surface)
646 {
647     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
648 
649     trace_dbus_gl_gfx_switch(new_surface);
650 
651     ddl->ds = new_surface;
652     ddl->ds_share = SHARE_KIND_NONE;
653     if (ddl->ds) {
654         int width = surface_width(ddl->ds);
655         int height = surface_height(ddl->ds);
656 
657         /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
658         dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
659                              width, height, 0, 0, width, height, NULL);
660     }
661 }
662 #endif
663 
664 static void dbus_gfx_switch(DisplayChangeListener *dcl,
665                             struct DisplaySurface *new_surface)
666 {
667     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
668 
669     ddl->ds = new_surface;
670     ddl->ds_share = SHARE_KIND_NONE;
671 }
672 
673 static void dbus_mouse_set(DisplayChangeListener *dcl,
674                            int x, int y, int on)
675 {
676     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
677 
678     qemu_dbus_display1_listener_call_mouse_set(
679         ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
680 }
681 
682 static void dbus_cursor_define(DisplayChangeListener *dcl,
683                                QEMUCursor *c)
684 {
685     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
686     GVariant *v_data = NULL;
687 
688     v_data = g_variant_new_from_data(
689         G_VARIANT_TYPE("ay"),
690         c->data,
691         c->width * c->height * 4,
692         TRUE,
693         (GDestroyNotify)cursor_unref,
694         cursor_ref(c));
695 
696     qemu_dbus_display1_listener_call_cursor_define(
697         ddl->proxy,
698         c->width,
699         c->height,
700         c->hot_x,
701         c->hot_y,
702         v_data,
703         G_DBUS_CALL_FLAGS_NONE,
704         -1,
705         NULL,
706         NULL,
707         NULL);
708 }
709 
710 #ifdef CONFIG_OPENGL
711 const DisplayChangeListenerOps dbus_gl_dcl_ops = {
712     .dpy_name                = "dbus-gl",
713     .dpy_gfx_update          = dbus_gl_gfx_update,
714     .dpy_gfx_switch          = dbus_gl_gfx_switch,
715     .dpy_gfx_check_format    = console_gl_check_format,
716     .dpy_refresh             = dbus_gl_refresh,
717     .dpy_mouse_set           = dbus_mouse_set,
718     .dpy_cursor_define       = dbus_cursor_define,
719 
720     .dpy_gl_scanout_disable  = dbus_scanout_disable,
721     .dpy_gl_scanout_texture  = dbus_scanout_texture,
722 #ifdef CONFIG_GBM
723     .dpy_gl_scanout_dmabuf   = dbus_scanout_dmabuf,
724     .dpy_gl_cursor_dmabuf    = dbus_cursor_dmabuf,
725     .dpy_gl_release_dmabuf   = dbus_release_dmabuf,
726 #endif
727     .dpy_gl_cursor_position  = dbus_gl_cursor_position,
728     .dpy_gl_update           = dbus_scanout_update,
729 };
730 #endif
731 
732 const DisplayChangeListenerOps dbus_dcl_ops = {
733     .dpy_name                = "dbus",
734     .dpy_gfx_update          = dbus_gfx_update,
735     .dpy_gfx_switch          = dbus_gfx_switch,
736     .dpy_refresh             = dbus_refresh,
737     .dpy_mouse_set           = dbus_mouse_set,
738     .dpy_cursor_define       = dbus_cursor_define,
739 };
740 
741 static void
742 dbus_display_listener_dispose(GObject *object)
743 {
744     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
745 
746     unregister_displaychangelistener(&ddl->dcl);
747     g_clear_object(&ddl->conn);
748     g_clear_pointer(&ddl->bus_name, g_free);
749     g_clear_object(&ddl->proxy);
750 #ifdef WIN32
751     g_clear_object(&ddl->map_proxy);
752     g_clear_object(&ddl->d3d11_proxy);
753     g_clear_pointer(&ddl->peer_process, CloseHandle);
754 #ifdef CONFIG_OPENGL
755     pixman_region32_fini(&ddl->gl_damage);
756     egl_fb_destroy(&ddl->fb);
757 #endif
758 #endif
759 
760     G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
761 }
762 
763 static void
764 dbus_display_listener_constructed(GObject *object)
765 {
766     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
767 
768     ddl->dcl.ops = &dbus_dcl_ops;
769 #ifdef CONFIG_OPENGL
770     if (display_opengl) {
771         ddl->dcl.ops = &dbus_gl_dcl_ops;
772     }
773 #endif
774 
775     G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object);
776 }
777 
778 static void
779 dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
780 {
781     GObjectClass *object_class = G_OBJECT_CLASS(klass);
782 
783     object_class->dispose = dbus_display_listener_dispose;
784     object_class->constructed = dbus_display_listener_constructed;
785 }
786 
787 static void
788 dbus_display_listener_init(DBusDisplayListener *ddl)
789 {
790 #ifdef CONFIG_OPENGL
791     pixman_region32_init(&ddl->gl_damage);
792 #endif
793 }
794 
795 const char *
796 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl)
797 {
798     return ddl->bus_name ?: "p2p";
799 }
800 
801 DBusDisplayConsole *
802 dbus_display_listener_get_console(DBusDisplayListener *ddl)
803 {
804     return ddl->console;
805 }
806 
807 #ifdef WIN32
808 static bool
809 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface)
810 {
811     QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy);
812     bool implements;
813 
814     implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface);
815     if (!implements) {
816         g_debug("Display listener does not implement: `%s`", iface);
817     }
818 
819     return implements;
820 }
821 
822 static bool
823 dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl)
824 {
825     g_autoptr(GError) err = NULL;
826     GDBusConnection *conn;
827     GIOStream *stream;
828     GSocket *sock;
829     g_autoptr(GCredentials) creds = NULL;
830     DWORD *pid;
831 
832     if (ddl->peer_process) {
833         return true;
834     }
835 
836     conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy));
837     stream = g_dbus_connection_get_stream(conn);
838 
839     if (!G_IS_UNIX_CONNECTION(stream)) {
840         return false;
841     }
842 
843     sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream));
844     creds = g_socket_get_credentials(sock, &err);
845 
846     if (!creds) {
847         g_debug("Failed to get peer credentials: %s", err->message);
848         return false;
849     }
850 
851     pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID);
852 
853     if (pid == NULL) {
854         g_debug("Failed to get peer PID");
855         return false;
856     }
857 
858     ddl->peer_process = OpenProcess(
859         PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
860         false, *pid);
861 
862     if (!ddl->peer_process) {
863         g_autofree char *msg = g_win32_error_message(GetLastError());
864         g_debug("Failed to OpenProcess: %s", msg);
865         return false;
866     }
867 
868     return true;
869 }
870 #endif
871 
872 static void
873 dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl)
874 {
875 #ifdef WIN32
876     g_autoptr(GError) err = NULL;
877 
878     if (!dbus_display_listener_implements(ddl,
879             "org.qemu.Display1.Listener.Win32.D3d11")) {
880         return;
881     }
882 
883     if (!dbus_display_listener_setup_peer_process(ddl)) {
884         return;
885     }
886 
887     ddl->d3d11_proxy =
888         qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn,
889             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
890             NULL,
891             "/org/qemu/Display1/Listener",
892             NULL,
893             &err);
894     if (!ddl->d3d11_proxy) {
895         g_debug("Failed to setup win32 d3d11 proxy: %s", err->message);
896         return;
897     }
898 #endif
899 }
900 
901 static void
902 dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl)
903 {
904 #ifdef WIN32
905     g_autoptr(GError) err = NULL;
906 
907     if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) {
908         return;
909     }
910 
911     if (!dbus_display_listener_setup_peer_process(ddl)) {
912         return;
913     }
914 
915     ddl->map_proxy =
916         qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn,
917             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
918             NULL,
919             "/org/qemu/Display1/Listener",
920             NULL,
921             &err);
922     if (!ddl->map_proxy) {
923         g_debug("Failed to setup win32 map proxy: %s", err->message);
924         return;
925     }
926 
927     ddl->can_share_map = true;
928 #endif
929 }
930 
931 DBusDisplayListener *
932 dbus_display_listener_new(const char *bus_name,
933                           GDBusConnection *conn,
934                           DBusDisplayConsole *console)
935 {
936     DBusDisplayListener *ddl;
937     QemuConsole *con;
938     g_autoptr(GError) err = NULL;
939 
940     ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL);
941     ddl->proxy =
942         qemu_dbus_display1_listener_proxy_new_sync(conn,
943             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
944             NULL,
945             "/org/qemu/Display1/Listener",
946             NULL,
947             &err);
948     if (!ddl->proxy) {
949         error_report("Failed to setup proxy: %s", err->message);
950         g_object_unref(conn);
951         g_object_unref(ddl);
952         return NULL;
953     }
954 
955     ddl->bus_name = g_strdup(bus_name);
956     ddl->conn = conn;
957     ddl->console = console;
958 
959     dbus_display_listener_setup_shared_map(ddl);
960     dbus_display_listener_setup_d3d11(ddl);
961 
962     con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
963     assert(con);
964     ddl->dcl.con = con;
965     register_displaychangelistener(&ddl->dcl);
966 
967     return ddl;
968 }
969