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