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