xref: /qemu/ui/dbus-listener.c (revision c0fcd633)
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 
ddl_discard_pending_messages(DBusDisplayListener * ddl)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
dbus_scanout_disable(DisplayChangeListener * dcl)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
d3d_texture2d_share(ID3D11Texture2D * d3d_texture,HANDLE * handle,Error ** errp)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 
d3d_texture2d_acquire0(ID3D11Texture2D * d3d_texture,Error ** errp)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 
d3d_texture2d_release0(ID3D11Texture2D * d3d_texture,Error ** errp)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)
dbus_update_gl_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)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 
dbus_call_update_gl(DisplayChangeListener * dcl,int x,int y,int w,int h)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
dbus_scanout_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf)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     int fd;
282     uint32_t width, height, stride, fourcc;
283     uint64_t modifier;
284     bool y0_top;
285 
286     fd = qemu_dmabuf_get_fd(dmabuf);
287     fd_list = g_unix_fd_list_new();
288     if (g_unix_fd_list_append(fd_list, fd, &err) != 0) {
289         error_report("Failed to setup dmabuf fdlist: %s", err->message);
290         return;
291     }
292 
293     ddl_discard_pending_messages(ddl);
294 
295     width = qemu_dmabuf_get_width(dmabuf);
296     height = qemu_dmabuf_get_height(dmabuf);
297     stride = qemu_dmabuf_get_stride(dmabuf);
298     fourcc = qemu_dmabuf_get_fourcc(dmabuf);
299     modifier = qemu_dmabuf_get_modifier(dmabuf);
300     y0_top = qemu_dmabuf_get_y0_top(dmabuf);
301 
302     /* FIXME: add missing x/y/w/h support */
303     qemu_dbus_display1_listener_call_scanout_dmabuf(
304         ddl->proxy, g_variant_new_handle(0),
305         width, height, stride, fourcc, modifier,
306         y0_top, G_DBUS_CALL_FLAGS_NONE,
307         -1, fd_list, NULL, NULL, NULL);
308 }
309 #endif /* GBM */
310 #endif /* OPENGL */
311 
312 #ifdef WIN32
dbus_scanout_map(DBusDisplayListener * ddl)313 static bool dbus_scanout_map(DBusDisplayListener *ddl)
314 {
315     g_autoptr(GError) err = NULL;
316     BOOL success;
317     HANDLE target_handle;
318 
319     if (ddl->ds_share == SHARE_KIND_MAPPED) {
320         return true;
321     }
322 
323     if (!ddl->can_share_map || !ddl->ds->handle) {
324         return false;
325     }
326 
327     success = DuplicateHandle(
328         GetCurrentProcess(),
329         ddl->ds->handle,
330         ddl->peer_process,
331         &target_handle,
332         FILE_MAP_READ | SECTION_QUERY,
333         FALSE, 0);
334     if (!success) {
335         g_autofree char *msg = g_win32_error_message(GetLastError());
336         g_debug("Failed to DuplicateHandle: %s", msg);
337         ddl->can_share_map = false;
338         return false;
339     }
340 
341     ddl_discard_pending_messages(ddl);
342 
343     if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync(
344             ddl->map_proxy,
345             GPOINTER_TO_UINT(target_handle),
346             ddl->ds->handle_offset,
347             surface_width(ddl->ds),
348             surface_height(ddl->ds),
349             surface_stride(ddl->ds),
350             surface_format(ddl->ds),
351             G_DBUS_CALL_FLAGS_NONE,
352             DBUS_DEFAULT_TIMEOUT,
353             NULL,
354             &err)) {
355         g_debug("Failed to call ScanoutMap: %s", err->message);
356         ddl->can_share_map = false;
357         return false;
358     }
359 
360     ddl->ds_share = SHARE_KIND_MAPPED;
361 
362     return true;
363 }
364 
365 #ifdef CONFIG_OPENGL
366 static bool
dbus_scanout_share_d3d_texture(DBusDisplayListener * ddl,ID3D11Texture2D * tex,bool backing_y_0_top,uint32_t backing_width,uint32_t backing_height,uint32_t x,uint32_t y,uint32_t w,uint32_t h)367 dbus_scanout_share_d3d_texture(
368     DBusDisplayListener *ddl,
369     ID3D11Texture2D *tex,
370     bool backing_y_0_top,
371     uint32_t backing_width,
372     uint32_t backing_height,
373     uint32_t x, uint32_t y,
374     uint32_t w, uint32_t h)
375 {
376     Error *err = NULL;
377     BOOL success;
378     HANDLE share_handle, target_handle;
379 
380     if (!d3d_texture2d_release0(tex, &err)) {
381         error_report_err(err);
382         return false;
383     }
384 
385     if (!d3d_texture2d_share(tex, &share_handle, &err)) {
386         error_report_err(err);
387         return false;
388     }
389 
390     success = DuplicateHandle(
391         GetCurrentProcess(),
392         share_handle,
393         ddl->peer_process,
394         &target_handle,
395         0,
396         FALSE, DUPLICATE_SAME_ACCESS);
397     if (!success) {
398         g_autofree char *msg = g_win32_error_message(GetLastError());
399         g_debug("Failed to DuplicateHandle: %s", msg);
400         CloseHandle(share_handle);
401         return false;
402     }
403 
404     ddl_discard_pending_messages(ddl);
405 
406     qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d(
407         ddl->d3d11_proxy,
408         GPOINTER_TO_INT(target_handle),
409         backing_width,
410         backing_height,
411         backing_y_0_top,
412         x, y, w, h,
413         G_DBUS_CALL_FLAGS_NONE,
414         -1,
415         NULL, NULL, NULL);
416 
417     CloseHandle(share_handle);
418 
419     if (!d3d_texture2d_acquire0(tex, &err)) {
420         error_report_err(err);
421         return false;
422     }
423 
424     ddl->d3d_texture = tex;
425     ddl->ds_share = SHARE_KIND_D3DTEX;
426 
427     return true;
428 }
429 #endif /* CONFIG_OPENGL */
430 #endif /* WIN32 */
431 
432 #ifdef CONFIG_OPENGL
dbus_scanout_texture(DisplayChangeListener * dcl,uint32_t tex_id,bool backing_y_0_top,uint32_t backing_width,uint32_t backing_height,uint32_t x,uint32_t y,uint32_t w,uint32_t h,void * d3d_tex2d)433 static void dbus_scanout_texture(DisplayChangeListener *dcl,
434                                  uint32_t tex_id,
435                                  bool backing_y_0_top,
436                                  uint32_t backing_width,
437                                  uint32_t backing_height,
438                                  uint32_t x, uint32_t y,
439                                  uint32_t w, uint32_t h,
440                                  void *d3d_tex2d)
441 {
442     trace_dbus_scanout_texture(tex_id, backing_y_0_top,
443                                backing_width, backing_height, x, y, w, h);
444 #ifdef CONFIG_GBM
445     g_autoptr(QemuDmaBuf) dmabuf = NULL;
446     int fd;
447     uint32_t stride, fourcc;
448     uint64_t modifier;
449 
450     assert(tex_id);
451     fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc,
452                                 &modifier);
453     if (fd < 0) {
454         error_report("%s: failed to get fd for texture", __func__);
455         return;
456     }
457     dmabuf = qemu_dmabuf_new(w, h, stride, x, y, backing_width,
458                              backing_height, fourcc, modifier, fd,
459                              false, backing_y_0_top);
460 
461     dbus_scanout_dmabuf(dcl, dmabuf);
462     qemu_dmabuf_close(dmabuf);
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
dbus_cursor_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf,bool have_hot,uint32_t hot_x,uint32_t hot_y)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     uint32_t width, height, texture;
492 
493     if (!dmabuf) {
494         qemu_dbus_display1_listener_call_mouse_set(
495             ddl->proxy, 0, 0, false,
496             G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
497         return;
498     }
499 
500     egl_dmabuf_import_texture(dmabuf);
501     texture = qemu_dmabuf_get_texture(dmabuf);
502     if (!texture) {
503         return;
504     }
505 
506     width = qemu_dmabuf_get_width(dmabuf);
507     height = qemu_dmabuf_get_height(dmabuf);
508 
509     egl_fb_setup_for_tex(&cursor_fb, width, height, texture, false);
510     ds = qemu_create_displaysurface(width, height);
511     egl_fb_read(ds, &cursor_fb);
512 
513     v_data = g_variant_new_from_data(
514         G_VARIANT_TYPE("ay"),
515         surface_data(ds),
516         surface_width(ds) * surface_height(ds) * 4,
517         TRUE,
518         (GDestroyNotify)qemu_free_displaysurface,
519         ds);
520     qemu_dbus_display1_listener_call_cursor_define(
521         ddl->proxy,
522         surface_width(ds),
523         surface_height(ds),
524         hot_x,
525         hot_y,
526         v_data,
527         G_DBUS_CALL_FLAGS_NONE,
528         -1,
529         NULL,
530         NULL,
531         NULL);
532 }
533 
dbus_release_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf)534 static void dbus_release_dmabuf(DisplayChangeListener *dcl,
535                                 QemuDmaBuf *dmabuf)
536 {
537     dbus_scanout_disable(dcl);
538 }
539 #endif /* GBM */
540 
dbus_gl_cursor_position(DisplayChangeListener * dcl,uint32_t pos_x,uint32_t pos_y)541 static void dbus_gl_cursor_position(DisplayChangeListener *dcl,
542                                  uint32_t pos_x, uint32_t pos_y)
543 {
544     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
545 
546     qemu_dbus_display1_listener_call_mouse_set(
547         ddl->proxy, pos_x, pos_y, true,
548         G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
549 }
550 
dbus_scanout_update(DisplayChangeListener * dcl,uint32_t x,uint32_t y,uint32_t w,uint32_t h)551 static void dbus_scanout_update(DisplayChangeListener *dcl,
552                                 uint32_t x, uint32_t y,
553                                 uint32_t w, uint32_t h)
554 {
555     dbus_call_update_gl(dcl, x, y, w, h);
556 }
557 
dbus_gl_refresh(DisplayChangeListener * dcl)558 static void dbus_gl_refresh(DisplayChangeListener *dcl)
559 {
560     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
561 
562     graphic_hw_update(dcl->con);
563 
564     if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) {
565         return;
566     }
567 
568 #ifdef CONFIG_PIXMAN
569     int n_rects = pixman_region32_n_rects(&ddl->gl_damage);
570 
571     for (int i = 0; i < n_rects; i++) {
572         pixman_box32_t *box;
573         box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i;
574         /* TODO: Add a UpdateList call to send multiple updates at once */
575         dbus_call_update_gl(dcl, box->x1, box->y1,
576                             box->x2 - box->x1, box->y2 - box->y1);
577     }
578     pixman_region32_clear(&ddl->gl_damage);
579 #else
580     if (ddl->gl_damage) {
581         dbus_call_update_gl(dcl, 0, 0,
582                             surface_width(ddl->ds), surface_height(ddl->ds));
583         ddl->gl_damage = 0;
584     }
585 #endif
586 }
587 #endif /* OPENGL */
588 
dbus_refresh(DisplayChangeListener * dcl)589 static void dbus_refresh(DisplayChangeListener *dcl)
590 {
591     graphic_hw_update(dcl->con);
592 }
593 
594 #ifdef CONFIG_OPENGL
dbus_gl_gfx_update(DisplayChangeListener * dcl,int x,int y,int w,int h)595 static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
596                                int x, int y, int w, int h)
597 {
598     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
599 
600 #ifdef CONFIG_PIXMAN
601     pixman_region32_t rect_region;
602     pixman_region32_init_rect(&rect_region, x, y, w, h);
603     pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region);
604     pixman_region32_fini(&rect_region);
605 #else
606     ddl->gl_damage++;
607 #endif
608 }
609 #endif
610 
dbus_gfx_update_sub(DBusDisplayListener * ddl,int x,int y,int w,int h)611 static void dbus_gfx_update_sub(DBusDisplayListener *ddl,
612                                 int x, int y, int w, int h)
613 {
614     pixman_image_t *img;
615     size_t stride;
616     GVariant *v_data;
617 
618     /* make a copy, since gvariant only handles linear data */
619     stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
620     img = pixman_image_create_bits(surface_format(ddl->ds),
621                                    w, h, NULL, stride);
622 #ifdef CONFIG_PIXMAN
623     pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img,
624                            x, y, 0, 0, 0, 0, w, h);
625 #else
626     {
627         uint8_t *src = (uint8_t *)pixman_image_get_data(ddl->ds->image);
628         uint8_t *dst = (uint8_t *)pixman_image_get_data(img);
629         int bp = PIXMAN_FORMAT_BPP(surface_format(ddl->ds)) / 8;
630         int hh;
631 
632         for (hh = 0; hh < h; hh++) {
633             memcpy(&dst[stride * hh],
634                    &src[surface_stride(ddl->ds) * (hh + y) + x * bp],
635                    stride);
636         }
637     }
638 #endif
639     v_data = g_variant_new_from_data(
640         G_VARIANT_TYPE("ay"),
641         pixman_image_get_data(img),
642         pixman_image_get_stride(img) * h,
643         TRUE,
644         (GDestroyNotify)pixman_image_unref,
645         img);
646     qemu_dbus_display1_listener_call_update(ddl->proxy,
647         x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img),
648         v_data,
649         G_DBUS_CALL_FLAGS_NONE,
650         DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
651 }
652 
ddl_scanout(DBusDisplayListener * ddl)653 static void ddl_scanout(DBusDisplayListener *ddl)
654 {
655     GVariant *v_data;
656 
657     v_data = g_variant_new_from_data(
658         G_VARIANT_TYPE("ay"), surface_data(ddl->ds),
659         surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE,
660         (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image));
661 
662     ddl_discard_pending_messages(ddl);
663 
664     qemu_dbus_display1_listener_call_scanout(
665         ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds),
666         surface_stride(ddl->ds), surface_format(ddl->ds), v_data,
667         G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL,
668         g_object_ref(ddl));
669 }
670 
dbus_gfx_update(DisplayChangeListener * dcl,int x,int y,int w,int h)671 static void dbus_gfx_update(DisplayChangeListener *dcl,
672                             int x, int y, int w, int h)
673 {
674     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
675 
676     assert(ddl->ds);
677 
678     trace_dbus_update(x, y, w, h);
679 
680 #ifdef WIN32
681     if (dbus_scanout_map(ddl)) {
682         qemu_dbus_display1_listener_win32_map_call_update_map(
683             ddl->map_proxy,
684             x, y, w, h,
685             G_DBUS_CALL_FLAGS_NONE,
686             DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
687         return;
688     }
689 #endif
690 
691     if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
692         return ddl_scanout(ddl);
693     }
694 
695     dbus_gfx_update_sub(ddl, x, y, w, h);
696 }
697 
698 #ifdef CONFIG_OPENGL
dbus_gl_gfx_switch(DisplayChangeListener * dcl,struct DisplaySurface * new_surface)699 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
700                                struct DisplaySurface *new_surface)
701 {
702     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
703 
704     trace_dbus_gl_gfx_switch(new_surface);
705 
706     ddl->ds = new_surface;
707     ddl->ds_share = SHARE_KIND_NONE;
708     if (ddl->ds) {
709         int width = surface_width(ddl->ds);
710         int height = surface_height(ddl->ds);
711 
712         /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
713         dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
714                              width, height, 0, 0, width, height, NULL);
715     }
716 }
717 #endif
718 
dbus_gfx_switch(DisplayChangeListener * dcl,struct DisplaySurface * new_surface)719 static void dbus_gfx_switch(DisplayChangeListener *dcl,
720                             struct DisplaySurface *new_surface)
721 {
722     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
723 
724     ddl->ds = new_surface;
725     ddl->ds_share = SHARE_KIND_NONE;
726 }
727 
dbus_mouse_set(DisplayChangeListener * dcl,int x,int y,int on)728 static void dbus_mouse_set(DisplayChangeListener *dcl,
729                            int x, int y, int on)
730 {
731     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
732 
733     qemu_dbus_display1_listener_call_mouse_set(
734         ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
735 }
736 
dbus_cursor_define(DisplayChangeListener * dcl,QEMUCursor * c)737 static void dbus_cursor_define(DisplayChangeListener *dcl,
738                                QEMUCursor *c)
739 {
740     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
741     GVariant *v_data = NULL;
742 
743     v_data = g_variant_new_from_data(
744         G_VARIANT_TYPE("ay"),
745         c->data,
746         c->width * c->height * 4,
747         TRUE,
748         (GDestroyNotify)cursor_unref,
749         cursor_ref(c));
750 
751     qemu_dbus_display1_listener_call_cursor_define(
752         ddl->proxy,
753         c->width,
754         c->height,
755         c->hot_x,
756         c->hot_y,
757         v_data,
758         G_DBUS_CALL_FLAGS_NONE,
759         -1,
760         NULL,
761         NULL,
762         NULL);
763 }
764 
765 #ifdef CONFIG_OPENGL
766 const DisplayChangeListenerOps dbus_gl_dcl_ops = {
767     .dpy_name                = "dbus-gl",
768     .dpy_gfx_update          = dbus_gl_gfx_update,
769     .dpy_gfx_switch          = dbus_gl_gfx_switch,
770     .dpy_gfx_check_format    = console_gl_check_format,
771     .dpy_refresh             = dbus_gl_refresh,
772     .dpy_mouse_set           = dbus_mouse_set,
773     .dpy_cursor_define       = dbus_cursor_define,
774 
775     .dpy_gl_scanout_disable  = dbus_scanout_disable,
776     .dpy_gl_scanout_texture  = dbus_scanout_texture,
777 #ifdef CONFIG_GBM
778     .dpy_gl_scanout_dmabuf   = dbus_scanout_dmabuf,
779     .dpy_gl_cursor_dmabuf    = dbus_cursor_dmabuf,
780     .dpy_gl_release_dmabuf   = dbus_release_dmabuf,
781 #endif
782     .dpy_gl_cursor_position  = dbus_gl_cursor_position,
783     .dpy_gl_update           = dbus_scanout_update,
784 };
785 #endif
786 
787 const DisplayChangeListenerOps dbus_dcl_ops = {
788     .dpy_name                = "dbus",
789     .dpy_gfx_update          = dbus_gfx_update,
790     .dpy_gfx_switch          = dbus_gfx_switch,
791     .dpy_refresh             = dbus_refresh,
792     .dpy_mouse_set           = dbus_mouse_set,
793     .dpy_cursor_define       = dbus_cursor_define,
794 };
795 
796 static void
dbus_display_listener_dispose(GObject * object)797 dbus_display_listener_dispose(GObject *object)
798 {
799     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
800 
801     unregister_displaychangelistener(&ddl->dcl);
802     g_clear_object(&ddl->conn);
803     g_clear_pointer(&ddl->bus_name, g_free);
804     g_clear_object(&ddl->proxy);
805 #ifdef WIN32
806     g_clear_object(&ddl->map_proxy);
807     g_clear_object(&ddl->d3d11_proxy);
808     g_clear_pointer(&ddl->peer_process, CloseHandle);
809 #ifdef CONFIG_PIXMAN
810     pixman_region32_fini(&ddl->gl_damage);
811 #endif
812 #ifdef CONFIG_OPENGL
813     egl_fb_destroy(&ddl->fb);
814 #endif
815 #endif
816 
817     G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
818 }
819 
820 static void
dbus_display_listener_constructed(GObject * object)821 dbus_display_listener_constructed(GObject *object)
822 {
823     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
824 
825     ddl->dcl.ops = &dbus_dcl_ops;
826 #ifdef CONFIG_OPENGL
827     if (display_opengl) {
828         ddl->dcl.ops = &dbus_gl_dcl_ops;
829     }
830 #endif
831 
832     G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object);
833 }
834 
835 static void
dbus_display_listener_class_init(DBusDisplayListenerClass * klass)836 dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
837 {
838     GObjectClass *object_class = G_OBJECT_CLASS(klass);
839 
840     object_class->dispose = dbus_display_listener_dispose;
841     object_class->constructed = dbus_display_listener_constructed;
842 }
843 
844 static void
dbus_display_listener_init(DBusDisplayListener * ddl)845 dbus_display_listener_init(DBusDisplayListener *ddl)
846 {
847 #ifdef CONFIG_PIXMAN
848     pixman_region32_init(&ddl->gl_damage);
849 #endif
850 }
851 
852 const char *
dbus_display_listener_get_bus_name(DBusDisplayListener * ddl)853 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl)
854 {
855     return ddl->bus_name ?: "p2p";
856 }
857 
858 DBusDisplayConsole *
dbus_display_listener_get_console(DBusDisplayListener * ddl)859 dbus_display_listener_get_console(DBusDisplayListener *ddl)
860 {
861     return ddl->console;
862 }
863 
864 #ifdef WIN32
865 static bool
dbus_display_listener_implements(DBusDisplayListener * ddl,const char * iface)866 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface)
867 {
868     QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy);
869     bool implements;
870 
871     implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface);
872     if (!implements) {
873         g_debug("Display listener does not implement: `%s`", iface);
874     }
875 
876     return implements;
877 }
878 
879 static bool
dbus_display_listener_setup_peer_process(DBusDisplayListener * ddl)880 dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl)
881 {
882     g_autoptr(GError) err = NULL;
883     GDBusConnection *conn;
884     GIOStream *stream;
885     GSocket *sock;
886     g_autoptr(GCredentials) creds = NULL;
887     DWORD *pid;
888 
889     if (ddl->peer_process) {
890         return true;
891     }
892 
893     conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy));
894     stream = g_dbus_connection_get_stream(conn);
895 
896     if (!G_IS_UNIX_CONNECTION(stream)) {
897         return false;
898     }
899 
900     sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream));
901     creds = g_socket_get_credentials(sock, &err);
902 
903     if (!creds) {
904         g_debug("Failed to get peer credentials: %s", err->message);
905         return false;
906     }
907 
908     pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID);
909 
910     if (pid == NULL) {
911         g_debug("Failed to get peer PID");
912         return false;
913     }
914 
915     ddl->peer_process = OpenProcess(
916         PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
917         false, *pid);
918 
919     if (!ddl->peer_process) {
920         g_autofree char *msg = g_win32_error_message(GetLastError());
921         g_debug("Failed to OpenProcess: %s", msg);
922         return false;
923     }
924 
925     return true;
926 }
927 #endif
928 
929 static void
dbus_display_listener_setup_d3d11(DBusDisplayListener * ddl)930 dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl)
931 {
932 #ifdef WIN32
933     g_autoptr(GError) err = NULL;
934 
935     if (!dbus_display_listener_implements(ddl,
936             "org.qemu.Display1.Listener.Win32.D3d11")) {
937         return;
938     }
939 
940     if (!dbus_display_listener_setup_peer_process(ddl)) {
941         return;
942     }
943 
944     ddl->d3d11_proxy =
945         qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn,
946             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
947             NULL,
948             "/org/qemu/Display1/Listener",
949             NULL,
950             &err);
951     if (!ddl->d3d11_proxy) {
952         g_debug("Failed to setup win32 d3d11 proxy: %s", err->message);
953         return;
954     }
955 #endif
956 }
957 
958 static void
dbus_display_listener_setup_shared_map(DBusDisplayListener * ddl)959 dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl)
960 {
961 #ifdef WIN32
962     g_autoptr(GError) err = NULL;
963 
964     if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) {
965         return;
966     }
967 
968     if (!dbus_display_listener_setup_peer_process(ddl)) {
969         return;
970     }
971 
972     ddl->map_proxy =
973         qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn,
974             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
975             NULL,
976             "/org/qemu/Display1/Listener",
977             NULL,
978             &err);
979     if (!ddl->map_proxy) {
980         g_debug("Failed to setup win32 map proxy: %s", err->message);
981         return;
982     }
983 
984     ddl->can_share_map = true;
985 #endif
986 }
987 
988 static GDBusMessage *
dbus_filter(GDBusConnection * connection,GDBusMessage * message,gboolean incoming,gpointer user_data)989 dbus_filter(GDBusConnection *connection,
990             GDBusMessage    *message,
991             gboolean         incoming,
992             gpointer         user_data)
993 {
994     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data);
995     guint32 serial;
996 
997     if (incoming) {
998         return message;
999     }
1000 
1001     serial = g_dbus_message_get_serial(message);
1002     if (serial <= ddl->out_serial_to_discard) {
1003         trace_dbus_filter(serial, ddl->out_serial_to_discard);
1004         return NULL;
1005     }
1006 
1007     return message;
1008 }
1009 
1010 DBusDisplayListener *
dbus_display_listener_new(const char * bus_name,GDBusConnection * conn,DBusDisplayConsole * console)1011 dbus_display_listener_new(const char *bus_name,
1012                           GDBusConnection *conn,
1013                           DBusDisplayConsole *console)
1014 {
1015     DBusDisplayListener *ddl;
1016     QemuConsole *con;
1017     g_autoptr(GError) err = NULL;
1018 
1019     ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL);
1020     ddl->proxy =
1021         qemu_dbus_display1_listener_proxy_new_sync(conn,
1022             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
1023             NULL,
1024             "/org/qemu/Display1/Listener",
1025             NULL,
1026             &err);
1027     if (!ddl->proxy) {
1028         error_report("Failed to setup proxy: %s", err->message);
1029         g_object_unref(conn);
1030         g_object_unref(ddl);
1031         return NULL;
1032     }
1033 
1034     ddl->dbus_filter = g_dbus_connection_add_filter(conn, dbus_filter, g_object_ref(ddl), g_object_unref);
1035     ddl->bus_name = g_strdup(bus_name);
1036     ddl->conn = conn;
1037     ddl->console = console;
1038 
1039     dbus_display_listener_setup_shared_map(ddl);
1040     dbus_display_listener_setup_d3d11(ddl);
1041 
1042     con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
1043     assert(con);
1044     ddl->dcl.con = con;
1045     register_displaychangelistener(&ddl->dcl);
1046 
1047     return ddl;
1048 }
1049