1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "qemu/osdep.h"
25#include "qemu/error-report.h"
26#include "qapi/error.h"
27#include "sysemu/sysemu.h"
28#include "dbus.h"
29#ifdef G_OS_UNIX
30#include <gio/gunixfdlist.h>
31#endif
32#ifdef WIN32
33#include <d3d11.h>
34#include <dxgi1_2.h>
35#endif
36
37#ifdef CONFIG_OPENGL
38#include "ui/shader.h"
39#include "ui/egl-helpers.h"
40#include "ui/egl-context.h"
41#endif
42#include "trace.h"
43
44static void dbus_gfx_switch(DisplayChangeListener *dcl,
45 struct DisplaySurface *new_surface);
46
47enum share_kind {
48 SHARE_KIND_NONE,
49 SHARE_KIND_MAPPED,
50 SHARE_KIND_D3DTEX,
51};
52
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;
64 enum share_kind ds_share;
65
66 int gl_updates;
67
68 bool ds_mapped;
69 bool can_share_map;
70
71#ifdef WIN32
72 QemuDBusDisplay1ListenerWin32Map *map_proxy;
73 QemuDBusDisplay1ListenerWin32D3d11 *d3d11_proxy;
74 HANDLE peer_process;
75 ID3D11Texture2D *d3d_texture;
76#ifdef CONFIG_OPENGL
77 egl_fb fb;
78#endif
79#endif
80};
81
82G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
83
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
92 qemu_dbus_display1_listener_call_disable(
93 ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
94}
95
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
179
180#if defined(CONFIG_GBM) || defined(WIN32)
181static void dbus_update_gl_cb(GObject *source_object,
182 GAsyncResult *res,
183 gpointer user_data)
184{
185 g_autoptr(GError) err = NULL;
186 DBusDisplayListener *ddl = user_data;
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
199
200 if (!success) {
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}
207#endif
208
209static void dbus_call_update_gl(DisplayChangeListener *dcl,
210 int x, int y, int w, int h)
211{
212#if defined(CONFIG_GBM) || defined(WIN32)
213 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
214#endif
215
216 trace_dbus_update_gl(x, y, w, h);
217
218 glFlush();
219#ifdef CONFIG_GBM
220 graphic_hw_gl_block(ddl->dcl.con, true);
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));
227#endif
228
229#ifdef WIN32
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;
235 case SHARE_KIND_D3DTEX: {
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;
252 }
253 default:
254 g_warn_if_reached();
255 }
256#endif
257}
258
259#ifdef CONFIG_GBM
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
273
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}
288#endif
289#endif
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
298 if (ddl->ds_share == SHARE_KIND_MAPPED) {
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
337 ddl->ds_share = SHARE_KIND_MAPPED;
338
339 return true;
340}
341
342#ifdef CONFIG_OPENGL
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;
401
402 return true;
403}
404#endif
405#endif
406
407#ifdef CONFIG_OPENGL
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,
414 uint32_t w, uint32_t h,
415 void *d3d_tex2d)
416{
417 trace_dbus_scanout_texture(tex_id, backing_y_0_top,
418 backing_width, backing_height, x, y, w, h);
419#ifdef CONFIG_GBM
420 QemuDmaBuf dmabuf = {
421 .width = w,
422 .height = h,
423 .y0_top = backing_y_0_top,
424 .x = x,
425 .y = y,
426 .backing_width = backing_width,
427 .backing_height = backing_height,
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);
442#endif
443
444#ifdef WIN32
445 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
446
447
448 assert(surface_width(ddl->ds) == w);
449 assert(surface_height(ddl->ds) == h);
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 }
458#endif
459}
460
461#ifdef CONFIG_GBM
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;
469 egl_fb cursor_fb = EGL_FB_INIT;
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
508static void dbus_release_dmabuf(DisplayChangeListener *dcl,
509 QemuDmaBuf *dmabuf)
510{
511 dbus_scanout_disable(dcl);
512}
513#endif
514
515static void dbus_gl_cursor_position(DisplayChangeListener *dcl,
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
525static void dbus_scanout_update(DisplayChangeListener *dcl,
526 uint32_t x, uint32_t y,
527 uint32_t w, uint32_t h)
528{
529 dbus_call_update_gl(dcl, x, y, w, h);
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) {
543 dbus_call_update_gl(dcl, 0, 0,
544 surface_width(ddl->ds), surface_height(ddl->ds));
545 ddl->gl_updates = 0;
546 }
547}
548#endif
549
550static void dbus_refresh(DisplayChangeListener *dcl)
551{
552 graphic_hw_update(dcl->con);
553}
554
555#ifdef CONFIG_OPENGL
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
561 ddl->gl_updates++;
562}
563#endif
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);
574
575 trace_dbus_update(x, y, w, h);
576
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
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
608
609 stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
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
629#ifdef CONFIG_OPENGL
630static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
631 struct DisplaySurface *new_surface)
632{
633 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
634
635 trace_dbus_gl_gfx_switch(new_surface);
636
637 ddl->ds = new_surface;
638 ddl->ds_share = SHARE_KIND_NONE;
639 if (ddl->ds) {
640 int width = surface_width(ddl->ds);
641 int height = surface_height(ddl->ds);
642
643
644 dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
645 width, height, 0, 0, width, height, NULL);
646 }
647}
648#endif
649
650static void dbus_gfx_switch(DisplayChangeListener *dcl,
651 struct DisplaySurface *new_surface)
652{
653 DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
654
655 ddl->ds = new_surface;
656 ddl->ds_share = SHARE_KIND_NONE;
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
674 v_data = g_variant_new_from_data(
675 G_VARIANT_TYPE("ay"),
676 c->data,
677 c->width * c->height * 4,
678 TRUE,
679 (GDestroyNotify)cursor_unref,
680 cursor_ref(c));
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
696#ifdef CONFIG_OPENGL
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,
708#ifdef CONFIG_GBM
709 .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf,
710 .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf,
711 .dpy_gl_release_dmabuf = dbus_release_dmabuf,
712#endif
713 .dpy_gl_cursor_position = dbus_gl_cursor_position,
714 .dpy_gl_update = dbus_scanout_update,
715};
716#endif
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);
736#ifdef WIN32
737 g_clear_object(&ddl->map_proxy);
738 g_clear_object(&ddl->d3d11_proxy);
739 g_clear_pointer(&ddl->peer_process, CloseHandle);
740#ifdef CONFIG_OPENGL
741 egl_fb_destroy(&ddl->fb);
742#endif
743#endif
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
753 ddl->dcl.ops = &dbus_dcl_ops;
754#ifdef CONFIG_OPENGL
755 if (display_opengl) {
756 ddl->dcl.ops = &dbus_gl_dcl_ops;
757 }
758#endif
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{
780 return ddl->bus_name ?: "p2p";
781}
782
783DBusDisplayConsole *
784dbus_display_listener_get_console(DBusDisplayListener *ddl)
785{
786 return ddl->console;
787}
788
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}
803
804static bool
805dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl)
806{
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
814 if (ddl->peer_process) {
815 return true;
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)) {
822 return false;
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);
830 return false;
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");
837 return false;
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);
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)) {
894 return;
895 }
896
897 ddl->map_proxy =
898 qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn,
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
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
941 dbus_display_listener_setup_shared_map(ddl);
942 dbus_display_listener_setup_d3d11(ddl);
943
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}
951