|  | /* | 
|  | * QEMU DBus display console | 
|  | * | 
|  | * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com> | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
|  | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  | * THE SOFTWARE. | 
|  | */ | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/error-report.h" | 
|  | #include "qapi/error.h" | 
|  | #include "ui/input.h" | 
|  | #include "ui/kbd-state.h" | 
|  | #include "trace.h" | 
|  |  | 
|  | #ifdef G_OS_UNIX | 
|  | #include <gio/gunixfdlist.h> | 
|  | #endif | 
|  |  | 
|  | #include "dbus.h" | 
|  |  | 
|  | static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX]; | 
|  |  | 
|  | struct _DBusDisplayConsole { | 
|  | GDBusObjectSkeleton parent_instance; | 
|  | DisplayChangeListener dcl; | 
|  |  | 
|  | DBusDisplay *display; | 
|  | GHashTable *listeners; | 
|  | QemuDBusDisplay1Console *iface; | 
|  |  | 
|  | QemuDBusDisplay1Keyboard *iface_kbd; | 
|  | QKbdState *kbd; | 
|  |  | 
|  | QemuDBusDisplay1Mouse *iface_mouse; | 
|  | QemuDBusDisplay1MultiTouch *iface_touch; | 
|  | gboolean last_set; | 
|  | guint last_x; | 
|  | guint last_y; | 
|  | Notifier mouse_mode_notifier; | 
|  | }; | 
|  |  | 
|  | G_DEFINE_TYPE(DBusDisplayConsole, | 
|  | dbus_display_console, | 
|  | G_TYPE_DBUS_OBJECT_SKELETON) | 
|  |  | 
|  | static void | 
|  | dbus_display_console_set_size(DBusDisplayConsole *ddc, | 
|  | uint32_t width, uint32_t height) | 
|  | { | 
|  | g_object_set(ddc->iface, | 
|  | "width", width, | 
|  | "height", height, | 
|  | NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_gfx_switch(DisplayChangeListener *dcl, | 
|  | struct DisplaySurface *new_surface) | 
|  | { | 
|  | DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); | 
|  |  | 
|  | dbus_display_console_set_size(ddc, | 
|  | surface_width(new_surface), | 
|  | surface_height(new_surface)); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_gfx_update(DisplayChangeListener *dcl, | 
|  | int x, int y, int w, int h) | 
|  | { | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_gl_scanout_disable(DisplayChangeListener *dcl) | 
|  | { | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_gl_scanout_texture(DisplayChangeListener *dcl, | 
|  | uint32_t tex_id, | 
|  | bool backing_y_0_top, | 
|  | uint32_t backing_width, | 
|  | uint32_t backing_height, | 
|  | uint32_t x, uint32_t y, | 
|  | uint32_t w, uint32_t h, | 
|  | void *d3d_tex2d) | 
|  | { | 
|  | DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); | 
|  |  | 
|  | dbus_display_console_set_size(ddc, w, h); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl, | 
|  | QemuDmaBuf *dmabuf) | 
|  | { | 
|  | DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); | 
|  |  | 
|  | dbus_display_console_set_size(ddc, | 
|  | dmabuf->width, | 
|  | dmabuf->height); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_gl_scanout_update(DisplayChangeListener *dcl, | 
|  | uint32_t x, uint32_t y, | 
|  | uint32_t w, uint32_t h) | 
|  | { | 
|  | } | 
|  |  | 
|  | const DisplayChangeListenerOps dbus_console_dcl_ops = { | 
|  | .dpy_name                = "dbus-console", | 
|  | .dpy_gfx_switch          = dbus_gfx_switch, | 
|  | .dpy_gfx_update          = dbus_gfx_update, | 
|  | .dpy_gl_scanout_disable  = dbus_gl_scanout_disable, | 
|  | .dpy_gl_scanout_texture  = dbus_gl_scanout_texture, | 
|  | .dpy_gl_scanout_dmabuf   = dbus_gl_scanout_dmabuf, | 
|  | .dpy_gl_update           = dbus_gl_scanout_update, | 
|  | }; | 
|  |  | 
|  | static void | 
|  | dbus_display_console_init(DBusDisplayConsole *object) | 
|  | { | 
|  | DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); | 
|  |  | 
|  | ddc->listeners = g_hash_table_new_full(g_str_hash, g_str_equal, | 
|  | NULL, g_object_unref); | 
|  | ddc->dcl.ops = &dbus_console_dcl_ops; | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_display_console_dispose(GObject *object) | 
|  | { | 
|  | DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); | 
|  |  | 
|  | unregister_displaychangelistener(&ddc->dcl); | 
|  | g_clear_object(&ddc->iface_touch); | 
|  | g_clear_object(&ddc->iface_mouse); | 
|  | g_clear_object(&ddc->iface_kbd); | 
|  | g_clear_object(&ddc->iface); | 
|  | g_clear_pointer(&ddc->listeners, g_hash_table_unref); | 
|  | g_clear_pointer(&ddc->kbd, qkbd_state_free); | 
|  |  | 
|  | G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_display_console_class_init(DBusDisplayConsoleClass *klass) | 
|  | { | 
|  | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); | 
|  |  | 
|  | gobject_class->dispose = dbus_display_console_dispose; | 
|  | } | 
|  |  | 
|  | static void | 
|  | listener_vanished_cb(DBusDisplayListener *listener) | 
|  | { | 
|  | DBusDisplayConsole *ddc = dbus_display_listener_get_console(listener); | 
|  | const char *name = dbus_display_listener_get_bus_name(listener); | 
|  |  | 
|  | trace_dbus_listener_vanished(name); | 
|  |  | 
|  | g_hash_table_remove(ddc->listeners, name); | 
|  | qkbd_state_lift_all_keys(ddc->kbd); | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | dbus_console_set_ui_info(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | guint16 arg_width_mm, | 
|  | guint16 arg_height_mm, | 
|  | gint arg_xoff, | 
|  | gint arg_yoff, | 
|  | guint arg_width, | 
|  | guint arg_height) | 
|  | { | 
|  | QemuUIInfo info = { | 
|  | .width_mm = arg_width_mm, | 
|  | .height_mm = arg_height_mm, | 
|  | .xoff = arg_xoff, | 
|  | .yoff = arg_yoff, | 
|  | .width = arg_width, | 
|  | .height = arg_height, | 
|  | }; | 
|  |  | 
|  | if (!dpy_ui_info_supported(ddc->dcl.con)) { | 
|  | g_dbus_method_invocation_return_error(invocation, | 
|  | DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_UNSUPPORTED, | 
|  | "SetUIInfo is not supported"); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | dpy_set_ui_info(ddc->dcl.con, &info, false); | 
|  | qemu_dbus_display1_console_complete_set_uiinfo(ddc->iface, invocation); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | #ifdef G_OS_WIN32 | 
|  | bool | 
|  | dbus_win32_import_socket(GDBusMethodInvocation *invocation, | 
|  | GVariant *arg_listener, int *socket) | 
|  | { | 
|  | gsize n; | 
|  | WSAPROTOCOL_INFOW *info = (void *)g_variant_get_fixed_array(arg_listener, &n, 1); | 
|  |  | 
|  | if (!info || n != sizeof(*info)) { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, | 
|  | DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_FAILED, | 
|  | "Failed to get socket infos"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *socket = WSASocketW(FROM_PROTOCOL_INFO, | 
|  | FROM_PROTOCOL_INFO, | 
|  | FROM_PROTOCOL_INFO, | 
|  | info, 0, 0); | 
|  | if (*socket == INVALID_SOCKET) { | 
|  | g_autofree gchar *emsg = g_win32_error_message(WSAGetLastError()); | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, | 
|  | DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_FAILED, | 
|  | "Couldn't create socket: %s", emsg); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static gboolean | 
|  | dbus_console_register_listener(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | #ifdef G_OS_UNIX | 
|  | GUnixFDList *fd_list, | 
|  | #endif | 
|  | GVariant *arg_listener) | 
|  | { | 
|  | const char *sender = g_dbus_method_invocation_get_sender(invocation); | 
|  | GDBusConnection *listener_conn; | 
|  | g_autoptr(GError) err = NULL; | 
|  | g_autoptr(GSocket) socket = NULL; | 
|  | g_autoptr(GSocketConnection) socket_conn = NULL; | 
|  | g_autofree char *guid = g_dbus_generate_guid(); | 
|  | DBusDisplayListener *listener; | 
|  | int fd; | 
|  |  | 
|  | if (sender && g_hash_table_contains(ddc->listeners, sender)) { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, | 
|  | DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_INVALID, | 
|  | "`%s` is already registered!", | 
|  | sender); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | #ifdef G_OS_WIN32 | 
|  | if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  | #else | 
|  | fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err); | 
|  | if (err) { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, | 
|  | DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_FAILED, | 
|  | "Couldn't get peer fd: %s", err->message); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | socket = g_socket_new_from_fd(fd, &err); | 
|  | if (err) { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, | 
|  | DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_FAILED, | 
|  | "Couldn't make a socket: %s", err->message); | 
|  | #ifdef G_OS_WIN32 | 
|  | closesocket(fd); | 
|  | #else | 
|  | close(fd); | 
|  | #endif | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  | socket_conn = g_socket_connection_factory_create_connection(socket); | 
|  |  | 
|  | qemu_dbus_display1_console_complete_register_listener( | 
|  | ddc->iface, invocation | 
|  | #ifdef G_OS_UNIX | 
|  | , NULL | 
|  | #endif | 
|  | ); | 
|  |  | 
|  | listener_conn = g_dbus_connection_new_sync( | 
|  | G_IO_STREAM(socket_conn), | 
|  | guid, | 
|  | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, | 
|  | NULL, NULL, &err); | 
|  | if (err) { | 
|  | error_report("Failed to setup peer connection: %s", err->message); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | listener = dbus_display_listener_new(sender, listener_conn, ddc); | 
|  | if (!listener) { | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | g_hash_table_insert(ddc->listeners, | 
|  | (gpointer)dbus_display_listener_get_bus_name(listener), | 
|  | listener); | 
|  | g_object_connect(listener_conn, | 
|  | "swapped-signal::closed", listener_vanished_cb, listener, | 
|  | NULL); | 
|  |  | 
|  | trace_dbus_registered_listener(sender); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | dbus_kbd_press(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | guint arg_keycode) | 
|  | { | 
|  | QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode); | 
|  |  | 
|  | trace_dbus_kbd_press(arg_keycode); | 
|  |  | 
|  | qkbd_state_key_event(ddc->kbd, qcode, true); | 
|  |  | 
|  | qemu_dbus_display1_keyboard_complete_press(ddc->iface_kbd, invocation); | 
|  |  | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | dbus_kbd_release(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | guint arg_keycode) | 
|  | { | 
|  | QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode); | 
|  |  | 
|  | trace_dbus_kbd_release(arg_keycode); | 
|  |  | 
|  | qkbd_state_key_event(ddc->kbd, qcode, false); | 
|  |  | 
|  | qemu_dbus_display1_keyboard_complete_release(ddc->iface_kbd, invocation); | 
|  |  | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_kbd_qemu_leds_updated(void *data, int ledstate) | 
|  | { | 
|  | DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(data); | 
|  |  | 
|  | qemu_dbus_display1_keyboard_set_modifiers(ddc->iface_kbd, ledstate); | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | dbus_mouse_rel_motion(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | int dx, int dy) | 
|  | { | 
|  | trace_dbus_mouse_rel_motion(dx, dy); | 
|  |  | 
|  | if (qemu_input_is_absolute(ddc->dcl.con)) { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_INVALID, | 
|  | "Mouse is not relative"); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_X, dx); | 
|  | qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_Y, dy); | 
|  | qemu_input_event_sync(); | 
|  |  | 
|  | qemu_dbus_display1_mouse_complete_rel_motion(ddc->iface_mouse, | 
|  | invocation); | 
|  |  | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | dbus_touch_send_event(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | guint kind, uint64_t num_slot, | 
|  | double x, double y) | 
|  | { | 
|  | Error *error = NULL; | 
|  | int width, height; | 
|  | trace_dbus_touch_send_event(kind, num_slot, x, y); | 
|  |  | 
|  | if (kind != INPUT_MULTI_TOUCH_TYPE_BEGIN && | 
|  | kind != INPUT_MULTI_TOUCH_TYPE_UPDATE && | 
|  | kind != INPUT_MULTI_TOUCH_TYPE_CANCEL && | 
|  | kind != INPUT_MULTI_TOUCH_TYPE_END) | 
|  | { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_INVALID, | 
|  | "Invalid touch event kind"); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  | width = qemu_console_get_width(ddc->dcl.con, 0); | 
|  | height = qemu_console_get_height(ddc->dcl.con, 0); | 
|  |  | 
|  | console_handle_touch_event(ddc->dcl.con, touch_slots, | 
|  | num_slot, width, height, | 
|  | x, y, kind, &error); | 
|  | if (error != NULL) { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_INVALID, | 
|  | error_get_pretty(error), NULL); | 
|  | error_free(error); | 
|  | } else { | 
|  | qemu_dbus_display1_multi_touch_complete_send_event(ddc->iface_touch, | 
|  | invocation); | 
|  | } | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | dbus_mouse_set_pos(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | guint x, guint y) | 
|  | { | 
|  | int width, height; | 
|  |  | 
|  | trace_dbus_mouse_set_pos(x, y); | 
|  |  | 
|  | if (!qemu_input_is_absolute(ddc->dcl.con)) { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_INVALID, | 
|  | "Mouse is not absolute"); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | width = qemu_console_get_width(ddc->dcl.con, 0); | 
|  | height = qemu_console_get_height(ddc->dcl.con, 0); | 
|  | if (x >= width || y >= height) { | 
|  | g_dbus_method_invocation_return_error( | 
|  | invocation, DBUS_DISPLAY_ERROR, | 
|  | DBUS_DISPLAY_ERROR_INVALID, | 
|  | "Invalid mouse position"); | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  | qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_X, x, 0, width); | 
|  | qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_Y, y, 0, height); | 
|  | qemu_input_event_sync(); | 
|  |  | 
|  | qemu_dbus_display1_mouse_complete_set_abs_position(ddc->iface_mouse, | 
|  | invocation); | 
|  |  | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | dbus_mouse_press(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | guint button) | 
|  | { | 
|  | trace_dbus_mouse_press(button); | 
|  |  | 
|  | qemu_input_queue_btn(ddc->dcl.con, button, true); | 
|  | qemu_input_event_sync(); | 
|  |  | 
|  | qemu_dbus_display1_mouse_complete_press(ddc->iface_mouse, invocation); | 
|  |  | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | dbus_mouse_release(DBusDisplayConsole *ddc, | 
|  | GDBusMethodInvocation *invocation, | 
|  | guint button) | 
|  | { | 
|  | trace_dbus_mouse_release(button); | 
|  |  | 
|  | qemu_input_queue_btn(ddc->dcl.con, button, false); | 
|  | qemu_input_event_sync(); | 
|  |  | 
|  | qemu_dbus_display1_mouse_complete_release(ddc->iface_mouse, invocation); | 
|  |  | 
|  | return DBUS_METHOD_INVOCATION_HANDLED; | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_mouse_update_is_absolute(DBusDisplayConsole *ddc) | 
|  | { | 
|  | g_object_set(ddc->iface_mouse, | 
|  | "is-absolute", qemu_input_is_absolute(ddc->dcl.con), | 
|  | NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dbus_mouse_mode_change(Notifier *notify, void *data) | 
|  | { | 
|  | DBusDisplayConsole *ddc = | 
|  | container_of(notify, DBusDisplayConsole, mouse_mode_notifier); | 
|  |  | 
|  | dbus_mouse_update_is_absolute(ddc); | 
|  | } | 
|  |  | 
|  | int dbus_display_console_get_index(DBusDisplayConsole *ddc) | 
|  | { | 
|  | return qemu_console_get_index(ddc->dcl.con); | 
|  | } | 
|  |  | 
|  | DBusDisplayConsole * | 
|  | dbus_display_console_new(DBusDisplay *display, QemuConsole *con) | 
|  | { | 
|  | g_autofree char *path = NULL; | 
|  | g_autofree char *label = NULL; | 
|  | char device_addr[256] = ""; | 
|  | DBusDisplayConsole *ddc; | 
|  | int idx, i; | 
|  | const char *interfaces[] = { | 
|  | "org.qemu.Display1.Keyboard", | 
|  | "org.qemu.Display1.Mouse", | 
|  | "org.qemu.Display1.MultiTouch", | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | assert(display); | 
|  | assert(con); | 
|  |  | 
|  | label = qemu_console_get_label(con); | 
|  | idx = qemu_console_get_index(con); | 
|  | path = g_strdup_printf(DBUS_DISPLAY1_ROOT "/Console_%d", idx); | 
|  | ddc = g_object_new(DBUS_DISPLAY_TYPE_CONSOLE, | 
|  | "g-object-path", path, | 
|  | NULL); | 
|  | ddc->display = display; | 
|  | ddc->dcl.con = con; | 
|  | /* handle errors, and skip non graphics? */ | 
|  | qemu_console_fill_device_address( | 
|  | con, device_addr, sizeof(device_addr), NULL); | 
|  |  | 
|  | ddc->iface = qemu_dbus_display1_console_skeleton_new(); | 
|  | g_object_set(ddc->iface, | 
|  | "label", label, | 
|  | "type", qemu_console_is_graphic(con) ? "Graphic" : "Text", | 
|  | "head", qemu_console_get_head(con), | 
|  | "width", qemu_console_get_width(con, 0), | 
|  | "height", qemu_console_get_height(con, 0), | 
|  | "device-address", device_addr, | 
|  | "interfaces", interfaces, | 
|  | NULL); | 
|  | g_object_connect(ddc->iface, | 
|  | "swapped-signal::handle-register-listener", | 
|  | dbus_console_register_listener, ddc, | 
|  | "swapped-signal::handle-set-uiinfo", | 
|  | dbus_console_set_ui_info, ddc, | 
|  | NULL); | 
|  | g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), | 
|  | G_DBUS_INTERFACE_SKELETON(ddc->iface)); | 
|  |  | 
|  | ddc->kbd = qkbd_state_init(con); | 
|  | ddc->iface_kbd = qemu_dbus_display1_keyboard_skeleton_new(); | 
|  | qemu_add_led_event_handler(dbus_kbd_qemu_leds_updated, ddc); | 
|  | g_object_connect(ddc->iface_kbd, | 
|  | "swapped-signal::handle-press", dbus_kbd_press, ddc, | 
|  | "swapped-signal::handle-release", dbus_kbd_release, ddc, | 
|  | NULL); | 
|  | g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), | 
|  | G_DBUS_INTERFACE_SKELETON(ddc->iface_kbd)); | 
|  |  | 
|  | ddc->iface_mouse = qemu_dbus_display1_mouse_skeleton_new(); | 
|  | g_object_connect(ddc->iface_mouse, | 
|  | "swapped-signal::handle-set-abs-position", dbus_mouse_set_pos, ddc, | 
|  | "swapped-signal::handle-rel-motion", dbus_mouse_rel_motion, ddc, | 
|  | "swapped-signal::handle-press", dbus_mouse_press, ddc, | 
|  | "swapped-signal::handle-release", dbus_mouse_release, ddc, | 
|  | NULL); | 
|  | g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), | 
|  | G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse)); | 
|  |  | 
|  | ddc->iface_touch = qemu_dbus_display1_multi_touch_skeleton_new(); | 
|  | g_object_connect(ddc->iface_touch, | 
|  | "swapped-signal::handle-send-event", dbus_touch_send_event, ddc, | 
|  | NULL); | 
|  | qemu_dbus_display1_multi_touch_set_max_slots(ddc->iface_touch, | 
|  | INPUT_EVENT_SLOTS_MAX); | 
|  | g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), | 
|  | G_DBUS_INTERFACE_SKELETON(ddc->iface_touch)); | 
|  |  | 
|  | for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) { | 
|  | struct touch_slot *slot = &touch_slots[i]; | 
|  | slot->tracking_id = -1; | 
|  | } | 
|  |  | 
|  | register_displaychangelistener(&ddc->dcl); | 
|  | ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change; | 
|  | qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier); | 
|  | dbus_mouse_update_is_absolute(ddc); | 
|  |  | 
|  | return ddc; | 
|  | } |