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