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