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 "system/system.h"
28 #include "dbus.h"
29 #include "glib.h"
30 #ifdef G_OS_UNIX
31 #include <gio/gunixfdlist.h>
32 #endif
33 #ifdef WIN32
34 #include <d3d11.h>
35 #include <dxgi1_2.h>
36 #endif
37
38 #ifdef CONFIG_OPENGL
39 #include "ui/shader.h"
40 #include "ui/egl-helpers.h"
41 #include "ui/egl-context.h"
42 #include "ui/qemu-pixman.h"
43 #endif
44 #include "trace.h"
45
46 static void dbus_gfx_switch(DisplayChangeListener *dcl,
47 struct DisplaySurface *new_surface);
48
49 enum share_kind {
50 SHARE_KIND_NONE,
51 SHARE_KIND_MAPPED,
52 SHARE_KIND_D3DTEX,
53 };
54
55 struct _DBusDisplayListener {
56 GObject parent;
57
58 char *bus_name;
59 DBusDisplayConsole *console;
60 GDBusConnection *conn;
61
62 QemuDBusDisplay1Listener *proxy;
63
64 #ifdef CONFIG_PIXMAN
65 /* Keep track of the damage region */
66 pixman_region32_t gl_damage;
67 #else
68 int gl_damage;
69 #endif
70
71 DisplayChangeListener dcl;
72 DisplaySurface *ds;
73 enum share_kind ds_share;
74
75 bool ds_mapped;
76 bool can_share_map;
77
78 #ifdef WIN32
79 QemuDBusDisplay1ListenerWin32Map *map_proxy;
80 QemuDBusDisplay1ListenerWin32D3d11 *d3d11_proxy;
81 HANDLE peer_process;
82 ID3D11Texture2D *d3d_texture;
83 #ifdef CONFIG_OPENGL
84 egl_fb fb;
85 #endif
86 #else /* !WIN32 */
87 QemuDBusDisplay1ListenerUnixMap *map_proxy;
88 QemuDBusDisplay1ListenerUnixScanoutDMABUF2 *scanout_dmabuf_v2_proxy;
89 #endif
90
91 guint dbus_filter;
92 guint32 display_serial_to_discard;
93 guint32 cursor_serial_to_discard;
94 };
95
96 G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
97
98 static void dbus_gfx_update(DisplayChangeListener *dcl,
99 int x, int y, int w, int h);
100
ddl_discard_display_messages(DBusDisplayListener * ddl)101 static void ddl_discard_display_messages(DBusDisplayListener *ddl)
102 {
103 guint32 serial = g_dbus_connection_get_last_serial(
104 g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)));
105
106 g_atomic_int_set(&ddl->display_serial_to_discard, serial);
107 }
108
ddl_discard_cursor_messages(DBusDisplayListener * ddl)109 static void ddl_discard_cursor_messages(DBusDisplayListener *ddl)
110 {
111 guint32 serial = g_dbus_connection_get_last_serial(
112 g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)));
113
114 g_atomic_int_set(&ddl->cursor_serial_to_discard, serial);
115 }
116
117 #ifdef CONFIG_OPENGL
dbus_scanout_disable(DisplayChangeListener * dcl)118 static void dbus_scanout_disable(DisplayChangeListener *dcl)
119 {
120 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
121
122 ddl_discard_display_messages(ddl);
123
124 qemu_dbus_display1_listener_call_disable(
125 ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
126 }
127
128 #ifdef WIN32
d3d_texture2d_share(ID3D11Texture2D * d3d_texture,HANDLE * handle,Error ** errp)129 static bool d3d_texture2d_share(ID3D11Texture2D *d3d_texture,
130 HANDLE *handle, Error **errp)
131 {
132 IDXGIResource1 *dxgiResource = NULL;
133 HRESULT hr;
134
135 hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
136 &IID_IDXGIResource1,
137 (void **)&dxgiResource);
138 if (FAILED(hr)) {
139 goto fail;
140 }
141
142 hr = dxgiResource->lpVtbl->CreateSharedHandle(
143 dxgiResource,
144 NULL,
145 DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
146 NULL,
147 handle
148 );
149
150 dxgiResource->lpVtbl->Release(dxgiResource);
151
152 if (SUCCEEDED(hr)) {
153 return true;
154 }
155
156 fail:
157 error_setg_win32(errp, GetLastError(), "failed to create shared handle");
158 return false;
159 }
160
d3d_texture2d_acquire0(ID3D11Texture2D * d3d_texture,Error ** errp)161 static bool d3d_texture2d_acquire0(ID3D11Texture2D *d3d_texture, Error **errp)
162 {
163 IDXGIKeyedMutex *dxgiMutex = NULL;
164 HRESULT hr;
165
166 hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
167 &IID_IDXGIKeyedMutex,
168 (void **)&dxgiMutex);
169 if (FAILED(hr)) {
170 goto fail;
171 }
172
173 hr = dxgiMutex->lpVtbl->AcquireSync(dxgiMutex, 0, INFINITE);
174
175 dxgiMutex->lpVtbl->Release(dxgiMutex);
176
177 if (SUCCEEDED(hr)) {
178 return true;
179 }
180
181 fail:
182 error_setg_win32(errp, GetLastError(), "failed to acquire texture mutex");
183 return false;
184 }
185
d3d_texture2d_release0(ID3D11Texture2D * d3d_texture,Error ** errp)186 static bool d3d_texture2d_release0(ID3D11Texture2D *d3d_texture, Error **errp)
187 {
188 IDXGIKeyedMutex *dxgiMutex = NULL;
189 HRESULT hr;
190
191 hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
192 &IID_IDXGIKeyedMutex,
193 (void **)&dxgiMutex);
194 if (FAILED(hr)) {
195 goto fail;
196 }
197
198 hr = dxgiMutex->lpVtbl->ReleaseSync(dxgiMutex, 0);
199
200 dxgiMutex->lpVtbl->Release(dxgiMutex);
201
202 if (SUCCEEDED(hr)) {
203 return true;
204 }
205
206 fail:
207 error_setg_win32(errp, GetLastError(), "failed to release texture mutex");
208 return false;
209 }
210 #endif /* WIN32 */
211
212 #if defined(CONFIG_GBM) || defined(WIN32)
dbus_update_gl_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)213 static void dbus_update_gl_cb(GObject *source_object,
214 GAsyncResult *res,
215 gpointer user_data)
216 {
217 g_autoptr(GError) err = NULL;
218 DBusDisplayListener *ddl = user_data;
219 bool success;
220
221 #ifdef CONFIG_GBM
222 success = qemu_dbus_display1_listener_call_update_dmabuf_finish(
223 ddl->proxy, res, &err);
224 #endif
225
226 #ifdef WIN32
227 success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish(
228 ddl->d3d11_proxy, res, &err);
229 d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn);
230 #endif
231
232 if (!success) {
233 error_report("Failed to call update: %s", err->message);
234 }
235
236 graphic_hw_gl_block(ddl->dcl.con, false);
237 g_object_unref(ddl);
238 }
239 #endif
240
dbus_call_update_gl(DisplayChangeListener * dcl,int x,int y,int w,int h)241 static void dbus_call_update_gl(DisplayChangeListener *dcl,
242 int x, int y, int w, int h)
243 {
244 #if defined(CONFIG_GBM) || defined(WIN32)
245 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
246 #endif
247
248 trace_dbus_update_gl(x, y, w, h);
249
250 glFlush();
251 #ifdef CONFIG_GBM
252 graphic_hw_gl_block(ddl->dcl.con, true);
253 qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy,
254 x, y, w, h,
255 G_DBUS_CALL_FLAGS_NONE,
256 DBUS_DEFAULT_TIMEOUT, NULL,
257 dbus_update_gl_cb,
258 g_object_ref(ddl));
259 #endif
260
261 #ifdef WIN32
262 switch (ddl->ds_share) {
263 case SHARE_KIND_MAPPED:
264 egl_fb_read_rect(ddl->ds, &ddl->fb, x, y, w, h);
265 dbus_gfx_update(dcl, x, y, w, h);
266 break;
267 case SHARE_KIND_D3DTEX: {
268 Error *err = NULL;
269 assert(ddl->d3d_texture);
270
271 graphic_hw_gl_block(ddl->dcl.con, true);
272 if (!d3d_texture2d_release0(ddl->d3d_texture, &err)) {
273 error_report_err(err);
274 return;
275 }
276 qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d(
277 ddl->d3d11_proxy,
278 x, y, w, h,
279 G_DBUS_CALL_FLAGS_NONE,
280 DBUS_DEFAULT_TIMEOUT, NULL,
281 dbus_update_gl_cb,
282 g_object_ref(ddl));
283 break;
284 }
285 default:
286 g_warn_if_reached();
287 }
288 #endif
289 }
290
291 #ifdef CONFIG_GBM
dbus_scanout_dmabuf_v1(DBusDisplayListener * ddl,QemuDmaBuf * dmabuf)292 static void dbus_scanout_dmabuf_v1(DBusDisplayListener *ddl,
293 QemuDmaBuf *dmabuf)
294 {
295 g_autoptr(GError) err = NULL;
296 g_autoptr(GUnixFDList) fd_list = NULL;
297 int fd;
298 uint32_t width, height, stride, fourcc;
299 uint64_t modifier;
300 bool y0_top;
301
302 fd = qemu_dmabuf_get_fds(dmabuf, NULL)[0];
303 fd_list = g_unix_fd_list_new();
304 if (g_unix_fd_list_append(fd_list, fd, &err) != 0) {
305 error_report("Failed to setup dmabuf fdlist: %s", err->message);
306 return;
307 }
308
309 ddl_discard_display_messages(ddl);
310
311 width = qemu_dmabuf_get_width(dmabuf);
312 height = qemu_dmabuf_get_height(dmabuf);
313 stride = qemu_dmabuf_get_strides(dmabuf, NULL)[0];
314 fourcc = qemu_dmabuf_get_fourcc(dmabuf);
315 modifier = qemu_dmabuf_get_modifier(dmabuf);
316 y0_top = qemu_dmabuf_get_y0_top(dmabuf);
317
318 /* FIXME: add missing x/y/w/h support */
319 qemu_dbus_display1_listener_call_scanout_dmabuf(
320 ddl->proxy, g_variant_new_handle(0),
321 width, height, stride, fourcc, modifier,
322 y0_top, G_DBUS_CALL_FLAGS_NONE,
323 -1, fd_list, NULL, NULL, NULL);
324 }
325
dbus_scanout_dmabuf_v2(DBusDisplayListener * ddl,QemuDmaBuf * dmabuf)326 static void dbus_scanout_dmabuf_v2(DBusDisplayListener *ddl,
327 QemuDmaBuf *dmabuf)
328 {
329 g_autoptr(GError) err = NULL;
330 g_autoptr(GUnixFDList) fd_list = NULL;
331 int i, fd_index[DMABUF_MAX_PLANES], num_fds;
332 uint32_t x, y, width, height, fourcc, backing_width, backing_height;
333 GVariant *fd, *offset, *stride, *fd_handles[DMABUF_MAX_PLANES];
334 uint64_t modifier;
335 bool y0_top;
336 int nfds, noffsets, nstrides;
337 const int *fds = qemu_dmabuf_get_fds(dmabuf, &nfds);
338 const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets);
339 const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides);
340 uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf);
341
342 assert(nfds >= num_planes);
343 assert(noffsets >= num_planes);
344 assert(nstrides >= num_planes);
345
346 fd_list = g_unix_fd_list_new();
347
348 for (num_fds = 0; num_fds < num_planes; num_fds++) {
349 int plane_fd = fds[num_fds];
350
351 if (plane_fd < 0) {
352 break;
353 }
354
355 fd_index[num_fds] = g_unix_fd_list_append(fd_list, plane_fd, &err);
356 if (fd_index[num_fds] < 0) {
357 error_report("Failed to setup dmabuf fdlist: %s", err->message);
358 return;
359 }
360 }
361
362 ddl_discard_display_messages(ddl);
363
364 x = qemu_dmabuf_get_x(dmabuf);
365 y = qemu_dmabuf_get_y(dmabuf);
366 width = qemu_dmabuf_get_width(dmabuf);
367 height = qemu_dmabuf_get_height(dmabuf);
368 fourcc = qemu_dmabuf_get_fourcc(dmabuf);
369 backing_width = qemu_dmabuf_get_backing_width(dmabuf);
370 backing_height = qemu_dmabuf_get_backing_height(dmabuf);
371 modifier = qemu_dmabuf_get_modifier(dmabuf);
372 y0_top = qemu_dmabuf_get_y0_top(dmabuf);
373
374 offset = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
375 offsets, num_planes, sizeof(uint32_t));
376 stride = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
377 strides, num_planes, sizeof(uint32_t));
378
379 for (i = 0; i < num_fds; i++) {
380 fd_handles[i] = g_variant_new_handle(fd_index[i]);
381 }
382 fd = g_variant_new_array(G_VARIANT_TYPE_HANDLE, fd_handles, num_fds);
383
384 qemu_dbus_display1_listener_unix_scanout_dmabuf2_call_scanout_dmabuf2(
385 ddl->scanout_dmabuf_v2_proxy, fd, x, y, width, height, offset, stride,
386 num_planes, fourcc, backing_width, backing_height, modifier, y0_top,
387 G_DBUS_CALL_FLAGS_NONE, -1, fd_list, NULL, NULL, NULL);
388 }
389
dbus_scanout_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf)390 static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
391 QemuDmaBuf *dmabuf)
392 {
393 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
394
395 if (ddl->scanout_dmabuf_v2_proxy) {
396 dbus_scanout_dmabuf_v2(ddl, dmabuf);
397 } else {
398 if (qemu_dmabuf_get_num_planes(dmabuf) > 1) {
399 g_debug("org.qemu.Display1.Listener.ScanoutDMABUF "
400 "does not support mutli plane");
401 return;
402 }
403 dbus_scanout_dmabuf_v1(ddl, dmabuf);
404 }
405 }
406 #endif /* GBM */
407 #endif /* OPENGL */
408
409 #ifdef WIN32
dbus_scanout_map(DBusDisplayListener * ddl)410 static bool dbus_scanout_map(DBusDisplayListener *ddl)
411 {
412 g_autoptr(GError) err = NULL;
413 BOOL success;
414 HANDLE target_handle;
415
416 if (ddl->ds_share == SHARE_KIND_MAPPED) {
417 return true;
418 }
419
420 if (!ddl->can_share_map || !ddl->ds->share_handle) {
421 return false;
422 }
423
424 success = DuplicateHandle(
425 GetCurrentProcess(),
426 ddl->ds->share_handle,
427 ddl->peer_process,
428 &target_handle,
429 FILE_MAP_READ | SECTION_QUERY,
430 FALSE, 0);
431 if (!success) {
432 g_autofree char *msg = g_win32_error_message(GetLastError());
433 g_debug("Failed to DuplicateHandle: %s", msg);
434 ddl->can_share_map = false;
435 return false;
436 }
437
438 ddl_discard_display_messages(ddl);
439
440 if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync(
441 ddl->map_proxy,
442 GPOINTER_TO_UINT(target_handle),
443 ddl->ds->share_handle_offset,
444 surface_width(ddl->ds),
445 surface_height(ddl->ds),
446 surface_stride(ddl->ds),
447 surface_format(ddl->ds),
448 G_DBUS_CALL_FLAGS_NONE,
449 DBUS_DEFAULT_TIMEOUT,
450 NULL,
451 &err)) {
452 g_debug("Failed to call ScanoutMap: %s", err->message);
453 ddl->can_share_map = false;
454 return false;
455 }
456
457 ddl->ds_share = SHARE_KIND_MAPPED;
458
459 return true;
460 }
461
462 #ifdef CONFIG_OPENGL
463 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)464 dbus_scanout_share_d3d_texture(
465 DBusDisplayListener *ddl,
466 ID3D11Texture2D *tex,
467 bool backing_y_0_top,
468 uint32_t backing_width,
469 uint32_t backing_height,
470 uint32_t x, uint32_t y,
471 uint32_t w, uint32_t h)
472 {
473 Error *err = NULL;
474 BOOL success;
475 HANDLE share_handle, target_handle;
476
477 if (!d3d_texture2d_release0(tex, &err)) {
478 error_report_err(err);
479 return false;
480 }
481
482 if (!d3d_texture2d_share(tex, &share_handle, &err)) {
483 error_report_err(err);
484 return false;
485 }
486
487 success = DuplicateHandle(
488 GetCurrentProcess(),
489 share_handle,
490 ddl->peer_process,
491 &target_handle,
492 0,
493 FALSE, DUPLICATE_SAME_ACCESS);
494 if (!success) {
495 g_autofree char *msg = g_win32_error_message(GetLastError());
496 g_debug("Failed to DuplicateHandle: %s", msg);
497 CloseHandle(share_handle);
498 return false;
499 }
500
501 ddl_discard_display_messages(ddl);
502
503 qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d(
504 ddl->d3d11_proxy,
505 GPOINTER_TO_INT(target_handle),
506 backing_width,
507 backing_height,
508 backing_y_0_top,
509 x, y, w, h,
510 G_DBUS_CALL_FLAGS_NONE,
511 -1,
512 NULL, NULL, NULL);
513
514 CloseHandle(share_handle);
515
516 if (!d3d_texture2d_acquire0(tex, &err)) {
517 error_report_err(err);
518 return false;
519 }
520
521 ddl->d3d_texture = tex;
522 ddl->ds_share = SHARE_KIND_D3DTEX;
523
524 return true;
525 }
526 #endif /* CONFIG_OPENGL */
527 #else /* !WIN32 */
dbus_scanout_map(DBusDisplayListener * ddl)528 static bool dbus_scanout_map(DBusDisplayListener *ddl)
529 {
530 g_autoptr(GError) err = NULL;
531 g_autoptr(GUnixFDList) fd_list = NULL;
532
533 if (ddl->ds_share == SHARE_KIND_MAPPED) {
534 return true;
535 }
536
537 if (!ddl->can_share_map || ddl->ds->share_handle == SHAREABLE_NONE) {
538 return false;
539 }
540
541 ddl_discard_display_messages(ddl);
542 fd_list = g_unix_fd_list_new();
543 if (g_unix_fd_list_append(fd_list, ddl->ds->share_handle, &err) != 0) {
544 g_debug("Failed to setup scanout map fdlist: %s", err->message);
545 ddl->can_share_map = false;
546 return false;
547 }
548
549 if (!qemu_dbus_display1_listener_unix_map_call_scanout_map_sync(
550 ddl->map_proxy,
551 g_variant_new_handle(0),
552 ddl->ds->share_handle_offset,
553 surface_width(ddl->ds),
554 surface_height(ddl->ds),
555 surface_stride(ddl->ds),
556 surface_format(ddl->ds),
557 G_DBUS_CALL_FLAGS_NONE,
558 DBUS_DEFAULT_TIMEOUT,
559 fd_list,
560 NULL,
561 NULL,
562 &err)) {
563 g_debug("Failed to call ScanoutMap: %s", err->message);
564 ddl->can_share_map = false;
565 return false;
566 }
567
568 ddl->ds_share = SHARE_KIND_MAPPED;
569
570 return true;
571 }
572 #endif /* WIN32 */
573
574 #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)575 static void dbus_scanout_texture(DisplayChangeListener *dcl,
576 uint32_t tex_id,
577 bool backing_y_0_top,
578 uint32_t backing_width,
579 uint32_t backing_height,
580 uint32_t x, uint32_t y,
581 uint32_t w, uint32_t h,
582 void *d3d_tex2d)
583 {
584 trace_dbus_scanout_texture(tex_id, backing_y_0_top,
585 backing_width, backing_height, x, y, w, h);
586 #ifdef CONFIG_GBM
587 g_autoptr(QemuDmaBuf) dmabuf = NULL;
588 int fd[DMABUF_MAX_PLANES], num_planes;
589 uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc;
590 uint64_t modifier;
591
592 assert(tex_id);
593 if (!egl_dmabuf_export_texture(tex_id, fd, (EGLint *)offset, (EGLint *)stride,
594 (EGLint *)&fourcc, &num_planes, &modifier)) {
595 error_report("%s: failed to export dmabuf for texture", __func__);
596 return;
597 }
598 dmabuf = qemu_dmabuf_new(w, h, offset, stride, x, y, backing_width,
599 backing_height, fourcc, modifier, fd, num_planes,
600 false, backing_y_0_top);
601
602 dbus_scanout_dmabuf(dcl, dmabuf);
603 qemu_dmabuf_close(dmabuf);
604 #endif
605
606 #ifdef WIN32
607 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
608
609 /* there must be a matching gfx_switch before */
610 assert(surface_width(ddl->ds) == w);
611 assert(surface_height(ddl->ds) == h);
612
613 if (d3d_tex2d) {
614 dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top,
615 backing_width, backing_height, x, y, w, h);
616 } else {
617 dbus_scanout_map(ddl);
618 egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false);
619 }
620 #endif
621 }
622
623 #ifdef CONFIG_GBM
dbus_cursor_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf,bool have_hot,uint32_t hot_x,uint32_t hot_y)624 static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
625 QemuDmaBuf *dmabuf, bool have_hot,
626 uint32_t hot_x, uint32_t hot_y)
627 {
628 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
629 DisplaySurface *ds;
630 GVariant *v_data = NULL;
631 egl_fb cursor_fb = EGL_FB_INIT;
632 uint32_t width, height, texture;
633
634 if (!dmabuf) {
635 qemu_dbus_display1_listener_call_mouse_set(
636 ddl->proxy, 0, 0, false,
637 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
638 return;
639 }
640
641 ddl_discard_cursor_messages(ddl);
642
643 egl_dmabuf_import_texture(dmabuf);
644 texture = qemu_dmabuf_get_texture(dmabuf);
645 if (!texture) {
646 return;
647 }
648
649 width = qemu_dmabuf_get_width(dmabuf);
650 height = qemu_dmabuf_get_height(dmabuf);
651
652 egl_fb_setup_for_tex(&cursor_fb, width, height, texture, false);
653 ds = qemu_create_displaysurface(width, height);
654 egl_fb_read(ds, &cursor_fb);
655
656 v_data = g_variant_new_from_data(
657 G_VARIANT_TYPE("ay"),
658 surface_data(ds),
659 surface_width(ds) * surface_height(ds) * 4,
660 TRUE,
661 (GDestroyNotify)qemu_free_displaysurface,
662 ds);
663 qemu_dbus_display1_listener_call_cursor_define(
664 ddl->proxy,
665 surface_width(ds),
666 surface_height(ds),
667 hot_x,
668 hot_y,
669 v_data,
670 G_DBUS_CALL_FLAGS_NONE,
671 -1,
672 NULL,
673 NULL,
674 NULL);
675 }
676
dbus_release_dmabuf(DisplayChangeListener * dcl,QemuDmaBuf * dmabuf)677 static void dbus_release_dmabuf(DisplayChangeListener *dcl,
678 QemuDmaBuf *dmabuf)
679 {
680 dbus_scanout_disable(dcl);
681 }
682 #endif /* GBM */
683
dbus_gl_cursor_position(DisplayChangeListener * dcl,uint32_t pos_x,uint32_t pos_y)684 static void dbus_gl_cursor_position(DisplayChangeListener *dcl,
685 uint32_t pos_x, uint32_t pos_y)
686 {
687 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
688
689 qemu_dbus_display1_listener_call_mouse_set(
690 ddl->proxy, pos_x, pos_y, true,
691 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
692 }
693
dbus_scanout_update(DisplayChangeListener * dcl,uint32_t x,uint32_t y,uint32_t w,uint32_t h)694 static void dbus_scanout_update(DisplayChangeListener *dcl,
695 uint32_t x, uint32_t y,
696 uint32_t w, uint32_t h)
697 {
698 dbus_call_update_gl(dcl, x, y, w, h);
699 }
700
dbus_gl_refresh(DisplayChangeListener * dcl)701 static void dbus_gl_refresh(DisplayChangeListener *dcl)
702 {
703 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
704
705 graphic_hw_update(dcl->con);
706
707 if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) {
708 return;
709 }
710
711 #ifdef CONFIG_PIXMAN
712 int n_rects = pixman_region32_n_rects(&ddl->gl_damage);
713
714 for (int i = 0; i < n_rects; i++) {
715 pixman_box32_t *box;
716 box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i;
717 /* TODO: Add a UpdateList call to send multiple updates at once */
718 dbus_call_update_gl(dcl, box->x1, box->y1,
719 box->x2 - box->x1, box->y2 - box->y1);
720 }
721 pixman_region32_clear(&ddl->gl_damage);
722 #else
723 if (ddl->gl_damage) {
724 dbus_call_update_gl(dcl, 0, 0,
725 surface_width(ddl->ds), surface_height(ddl->ds));
726 ddl->gl_damage = 0;
727 }
728 #endif
729 }
730 #endif /* OPENGL */
731
dbus_refresh(DisplayChangeListener * dcl)732 static void dbus_refresh(DisplayChangeListener *dcl)
733 {
734 graphic_hw_update(dcl->con);
735 }
736
737 #ifdef CONFIG_OPENGL
dbus_gl_gfx_update(DisplayChangeListener * dcl,int x,int y,int w,int h)738 static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
739 int x, int y, int w, int h)
740 {
741 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
742
743 #ifdef CONFIG_PIXMAN
744 pixman_region32_t rect_region;
745 pixman_region32_init_rect(&rect_region, x, y, w, h);
746 pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region);
747 pixman_region32_fini(&rect_region);
748 #else
749 ddl->gl_damage++;
750 #endif
751 }
752 #endif
753
dbus_gfx_update_sub(DBusDisplayListener * ddl,int x,int y,int w,int h)754 static void dbus_gfx_update_sub(DBusDisplayListener *ddl,
755 int x, int y, int w, int h)
756 {
757 pixman_image_t *img;
758 size_t stride;
759 GVariant *v_data;
760
761 /* make a copy, since gvariant only handles linear data */
762 stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
763 img = pixman_image_create_bits(surface_format(ddl->ds),
764 w, h, NULL, stride);
765 #ifdef CONFIG_PIXMAN
766 pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img,
767 x, y, 0, 0, 0, 0, w, h);
768 #else
769 {
770 uint8_t *src = (uint8_t *)pixman_image_get_data(ddl->ds->image);
771 uint8_t *dst = (uint8_t *)pixman_image_get_data(img);
772 int bp = PIXMAN_FORMAT_BPP(surface_format(ddl->ds)) / 8;
773 int hh;
774
775 for (hh = 0; hh < h; hh++) {
776 memcpy(&dst[stride * hh],
777 &src[surface_stride(ddl->ds) * (hh + y) + x * bp],
778 stride);
779 }
780 }
781 #endif
782 v_data = g_variant_new_from_data(
783 G_VARIANT_TYPE("ay"),
784 pixman_image_get_data(img),
785 pixman_image_get_stride(img) * h,
786 TRUE,
787 (GDestroyNotify)pixman_image_unref,
788 img);
789 qemu_dbus_display1_listener_call_update(ddl->proxy,
790 x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img),
791 v_data,
792 G_DBUS_CALL_FLAGS_NONE,
793 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
794 }
795
ddl_scanout(DBusDisplayListener * ddl)796 static void ddl_scanout(DBusDisplayListener *ddl)
797 {
798 GVariant *v_data;
799
800 v_data = g_variant_new_from_data(
801 G_VARIANT_TYPE("ay"), surface_data(ddl->ds),
802 surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE,
803 (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image));
804
805 ddl_discard_display_messages(ddl);
806
807 qemu_dbus_display1_listener_call_scanout(
808 ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds),
809 surface_stride(ddl->ds), surface_format(ddl->ds), v_data,
810 G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL,
811 g_object_ref(ddl));
812 }
813
dbus_gfx_update(DisplayChangeListener * dcl,int x,int y,int w,int h)814 static void dbus_gfx_update(DisplayChangeListener *dcl,
815 int x, int y, int w, int h)
816 {
817 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
818
819 assert(ddl->ds);
820
821 trace_dbus_update(x, y, w, h);
822
823 if (dbus_scanout_map(ddl)) {
824 #ifdef WIN32
825 qemu_dbus_display1_listener_win32_map_call_update_map(
826 ddl->map_proxy,
827 x, y, w, h,
828 G_DBUS_CALL_FLAGS_NONE,
829 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
830 #else
831 qemu_dbus_display1_listener_unix_map_call_update_map(
832 ddl->map_proxy,
833 x, y, w, h,
834 G_DBUS_CALL_FLAGS_NONE,
835 DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
836 #endif
837 return;
838 }
839
840 if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
841 return ddl_scanout(ddl);
842 }
843
844 dbus_gfx_update_sub(ddl, x, y, w, h);
845 }
846
847 #ifdef CONFIG_OPENGL
dbus_gl_gfx_switch(DisplayChangeListener * dcl,struct DisplaySurface * new_surface)848 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
849 struct DisplaySurface *new_surface)
850 {
851 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
852
853 trace_dbus_gl_gfx_switch(new_surface);
854
855 ddl->ds = new_surface;
856 ddl->ds_share = SHARE_KIND_NONE;
857 if (ddl->ds) {
858 int width = surface_width(ddl->ds);
859 int height = surface_height(ddl->ds);
860
861 /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
862 dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
863 width, height, 0, 0, width, height, NULL);
864 }
865 }
866 #endif
867
dbus_gfx_switch(DisplayChangeListener * dcl,struct DisplaySurface * new_surface)868 static void dbus_gfx_switch(DisplayChangeListener *dcl,
869 struct DisplaySurface *new_surface)
870 {
871 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
872
873 ddl->ds = new_surface;
874 ddl->ds_share = SHARE_KIND_NONE;
875 }
876
dbus_mouse_set(DisplayChangeListener * dcl,int x,int y,bool on)877 static void dbus_mouse_set(DisplayChangeListener *dcl,
878 int x, int y, bool on)
879 {
880 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
881
882 qemu_dbus_display1_listener_call_mouse_set(
883 ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
884 }
885
dbus_cursor_define(DisplayChangeListener * dcl,QEMUCursor * c)886 static void dbus_cursor_define(DisplayChangeListener *dcl,
887 QEMUCursor *c)
888 {
889 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
890 GVariant *v_data = NULL;
891
892 ddl_discard_cursor_messages(ddl);
893
894 v_data = g_variant_new_from_data(
895 G_VARIANT_TYPE("ay"),
896 c->data,
897 c->width * c->height * 4,
898 TRUE,
899 (GDestroyNotify)cursor_unref,
900 cursor_ref(c));
901
902 qemu_dbus_display1_listener_call_cursor_define(
903 ddl->proxy,
904 c->width,
905 c->height,
906 c->hot_x,
907 c->hot_y,
908 v_data,
909 G_DBUS_CALL_FLAGS_NONE,
910 -1,
911 NULL,
912 NULL,
913 NULL);
914 }
915
916 #ifdef CONFIG_OPENGL
917 const DisplayChangeListenerOps dbus_gl_dcl_ops = {
918 .dpy_name = "dbus-gl",
919 .dpy_gfx_update = dbus_gl_gfx_update,
920 .dpy_gfx_switch = dbus_gl_gfx_switch,
921 .dpy_gfx_check_format = console_gl_check_format,
922 .dpy_refresh = dbus_gl_refresh,
923 .dpy_mouse_set = dbus_mouse_set,
924 .dpy_cursor_define = dbus_cursor_define,
925
926 .dpy_gl_scanout_disable = dbus_scanout_disable,
927 .dpy_gl_scanout_texture = dbus_scanout_texture,
928 #ifdef CONFIG_GBM
929 .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf,
930 .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf,
931 .dpy_gl_release_dmabuf = dbus_release_dmabuf,
932 #endif
933 .dpy_gl_cursor_position = dbus_gl_cursor_position,
934 .dpy_gl_update = dbus_scanout_update,
935 };
936 #endif
937
938 const DisplayChangeListenerOps dbus_dcl_ops = {
939 .dpy_name = "dbus",
940 .dpy_gfx_update = dbus_gfx_update,
941 .dpy_gfx_switch = dbus_gfx_switch,
942 .dpy_refresh = dbus_refresh,
943 .dpy_mouse_set = dbus_mouse_set,
944 .dpy_cursor_define = dbus_cursor_define,
945 };
946
947 static void
dbus_display_listener_dispose(GObject * object)948 dbus_display_listener_dispose(GObject *object)
949 {
950 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
951
952 unregister_displaychangelistener(&ddl->dcl);
953 g_clear_object(&ddl->conn);
954 g_clear_pointer(&ddl->bus_name, g_free);
955 g_clear_object(&ddl->proxy);
956 #ifdef WIN32
957 g_clear_object(&ddl->map_proxy);
958 g_clear_object(&ddl->d3d11_proxy);
959 g_clear_pointer(&ddl->peer_process, CloseHandle);
960 #ifdef CONFIG_PIXMAN
961 pixman_region32_fini(&ddl->gl_damage);
962 #endif
963 #ifdef CONFIG_OPENGL
964 egl_fb_destroy(&ddl->fb);
965 #endif
966 #else /* !WIN32 */
967 g_clear_object(&ddl->scanout_dmabuf_v2_proxy);
968 #endif
969
970 G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
971 }
972
973 static void
dbus_display_listener_constructed(GObject * object)974 dbus_display_listener_constructed(GObject *object)
975 {
976 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
977
978 ddl->dcl.ops = &dbus_dcl_ops;
979 #ifdef CONFIG_OPENGL
980 if (display_opengl) {
981 ddl->dcl.ops = &dbus_gl_dcl_ops;
982 }
983 #endif
984
985 G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object);
986 }
987
988 static void
dbus_display_listener_class_init(DBusDisplayListenerClass * klass)989 dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
990 {
991 GObjectClass *object_class = G_OBJECT_CLASS(klass);
992
993 object_class->dispose = dbus_display_listener_dispose;
994 object_class->constructed = dbus_display_listener_constructed;
995 }
996
997 static void
dbus_display_listener_init(DBusDisplayListener * ddl)998 dbus_display_listener_init(DBusDisplayListener *ddl)
999 {
1000 #ifdef CONFIG_PIXMAN
1001 pixman_region32_init(&ddl->gl_damage);
1002 #endif
1003 }
1004
1005 const char *
dbus_display_listener_get_bus_name(DBusDisplayListener * ddl)1006 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl)
1007 {
1008 return ddl->bus_name ?: "p2p";
1009 }
1010
1011 DBusDisplayConsole *
dbus_display_listener_get_console(DBusDisplayListener * ddl)1012 dbus_display_listener_get_console(DBusDisplayListener *ddl)
1013 {
1014 return ddl->console;
1015 }
1016
1017 static bool
dbus_display_listener_implements(DBusDisplayListener * ddl,const char * iface)1018 dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface)
1019 {
1020 QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy);
1021 bool implements;
1022
1023 implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface);
1024 if (!implements) {
1025 g_debug("Display listener does not implement: `%s`", iface);
1026 }
1027
1028 return implements;
1029 }
1030
1031 #ifdef WIN32
1032 static bool
dbus_display_listener_setup_peer_process(DBusDisplayListener * ddl)1033 dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl)
1034 {
1035 g_autoptr(GError) err = NULL;
1036 GDBusConnection *conn;
1037 GIOStream *stream;
1038 GSocket *sock;
1039 g_autoptr(GCredentials) creds = NULL;
1040 DWORD *pid;
1041
1042 if (ddl->peer_process) {
1043 return true;
1044 }
1045
1046 conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy));
1047 stream = g_dbus_connection_get_stream(conn);
1048
1049 if (!G_IS_UNIX_CONNECTION(stream)) {
1050 return false;
1051 }
1052
1053 sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream));
1054 creds = g_socket_get_credentials(sock, &err);
1055
1056 if (!creds) {
1057 g_debug("Failed to get peer credentials: %s", err->message);
1058 return false;
1059 }
1060
1061 pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID);
1062
1063 if (pid == NULL) {
1064 g_debug("Failed to get peer PID");
1065 return false;
1066 }
1067
1068 ddl->peer_process = OpenProcess(
1069 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
1070 false, *pid);
1071
1072 if (!ddl->peer_process) {
1073 g_autofree char *msg = g_win32_error_message(GetLastError());
1074 g_debug("Failed to OpenProcess: %s", msg);
1075 return false;
1076 }
1077
1078 return true;
1079 }
1080 #endif
1081
1082 static void
dbus_display_listener_setup_d3d11(DBusDisplayListener * ddl)1083 dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl)
1084 {
1085 #ifdef WIN32
1086 g_autoptr(GError) err = NULL;
1087
1088 if (!dbus_display_listener_implements(ddl,
1089 "org.qemu.Display1.Listener.Win32.D3d11")) {
1090 return;
1091 }
1092
1093 if (!dbus_display_listener_setup_peer_process(ddl)) {
1094 return;
1095 }
1096
1097 ddl->d3d11_proxy =
1098 qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn,
1099 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
1100 NULL,
1101 "/org/qemu/Display1/Listener",
1102 NULL,
1103 &err);
1104 if (!ddl->d3d11_proxy) {
1105 g_debug("Failed to setup win32 d3d11 proxy: %s", err->message);
1106 return;
1107 }
1108 #endif
1109 }
1110
1111 static void
dbus_display_listener_setup_shared_map(DBusDisplayListener * ddl)1112 dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl)
1113 {
1114 g_autoptr(GError) err = NULL;
1115
1116 #ifdef WIN32
1117 if (!dbus_display_listener_implements(
1118 ddl, "org.qemu.Display1.Listener.Win32.Map")) {
1119 return;
1120 }
1121
1122 if (!dbus_display_listener_setup_peer_process(ddl)) {
1123 return;
1124 }
1125
1126 ddl->map_proxy =
1127 qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn,
1128 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
1129 NULL,
1130 "/org/qemu/Display1/Listener",
1131 NULL,
1132 &err);
1133 if (!ddl->map_proxy) {
1134 g_debug("Failed to setup win32 map proxy: %s", err->message);
1135 return;
1136 }
1137
1138 ddl->can_share_map = true;
1139 #else /* !WIN32 */
1140 if (!dbus_display_listener_implements(
1141 ddl, "org.qemu.Display1.Listener.Unix.Map")) {
1142 return;
1143 }
1144 ddl->map_proxy = qemu_dbus_display1_listener_unix_map_proxy_new_sync(
1145 ddl->conn, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
1146 "/org/qemu/Display1/Listener", NULL, &err);
1147 if (!ddl->map_proxy) {
1148 g_debug("Failed to setup Unix map proxy: %s", err->message);
1149 return;
1150 }
1151
1152 ddl->can_share_map = true;
1153 #endif
1154 }
1155
dbus_display_listener_setup_scanout_dmabuf_v2(DBusDisplayListener * ddl)1156 static void dbus_display_listener_setup_scanout_dmabuf_v2(DBusDisplayListener *ddl)
1157 {
1158 #ifndef WIN32
1159 g_autoptr(GError) err = NULL;
1160
1161 if (!dbus_display_listener_implements(
1162 ddl, "org.qemu.Display1.Listener.Unix.ScanoutDMABUF2")) {
1163 return;
1164 }
1165 ddl->scanout_dmabuf_v2_proxy =
1166 qemu_dbus_display1_listener_unix_scanout_dmabuf2_proxy_new_sync(
1167 ddl->conn, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
1168 "/org/qemu/Display1/Listener", NULL, &err);
1169 if (!ddl->scanout_dmabuf_v2_proxy) {
1170 g_debug("Failed to setup Unix scanout dmabuf v2 proxy: %s", err->message);
1171 return;
1172 }
1173 #endif
1174 }
1175
1176 static GDBusMessage *
dbus_filter(GDBusConnection * connection,GDBusMessage * message,gboolean incoming,gpointer user_data)1177 dbus_filter(GDBusConnection *connection,
1178 GDBusMessage *message,
1179 gboolean incoming,
1180 gpointer user_data)
1181 {
1182 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data);
1183 guint32 serial, discard_serial;
1184
1185 if (incoming) {
1186 return message;
1187 }
1188
1189 serial = g_dbus_message_get_serial(message);
1190
1191 discard_serial = g_atomic_int_get(&ddl->display_serial_to_discard);
1192 if (serial <= discard_serial) {
1193 const char *member = g_dbus_message_get_member(message);
1194 static const char *const display_messages[] = {
1195 "Scanout",
1196 "Update",
1197 #ifdef CONFIG_GBM
1198 "ScanoutDMABUF",
1199 "UpdateDMABUF",
1200 #endif
1201 "ScanoutMap",
1202 "UpdateMap",
1203 "Disable",
1204 NULL,
1205 };
1206
1207 if (g_strv_contains(display_messages, member)) {
1208 trace_dbus_filter(serial, discard_serial);
1209 g_object_unref(message);
1210 return NULL;
1211 }
1212 }
1213
1214 discard_serial = g_atomic_int_get(&ddl->cursor_serial_to_discard);
1215 if (serial <= discard_serial) {
1216 const gchar *member = g_dbus_message_get_member(message);
1217 static const char *const cursor_messages[] = {
1218 "CursorDefine",
1219 NULL
1220 };
1221
1222 if (g_strv_contains(cursor_messages, member)) {
1223 trace_dbus_filter(serial, discard_serial);
1224 g_object_unref(message);
1225 return NULL;
1226 }
1227 }
1228
1229 return message;
1230 }
1231
1232 DBusDisplayListener *
dbus_display_listener_new(const char * bus_name,GDBusConnection * conn,DBusDisplayConsole * console)1233 dbus_display_listener_new(const char *bus_name,
1234 GDBusConnection *conn,
1235 DBusDisplayConsole *console)
1236 {
1237 DBusDisplayListener *ddl;
1238 QemuConsole *con;
1239 g_autoptr(GError) err = NULL;
1240
1241 ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL);
1242 ddl->proxy =
1243 qemu_dbus_display1_listener_proxy_new_sync(conn,
1244 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
1245 NULL,
1246 "/org/qemu/Display1/Listener",
1247 NULL,
1248 &err);
1249 if (!ddl->proxy) {
1250 error_report("Failed to setup proxy: %s", err->message);
1251 g_object_unref(conn);
1252 g_object_unref(ddl);
1253 return NULL;
1254 }
1255
1256 ddl->dbus_filter = g_dbus_connection_add_filter(conn, dbus_filter, g_object_ref(ddl), g_object_unref);
1257 ddl->bus_name = g_strdup(bus_name);
1258 ddl->conn = conn;
1259 ddl->console = console;
1260
1261 dbus_display_listener_setup_shared_map(ddl);
1262 trace_dbus_can_share_map(ddl->can_share_map);
1263 dbus_display_listener_setup_d3d11(ddl);
1264 dbus_display_listener_setup_scanout_dmabuf_v2(ddl);
1265
1266 con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
1267 assert(con);
1268 ddl->dcl.con = con;
1269 register_displaychangelistener(&ddl->dcl);
1270
1271 return ddl;
1272 }
1273