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