qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

dbus-listener.c (14528B)


      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 "sysemu/sysemu.h"
     26 #include "dbus.h"
     27 #include <gio/gunixfdlist.h>
     28 
     29 #include "ui/shader.h"
     30 #include "ui/egl-helpers.h"
     31 #include "ui/egl-context.h"
     32 #include "trace.h"
     33 
     34 struct _DBusDisplayListener {
     35     GObject parent;
     36 
     37     char *bus_name;
     38     DBusDisplayConsole *console;
     39     GDBusConnection *conn;
     40 
     41     QemuDBusDisplay1Listener *proxy;
     42 
     43     DisplayChangeListener dcl;
     44     DisplaySurface *ds;
     45     int gl_updates;
     46 };
     47 
     48 G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
     49 
     50 static void dbus_update_gl_cb(GObject *source_object,
     51                            GAsyncResult *res,
     52                            gpointer user_data)
     53 {
     54     g_autoptr(GError) err = NULL;
     55     DBusDisplayListener *ddl = user_data;
     56 
     57     if (!qemu_dbus_display1_listener_call_update_dmabuf_finish(ddl->proxy,
     58                                                                res, &err)) {
     59         error_report("Failed to call update: %s", err->message);
     60     }
     61 
     62     graphic_hw_gl_block(ddl->dcl.con, false);
     63     g_object_unref(ddl);
     64 }
     65 
     66 static void dbus_call_update_gl(DBusDisplayListener *ddl,
     67                                 int x, int y, int w, int h)
     68 {
     69     graphic_hw_gl_block(ddl->dcl.con, true);
     70     glFlush();
     71     qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy,
     72         x, y, w, h,
     73         G_DBUS_CALL_FLAGS_NONE,
     74         DBUS_DEFAULT_TIMEOUT, NULL,
     75         dbus_update_gl_cb,
     76         g_object_ref(ddl));
     77 }
     78 
     79 static void dbus_scanout_disable(DisplayChangeListener *dcl)
     80 {
     81     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
     82 
     83     ddl->ds = NULL;
     84     qemu_dbus_display1_listener_call_disable(
     85         ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
     86 }
     87 
     88 static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
     89                                 QemuDmaBuf *dmabuf)
     90 {
     91     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
     92     g_autoptr(GError) err = NULL;
     93     g_autoptr(GUnixFDList) fd_list = NULL;
     94 
     95     fd_list = g_unix_fd_list_new();
     96     if (g_unix_fd_list_append(fd_list, dmabuf->fd, &err) != 0) {
     97         error_report("Failed to setup dmabuf fdlist: %s", err->message);
     98         return;
     99     }
    100 
    101     qemu_dbus_display1_listener_call_scanout_dmabuf(
    102         ddl->proxy,
    103         g_variant_new_handle(0),
    104         dmabuf->width,
    105         dmabuf->height,
    106         dmabuf->stride,
    107         dmabuf->fourcc,
    108         dmabuf->modifier,
    109         dmabuf->y0_top,
    110         G_DBUS_CALL_FLAGS_NONE,
    111         -1,
    112         fd_list,
    113         NULL, NULL, NULL);
    114 }
    115 
    116 static void dbus_scanout_texture(DisplayChangeListener *dcl,
    117                                  uint32_t tex_id,
    118                                  bool backing_y_0_top,
    119                                  uint32_t backing_width,
    120                                  uint32_t backing_height,
    121                                  uint32_t x, uint32_t y,
    122                                  uint32_t w, uint32_t h)
    123 {
    124     QemuDmaBuf dmabuf = {
    125         .width = backing_width,
    126         .height = backing_height,
    127         .y0_top = backing_y_0_top,
    128     };
    129 
    130     assert(tex_id);
    131     dmabuf.fd = egl_get_fd_for_texture(
    132         tex_id, (EGLint *)&dmabuf.stride,
    133         (EGLint *)&dmabuf.fourcc,
    134         &dmabuf.modifier);
    135     if (dmabuf.fd < 0) {
    136         error_report("%s: failed to get fd for texture", __func__);
    137         return;
    138     }
    139 
    140     dbus_scanout_dmabuf(dcl, &dmabuf);
    141     close(dmabuf.fd);
    142 }
    143 
    144 static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
    145                                QemuDmaBuf *dmabuf, bool have_hot,
    146                                uint32_t hot_x, uint32_t hot_y)
    147 {
    148     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    149     DisplaySurface *ds;
    150     GVariant *v_data = NULL;
    151     egl_fb cursor_fb;
    152 
    153     if (!dmabuf) {
    154         qemu_dbus_display1_listener_call_mouse_set(
    155             ddl->proxy, 0, 0, false,
    156             G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
    157         return;
    158     }
    159 
    160     egl_dmabuf_import_texture(dmabuf);
    161     if (!dmabuf->texture) {
    162         return;
    163     }
    164     egl_fb_setup_for_tex(&cursor_fb, dmabuf->width, dmabuf->height,
    165                          dmabuf->texture, false);
    166     ds = qemu_create_displaysurface(dmabuf->width, dmabuf->height);
    167     egl_fb_read(ds, &cursor_fb);
    168 
    169     v_data = g_variant_new_from_data(
    170         G_VARIANT_TYPE("ay"),
    171         surface_data(ds),
    172         surface_width(ds) * surface_height(ds) * 4,
    173         TRUE,
    174         (GDestroyNotify)qemu_free_displaysurface,
    175         ds);
    176     qemu_dbus_display1_listener_call_cursor_define(
    177         ddl->proxy,
    178         surface_width(ds),
    179         surface_height(ds),
    180         hot_x,
    181         hot_y,
    182         v_data,
    183         G_DBUS_CALL_FLAGS_NONE,
    184         -1,
    185         NULL,
    186         NULL,
    187         NULL);
    188 }
    189 
    190 static void dbus_cursor_position(DisplayChangeListener *dcl,
    191                                  uint32_t pos_x, uint32_t pos_y)
    192 {
    193     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    194 
    195     qemu_dbus_display1_listener_call_mouse_set(
    196         ddl->proxy, pos_x, pos_y, true,
    197         G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
    198 }
    199 
    200 static void dbus_release_dmabuf(DisplayChangeListener *dcl,
    201                                 QemuDmaBuf *dmabuf)
    202 {
    203     dbus_scanout_disable(dcl);
    204 }
    205 
    206 static void dbus_scanout_update(DisplayChangeListener *dcl,
    207                                 uint32_t x, uint32_t y,
    208                                 uint32_t w, uint32_t h)
    209 {
    210     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    211 
    212     dbus_call_update_gl(ddl, x, y, w, h);
    213 }
    214 
    215 static void dbus_gl_refresh(DisplayChangeListener *dcl)
    216 {
    217     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    218 
    219     graphic_hw_update(dcl->con);
    220 
    221     if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) {
    222         return;
    223     }
    224 
    225     if (ddl->gl_updates) {
    226         dbus_call_update_gl(ddl, 0, 0,
    227                             surface_width(ddl->ds), surface_height(ddl->ds));
    228         ddl->gl_updates = 0;
    229     }
    230 }
    231 
    232 static void dbus_refresh(DisplayChangeListener *dcl)
    233 {
    234     graphic_hw_update(dcl->con);
    235 }
    236 
    237 static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
    238                                int x, int y, int w, int h)
    239 {
    240     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    241 
    242     ddl->gl_updates++;
    243 }
    244 
    245 static void dbus_gfx_update(DisplayChangeListener *dcl,
    246                             int x, int y, int w, int h)
    247 {
    248     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    249     pixman_image_t *img;
    250     GVariant *v_data;
    251     size_t stride;
    252 
    253     assert(ddl->ds);
    254     stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
    255 
    256     trace_dbus_update(x, y, w, h);
    257 
    258     if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
    259         v_data = g_variant_new_from_data(
    260             G_VARIANT_TYPE("ay"),
    261             surface_data(ddl->ds),
    262             surface_stride(ddl->ds) * surface_height(ddl->ds),
    263             TRUE,
    264             (GDestroyNotify)pixman_image_unref,
    265             pixman_image_ref(ddl->ds->image));
    266         qemu_dbus_display1_listener_call_scanout(
    267             ddl->proxy,
    268             surface_width(ddl->ds),
    269             surface_height(ddl->ds),
    270             surface_stride(ddl->ds),
    271             surface_format(ddl->ds),
    272             v_data,
    273             G_DBUS_CALL_FLAGS_NONE,
    274             DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
    275         return;
    276     }
    277 
    278     /* make a copy, since gvariant only handles linear data */
    279     img = pixman_image_create_bits(surface_format(ddl->ds),
    280                                    w, h, NULL, stride);
    281     pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img,
    282                            x, y, 0, 0, 0, 0, w, h);
    283 
    284     v_data = g_variant_new_from_data(
    285         G_VARIANT_TYPE("ay"),
    286         pixman_image_get_data(img),
    287         pixman_image_get_stride(img) * h,
    288         TRUE,
    289         (GDestroyNotify)pixman_image_unref,
    290         img);
    291     qemu_dbus_display1_listener_call_update(ddl->proxy,
    292         x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img),
    293         v_data,
    294         G_DBUS_CALL_FLAGS_NONE,
    295         DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
    296 }
    297 
    298 static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
    299                                struct DisplaySurface *new_surface)
    300 {
    301     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    302 
    303     ddl->ds = new_surface;
    304     if (ddl->ds) {
    305         int width = surface_width(ddl->ds);
    306         int height = surface_height(ddl->ds);
    307 
    308         /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
    309         dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
    310                              width, height, 0, 0, width, height);
    311     }
    312 }
    313 
    314 static void dbus_gfx_switch(DisplayChangeListener *dcl,
    315                             struct DisplaySurface *new_surface)
    316 {
    317     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    318 
    319     ddl->ds = new_surface;
    320     if (!ddl->ds) {
    321         /* why not call disable instead? */
    322         return;
    323     }
    324 }
    325 
    326 static void dbus_mouse_set(DisplayChangeListener *dcl,
    327                            int x, int y, int on)
    328 {
    329     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    330 
    331     qemu_dbus_display1_listener_call_mouse_set(
    332         ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
    333 }
    334 
    335 static void dbus_cursor_define(DisplayChangeListener *dcl,
    336                                QEMUCursor *c)
    337 {
    338     DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
    339     GVariant *v_data = NULL;
    340 
    341     cursor_get(c);
    342     v_data = g_variant_new_from_data(
    343         G_VARIANT_TYPE("ay"),
    344         c->data,
    345         c->width * c->height * 4,
    346         TRUE,
    347         (GDestroyNotify)cursor_put,
    348         c);
    349 
    350     qemu_dbus_display1_listener_call_cursor_define(
    351         ddl->proxy,
    352         c->width,
    353         c->height,
    354         c->hot_x,
    355         c->hot_y,
    356         v_data,
    357         G_DBUS_CALL_FLAGS_NONE,
    358         -1,
    359         NULL,
    360         NULL,
    361         NULL);
    362 }
    363 
    364 const DisplayChangeListenerOps dbus_gl_dcl_ops = {
    365     .dpy_name                = "dbus-gl",
    366     .dpy_gfx_update          = dbus_gl_gfx_update,
    367     .dpy_gfx_switch          = dbus_gl_gfx_switch,
    368     .dpy_gfx_check_format    = console_gl_check_format,
    369     .dpy_refresh             = dbus_gl_refresh,
    370     .dpy_mouse_set           = dbus_mouse_set,
    371     .dpy_cursor_define       = dbus_cursor_define,
    372 
    373     .dpy_gl_scanout_disable  = dbus_scanout_disable,
    374     .dpy_gl_scanout_texture  = dbus_scanout_texture,
    375     .dpy_gl_scanout_dmabuf   = dbus_scanout_dmabuf,
    376     .dpy_gl_cursor_dmabuf    = dbus_cursor_dmabuf,
    377     .dpy_gl_cursor_position  = dbus_cursor_position,
    378     .dpy_gl_release_dmabuf   = dbus_release_dmabuf,
    379     .dpy_gl_update           = dbus_scanout_update,
    380 };
    381 
    382 const DisplayChangeListenerOps dbus_dcl_ops = {
    383     .dpy_name                = "dbus",
    384     .dpy_gfx_update          = dbus_gfx_update,
    385     .dpy_gfx_switch          = dbus_gfx_switch,
    386     .dpy_refresh             = dbus_refresh,
    387     .dpy_mouse_set           = dbus_mouse_set,
    388     .dpy_cursor_define       = dbus_cursor_define,
    389 };
    390 
    391 static void
    392 dbus_display_listener_dispose(GObject *object)
    393 {
    394     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
    395 
    396     unregister_displaychangelistener(&ddl->dcl);
    397     g_clear_object(&ddl->conn);
    398     g_clear_pointer(&ddl->bus_name, g_free);
    399     g_clear_object(&ddl->proxy);
    400 
    401     G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
    402 }
    403 
    404 static void
    405 dbus_display_listener_constructed(GObject *object)
    406 {
    407     DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
    408 
    409     if (display_opengl) {
    410         ddl->dcl.ops = &dbus_gl_dcl_ops;
    411     } else {
    412         ddl->dcl.ops = &dbus_dcl_ops;
    413     }
    414 
    415     G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object);
    416 }
    417 
    418 static void
    419 dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
    420 {
    421     GObjectClass *object_class = G_OBJECT_CLASS(klass);
    422 
    423     object_class->dispose = dbus_display_listener_dispose;
    424     object_class->constructed = dbus_display_listener_constructed;
    425 }
    426 
    427 static void
    428 dbus_display_listener_init(DBusDisplayListener *ddl)
    429 {
    430 }
    431 
    432 const char *
    433 dbus_display_listener_get_bus_name(DBusDisplayListener *ddl)
    434 {
    435     return ddl->bus_name ?: "p2p";
    436 }
    437 
    438 DBusDisplayConsole *
    439 dbus_display_listener_get_console(DBusDisplayListener *ddl)
    440 {
    441     return ddl->console;
    442 }
    443 
    444 DBusDisplayListener *
    445 dbus_display_listener_new(const char *bus_name,
    446                           GDBusConnection *conn,
    447                           DBusDisplayConsole *console)
    448 {
    449     DBusDisplayListener *ddl;
    450     QemuConsole *con;
    451     g_autoptr(GError) err = NULL;
    452 
    453     ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL);
    454     ddl->proxy =
    455         qemu_dbus_display1_listener_proxy_new_sync(conn,
    456             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
    457             NULL,
    458             "/org/qemu/Display1/Listener",
    459             NULL,
    460             &err);
    461     if (!ddl->proxy) {
    462         error_report("Failed to setup proxy: %s", err->message);
    463         g_object_unref(conn);
    464         g_object_unref(ddl);
    465         return NULL;
    466     }
    467 
    468     ddl->bus_name = g_strdup(bus_name);
    469     ddl->conn = conn;
    470     ddl->console = console;
    471 
    472     con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
    473     assert(con);
    474     ddl->dcl.con = con;
    475     register_displaychangelistener(&ddl->dcl);
    476 
    477     return ddl;
    478 }