qemu

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

dbus-console.c (15939B)


      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 "qapi/error.h"
     26 #include "ui/input.h"
     27 #include "ui/kbd-state.h"
     28 #include "trace.h"
     29 
     30 #include <gio/gunixfdlist.h>
     31 
     32 #include "dbus.h"
     33 
     34 struct _DBusDisplayConsole {
     35     GDBusObjectSkeleton parent_instance;
     36     DisplayChangeListener dcl;
     37 
     38     DBusDisplay *display;
     39     GHashTable *listeners;
     40     QemuDBusDisplay1Console *iface;
     41 
     42     QemuDBusDisplay1Keyboard *iface_kbd;
     43     QKbdState *kbd;
     44 
     45     QemuDBusDisplay1Mouse *iface_mouse;
     46     gboolean last_set;
     47     guint last_x;
     48     guint last_y;
     49     Notifier mouse_mode_notifier;
     50 };
     51 
     52 G_DEFINE_TYPE(DBusDisplayConsole,
     53               dbus_display_console,
     54               G_TYPE_DBUS_OBJECT_SKELETON)
     55 
     56 static void
     57 dbus_display_console_set_size(DBusDisplayConsole *ddc,
     58                               uint32_t width, uint32_t height)
     59 {
     60     g_object_set(ddc->iface,
     61                  "width", width,
     62                  "height", height,
     63                  NULL);
     64 }
     65 
     66 static void
     67 dbus_gfx_switch(DisplayChangeListener *dcl,
     68                 struct DisplaySurface *new_surface)
     69 {
     70     DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
     71 
     72     dbus_display_console_set_size(ddc,
     73                                   surface_width(new_surface),
     74                                   surface_height(new_surface));
     75 }
     76 
     77 static void
     78 dbus_gfx_update(DisplayChangeListener *dcl,
     79                 int x, int y, int w, int h)
     80 {
     81 }
     82 
     83 static void
     84 dbus_gl_scanout_disable(DisplayChangeListener *dcl)
     85 {
     86 }
     87 
     88 static void
     89 dbus_gl_scanout_texture(DisplayChangeListener *dcl,
     90                         uint32_t tex_id,
     91                         bool backing_y_0_top,
     92                         uint32_t backing_width,
     93                         uint32_t backing_height,
     94                         uint32_t x, uint32_t y,
     95                         uint32_t w, uint32_t h)
     96 {
     97     DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
     98 
     99     dbus_display_console_set_size(ddc, w, h);
    100 }
    101 
    102 static void
    103 dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl,
    104                        QemuDmaBuf *dmabuf)
    105 {
    106     DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
    107 
    108     dbus_display_console_set_size(ddc,
    109                                   dmabuf->width,
    110                                   dmabuf->height);
    111 }
    112 
    113 static void
    114 dbus_gl_scanout_update(DisplayChangeListener *dcl,
    115                        uint32_t x, uint32_t y,
    116                        uint32_t w, uint32_t h)
    117 {
    118 }
    119 
    120 const DisplayChangeListenerOps dbus_console_dcl_ops = {
    121     .dpy_name                = "dbus-console",
    122     .dpy_gfx_switch          = dbus_gfx_switch,
    123     .dpy_gfx_update          = dbus_gfx_update,
    124     .dpy_gl_scanout_disable  = dbus_gl_scanout_disable,
    125     .dpy_gl_scanout_texture  = dbus_gl_scanout_texture,
    126     .dpy_gl_scanout_dmabuf   = dbus_gl_scanout_dmabuf,
    127     .dpy_gl_update           = dbus_gl_scanout_update,
    128 };
    129 
    130 static void
    131 dbus_display_console_init(DBusDisplayConsole *object)
    132 {
    133     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
    134 
    135     ddc->listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
    136                                             NULL, g_object_unref);
    137     ddc->dcl.ops = &dbus_console_dcl_ops;
    138 }
    139 
    140 static void
    141 dbus_display_console_dispose(GObject *object)
    142 {
    143     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
    144 
    145     unregister_displaychangelistener(&ddc->dcl);
    146     g_clear_object(&ddc->iface_kbd);
    147     g_clear_object(&ddc->iface);
    148     g_clear_pointer(&ddc->listeners, g_hash_table_unref);
    149     g_clear_pointer(&ddc->kbd, qkbd_state_free);
    150 
    151     G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object);
    152 }
    153 
    154 static void
    155 dbus_display_console_class_init(DBusDisplayConsoleClass *klass)
    156 {
    157     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
    158 
    159     gobject_class->dispose = dbus_display_console_dispose;
    160 }
    161 
    162 static void
    163 listener_vanished_cb(DBusDisplayListener *listener)
    164 {
    165     DBusDisplayConsole *ddc = dbus_display_listener_get_console(listener);
    166     const char *name = dbus_display_listener_get_bus_name(listener);
    167 
    168     trace_dbus_listener_vanished(name);
    169 
    170     g_hash_table_remove(ddc->listeners, name);
    171     qkbd_state_lift_all_keys(ddc->kbd);
    172 }
    173 
    174 static gboolean
    175 dbus_console_set_ui_info(DBusDisplayConsole *ddc,
    176                          GDBusMethodInvocation *invocation,
    177                          guint16 arg_width_mm,
    178                          guint16 arg_height_mm,
    179                          gint arg_xoff,
    180                          gint arg_yoff,
    181                          guint arg_width,
    182                          guint arg_height)
    183 {
    184     QemuUIInfo info = {
    185         .width_mm = arg_width_mm,
    186         .height_mm = arg_height_mm,
    187         .xoff = arg_xoff,
    188         .yoff = arg_yoff,
    189         .width = arg_width,
    190         .height = arg_height,
    191     };
    192 
    193     if (!dpy_ui_info_supported(ddc->dcl.con)) {
    194         g_dbus_method_invocation_return_error(invocation,
    195                                               DBUS_DISPLAY_ERROR,
    196                                               DBUS_DISPLAY_ERROR_UNSUPPORTED,
    197                                               "SetUIInfo is not supported");
    198         return DBUS_METHOD_INVOCATION_HANDLED;
    199     }
    200 
    201     dpy_set_ui_info(ddc->dcl.con, &info, false);
    202     qemu_dbus_display1_console_complete_set_uiinfo(ddc->iface, invocation);
    203     return DBUS_METHOD_INVOCATION_HANDLED;
    204 }
    205 
    206 static gboolean
    207 dbus_console_register_listener(DBusDisplayConsole *ddc,
    208                                GDBusMethodInvocation *invocation,
    209                                GUnixFDList *fd_list,
    210                                GVariant *arg_listener)
    211 {
    212     const char *sender = g_dbus_method_invocation_get_sender(invocation);
    213     GDBusConnection *listener_conn;
    214     g_autoptr(GError) err = NULL;
    215     g_autoptr(GSocket) socket = NULL;
    216     g_autoptr(GSocketConnection) socket_conn = NULL;
    217     g_autofree char *guid = g_dbus_generate_guid();
    218     DBusDisplayListener *listener;
    219     int fd;
    220 
    221     if (sender && g_hash_table_contains(ddc->listeners, sender)) {
    222         g_dbus_method_invocation_return_error(
    223             invocation,
    224             DBUS_DISPLAY_ERROR,
    225             DBUS_DISPLAY_ERROR_INVALID,
    226             "`%s` is already registered!",
    227             sender);
    228         return DBUS_METHOD_INVOCATION_HANDLED;
    229     }
    230 
    231     fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
    232     if (err) {
    233         g_dbus_method_invocation_return_error(
    234             invocation,
    235             DBUS_DISPLAY_ERROR,
    236             DBUS_DISPLAY_ERROR_FAILED,
    237             "Couldn't get peer fd: %s", err->message);
    238         return DBUS_METHOD_INVOCATION_HANDLED;
    239     }
    240 
    241     socket = g_socket_new_from_fd(fd, &err);
    242     if (err) {
    243         g_dbus_method_invocation_return_error(
    244             invocation,
    245             DBUS_DISPLAY_ERROR,
    246             DBUS_DISPLAY_ERROR_FAILED,
    247             "Couldn't make a socket: %s", err->message);
    248         close(fd);
    249         return DBUS_METHOD_INVOCATION_HANDLED;
    250     }
    251     socket_conn = g_socket_connection_factory_create_connection(socket);
    252 
    253     qemu_dbus_display1_console_complete_register_listener(
    254         ddc->iface, invocation, NULL);
    255 
    256     listener_conn = g_dbus_connection_new_sync(
    257         G_IO_STREAM(socket_conn),
    258         guid,
    259         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
    260         NULL, NULL, &err);
    261     if (err) {
    262         error_report("Failed to setup peer connection: %s", err->message);
    263         return DBUS_METHOD_INVOCATION_HANDLED;
    264     }
    265 
    266     listener = dbus_display_listener_new(sender, listener_conn, ddc);
    267     if (!listener) {
    268         return DBUS_METHOD_INVOCATION_HANDLED;
    269     }
    270 
    271     g_hash_table_insert(ddc->listeners,
    272                         (gpointer)dbus_display_listener_get_bus_name(listener),
    273                         listener);
    274     g_object_connect(listener_conn,
    275                      "swapped-signal::closed", listener_vanished_cb, listener,
    276                      NULL);
    277 
    278     trace_dbus_registered_listener(sender);
    279     return DBUS_METHOD_INVOCATION_HANDLED;
    280 }
    281 
    282 static gboolean
    283 dbus_kbd_press(DBusDisplayConsole *ddc,
    284                GDBusMethodInvocation *invocation,
    285                guint arg_keycode)
    286 {
    287     QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode);
    288 
    289     trace_dbus_kbd_press(arg_keycode);
    290 
    291     qkbd_state_key_event(ddc->kbd, qcode, true);
    292 
    293     qemu_dbus_display1_keyboard_complete_press(ddc->iface_kbd, invocation);
    294 
    295     return DBUS_METHOD_INVOCATION_HANDLED;
    296 }
    297 
    298 static gboolean
    299 dbus_kbd_release(DBusDisplayConsole *ddc,
    300                  GDBusMethodInvocation *invocation,
    301                  guint arg_keycode)
    302 {
    303     QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode);
    304 
    305     trace_dbus_kbd_release(arg_keycode);
    306 
    307     qkbd_state_key_event(ddc->kbd, qcode, false);
    308 
    309     qemu_dbus_display1_keyboard_complete_release(ddc->iface_kbd, invocation);
    310 
    311     return DBUS_METHOD_INVOCATION_HANDLED;
    312 }
    313 
    314 static void
    315 dbus_kbd_qemu_leds_updated(void *data, int ledstate)
    316 {
    317     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(data);
    318 
    319     qemu_dbus_display1_keyboard_set_modifiers(ddc->iface_kbd, ledstate);
    320 }
    321 
    322 static gboolean
    323 dbus_mouse_rel_motion(DBusDisplayConsole *ddc,
    324                       GDBusMethodInvocation *invocation,
    325                       int dx, int dy)
    326 {
    327     trace_dbus_mouse_rel_motion(dx, dy);
    328 
    329     if (qemu_input_is_absolute()) {
    330         g_dbus_method_invocation_return_error(
    331             invocation, DBUS_DISPLAY_ERROR,
    332             DBUS_DISPLAY_ERROR_INVALID,
    333             "Mouse is not relative");
    334         return DBUS_METHOD_INVOCATION_HANDLED;
    335     }
    336 
    337     qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_X, dx);
    338     qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_Y, dy);
    339     qemu_input_event_sync();
    340 
    341     qemu_dbus_display1_mouse_complete_rel_motion(ddc->iface_mouse,
    342                                                     invocation);
    343 
    344     return DBUS_METHOD_INVOCATION_HANDLED;
    345 }
    346 
    347 static gboolean
    348 dbus_mouse_set_pos(DBusDisplayConsole *ddc,
    349                    GDBusMethodInvocation *invocation,
    350                    guint x, guint y)
    351 {
    352     int width, height;
    353 
    354     trace_dbus_mouse_set_pos(x, y);
    355 
    356     if (!qemu_input_is_absolute()) {
    357         g_dbus_method_invocation_return_error(
    358             invocation, DBUS_DISPLAY_ERROR,
    359             DBUS_DISPLAY_ERROR_INVALID,
    360             "Mouse is not absolute");
    361         return DBUS_METHOD_INVOCATION_HANDLED;
    362     }
    363 
    364     width = qemu_console_get_width(ddc->dcl.con, 0);
    365     height = qemu_console_get_height(ddc->dcl.con, 0);
    366     if (x >= width || y >= height) {
    367         g_dbus_method_invocation_return_error(
    368             invocation, DBUS_DISPLAY_ERROR,
    369             DBUS_DISPLAY_ERROR_INVALID,
    370             "Invalid mouse position");
    371         return DBUS_METHOD_INVOCATION_HANDLED;
    372     }
    373     qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_X, x, 0, width);
    374     qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_Y, y, 0, height);
    375     qemu_input_event_sync();
    376 
    377     qemu_dbus_display1_mouse_complete_set_abs_position(ddc->iface_mouse,
    378                                                           invocation);
    379 
    380     return DBUS_METHOD_INVOCATION_HANDLED;
    381 }
    382 
    383 static gboolean
    384 dbus_mouse_press(DBusDisplayConsole *ddc,
    385                  GDBusMethodInvocation *invocation,
    386                  guint button)
    387 {
    388     trace_dbus_mouse_press(button);
    389 
    390     qemu_input_queue_btn(ddc->dcl.con, button, true);
    391     qemu_input_event_sync();
    392 
    393     qemu_dbus_display1_mouse_complete_press(ddc->iface_mouse, invocation);
    394 
    395     return DBUS_METHOD_INVOCATION_HANDLED;
    396 }
    397 
    398 static gboolean
    399 dbus_mouse_release(DBusDisplayConsole *ddc,
    400                    GDBusMethodInvocation *invocation,
    401                    guint button)
    402 {
    403     trace_dbus_mouse_release(button);
    404 
    405     qemu_input_queue_btn(ddc->dcl.con, button, false);
    406     qemu_input_event_sync();
    407 
    408     qemu_dbus_display1_mouse_complete_release(ddc->iface_mouse, invocation);
    409 
    410     return DBUS_METHOD_INVOCATION_HANDLED;
    411 }
    412 
    413 static void
    414 dbus_mouse_mode_change(Notifier *notify, void *data)
    415 {
    416     DBusDisplayConsole *ddc =
    417         container_of(notify, DBusDisplayConsole, mouse_mode_notifier);
    418 
    419     g_object_set(ddc->iface_mouse,
    420                  "is-absolute", qemu_input_is_absolute(),
    421                  NULL);
    422 }
    423 
    424 int dbus_display_console_get_index(DBusDisplayConsole *ddc)
    425 {
    426     return qemu_console_get_index(ddc->dcl.con);
    427 }
    428 
    429 DBusDisplayConsole *
    430 dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
    431 {
    432     g_autofree char *path = NULL;
    433     g_autofree char *label = NULL;
    434     char device_addr[256] = "";
    435     DBusDisplayConsole *ddc;
    436     int idx;
    437 
    438     assert(display);
    439     assert(con);
    440 
    441     label = qemu_console_get_label(con);
    442     idx = qemu_console_get_index(con);
    443     path = g_strdup_printf(DBUS_DISPLAY1_ROOT "/Console_%d", idx);
    444     ddc = g_object_new(DBUS_DISPLAY_TYPE_CONSOLE,
    445                         "g-object-path", path,
    446                         NULL);
    447     ddc->display = display;
    448     ddc->dcl.con = con;
    449     /* handle errors, and skip non graphics? */
    450     qemu_console_fill_device_address(
    451         con, device_addr, sizeof(device_addr), NULL);
    452 
    453     ddc->iface = qemu_dbus_display1_console_skeleton_new();
    454     g_object_set(ddc->iface,
    455         "label", label,
    456         "type", qemu_console_is_graphic(con) ? "Graphic" : "Text",
    457         "head", qemu_console_get_head(con),
    458         "width", qemu_console_get_width(con, 0),
    459         "height", qemu_console_get_height(con, 0),
    460         "device-address", device_addr,
    461         NULL);
    462     g_object_connect(ddc->iface,
    463         "swapped-signal::handle-register-listener",
    464         dbus_console_register_listener, ddc,
    465         "swapped-signal::handle-set-uiinfo",
    466         dbus_console_set_ui_info, ddc,
    467         NULL);
    468     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
    469         G_DBUS_INTERFACE_SKELETON(ddc->iface));
    470 
    471     ddc->kbd = qkbd_state_init(con);
    472     ddc->iface_kbd = qemu_dbus_display1_keyboard_skeleton_new();
    473     qemu_add_led_event_handler(dbus_kbd_qemu_leds_updated, ddc);
    474     g_object_connect(ddc->iface_kbd,
    475         "swapped-signal::handle-press", dbus_kbd_press, ddc,
    476         "swapped-signal::handle-release", dbus_kbd_release, ddc,
    477         NULL);
    478     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
    479         G_DBUS_INTERFACE_SKELETON(ddc->iface_kbd));
    480 
    481     ddc->iface_mouse = qemu_dbus_display1_mouse_skeleton_new();
    482     g_object_connect(ddc->iface_mouse,
    483         "swapped-signal::handle-set-abs-position", dbus_mouse_set_pos, ddc,
    484         "swapped-signal::handle-rel-motion", dbus_mouse_rel_motion, ddc,
    485         "swapped-signal::handle-press", dbus_mouse_press, ddc,
    486         "swapped-signal::handle-release", dbus_mouse_release, ddc,
    487         NULL);
    488     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
    489         G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse));
    490 
    491     register_displaychangelistener(&ddc->dcl);
    492     ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change;
    493     qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
    494 
    495     return ddc;
    496 }