 0337e4123e
			
		
	
	
		0337e4123e
		
	
	
	
	
		
			
			Although an input is routed depending on the console, qemu_input_is_absolute() had no mechanism to specify the console. Accept QemuConsole as an argument for qemu_input_is_absolute, and let the display know the absolute/relative state for a particular console. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20230921082936.28100-1-akihiko.odaki@daynix.com>
		
			
				
	
	
		
			625 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			625 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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;
 | |
| }
 |