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 "ui/input.h" 28 #include "ui/kbd-state.h" 29 #include "trace.h" 30 31 #ifdef G_OS_UNIX 32 #include <gio/gunixfdlist.h> 33 #endif 34 35 #include "dbus.h" 36 37 static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX]; 38 39 struct _DBusDisplayConsole { 40 GDBusObjectSkeleton parent_instance; 41 DisplayChangeListener dcl; 42 43 DBusDisplay *display; 44 GPtrArray *listeners; 45 QemuDBusDisplay1Console *iface; 46 47 QemuDBusDisplay1Keyboard *iface_kbd; 48 QKbdState *kbd; 49 50 QemuDBusDisplay1Mouse *iface_mouse; 51 QemuDBusDisplay1MultiTouch *iface_touch; 52 gboolean last_set; 53 guint last_x; 54 guint last_y; 55 Notifier mouse_mode_notifier; 56 }; 57 58 G_DEFINE_TYPE(DBusDisplayConsole, 59 dbus_display_console, 60 G_TYPE_DBUS_OBJECT_SKELETON) 61 62 static void 63 dbus_display_console_set_size(DBusDisplayConsole *ddc, 64 uint32_t width, uint32_t height) 65 { 66 g_object_set(ddc->iface, 67 "width", width, 68 "height", height, 69 NULL); 70 } 71 72 static void 73 dbus_gfx_switch(DisplayChangeListener *dcl, 74 struct DisplaySurface *new_surface) 75 { 76 DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); 77 78 dbus_display_console_set_size(ddc, 79 surface_width(new_surface), 80 surface_height(new_surface)); 81 } 82 83 static void 84 dbus_gfx_update(DisplayChangeListener *dcl, 85 int x, int y, int w, int h) 86 { 87 } 88 89 static void 90 dbus_gl_scanout_disable(DisplayChangeListener *dcl) 91 { 92 } 93 94 static void 95 dbus_gl_scanout_texture(DisplayChangeListener *dcl, 96 uint32_t tex_id, 97 bool backing_y_0_top, 98 uint32_t backing_width, 99 uint32_t backing_height, 100 uint32_t x, uint32_t y, 101 uint32_t w, uint32_t h, 102 void *d3d_tex2d) 103 { 104 DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); 105 106 dbus_display_console_set_size(ddc, w, h); 107 } 108 109 static void 110 dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl, 111 QemuDmaBuf *dmabuf) 112 { 113 uint32_t width, height; 114 115 DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); 116 117 width = qemu_dmabuf_get_width(dmabuf); 118 height = qemu_dmabuf_get_height(dmabuf); 119 120 dbus_display_console_set_size(ddc, width, height); 121 } 122 123 static void 124 dbus_gl_scanout_update(DisplayChangeListener *dcl, 125 uint32_t x, uint32_t y, 126 uint32_t w, uint32_t h) 127 { 128 } 129 130 const DisplayChangeListenerOps dbus_console_dcl_ops = { 131 .dpy_name = "dbus-console", 132 .dpy_gfx_switch = dbus_gfx_switch, 133 .dpy_gfx_update = dbus_gfx_update, 134 .dpy_gl_scanout_disable = dbus_gl_scanout_disable, 135 .dpy_gl_scanout_texture = dbus_gl_scanout_texture, 136 .dpy_gl_scanout_dmabuf = dbus_gl_scanout_dmabuf, 137 .dpy_gl_update = dbus_gl_scanout_update, 138 }; 139 140 static void 141 dbus_display_console_init(DBusDisplayConsole *object) 142 { 143 DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); 144 145 ddc->listeners = g_ptr_array_new_with_free_func(g_object_unref); 146 ddc->dcl.ops = &dbus_console_dcl_ops; 147 } 148 149 static void 150 dbus_display_console_dispose(GObject *object) 151 { 152 DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); 153 154 unregister_displaychangelistener(&ddc->dcl); 155 g_clear_object(&ddc->iface_touch); 156 g_clear_object(&ddc->iface_mouse); 157 g_clear_object(&ddc->iface_kbd); 158 g_clear_object(&ddc->iface); 159 g_clear_pointer(&ddc->listeners, g_ptr_array_unref); 160 g_clear_pointer(&ddc->kbd, qkbd_state_free); 161 162 G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object); 163 } 164 165 static void 166 dbus_display_console_class_init(DBusDisplayConsoleClass *klass) 167 { 168 GObjectClass *gobject_class = G_OBJECT_CLASS(klass); 169 170 gobject_class->dispose = dbus_display_console_dispose; 171 } 172 173 static void 174 listener_vanished_cb(DBusDisplayListener *listener) 175 { 176 DBusDisplayConsole *ddc = dbus_display_listener_get_console(listener); 177 const char *name = dbus_display_listener_get_bus_name(listener); 178 179 trace_dbus_listener_vanished(name); 180 181 g_ptr_array_remove_fast(ddc->listeners, listener); 182 qkbd_state_lift_all_keys(ddc->kbd); 183 } 184 185 static gboolean 186 dbus_console_set_ui_info(DBusDisplayConsole *ddc, 187 GDBusMethodInvocation *invocation, 188 guint16 arg_width_mm, 189 guint16 arg_height_mm, 190 gint arg_xoff, 191 gint arg_yoff, 192 guint arg_width, 193 guint arg_height) 194 { 195 QemuUIInfo info = { 196 .width_mm = arg_width_mm, 197 .height_mm = arg_height_mm, 198 .xoff = arg_xoff, 199 .yoff = arg_yoff, 200 .width = arg_width, 201 .height = arg_height, 202 }; 203 204 if (!dpy_ui_info_supported(ddc->dcl.con)) { 205 g_dbus_method_invocation_return_error(invocation, 206 DBUS_DISPLAY_ERROR, 207 DBUS_DISPLAY_ERROR_UNSUPPORTED, 208 "SetUIInfo is not supported"); 209 return DBUS_METHOD_INVOCATION_HANDLED; 210 } 211 212 dpy_set_ui_info(ddc->dcl.con, &info, false); 213 qemu_dbus_display1_console_complete_set_uiinfo(ddc->iface, invocation); 214 return DBUS_METHOD_INVOCATION_HANDLED; 215 } 216 217 #ifdef G_OS_WIN32 218 bool 219 dbus_win32_import_socket(GDBusMethodInvocation *invocation, 220 GVariant *arg_listener, int *socket) 221 { 222 gsize n; 223 WSAPROTOCOL_INFOW *info = (void *)g_variant_get_fixed_array(arg_listener, &n, 1); 224 225 if (!info || n != sizeof(*info)) { 226 g_dbus_method_invocation_return_error( 227 invocation, 228 DBUS_DISPLAY_ERROR, 229 DBUS_DISPLAY_ERROR_FAILED, 230 "Failed to get socket infos"); 231 return false; 232 } 233 234 *socket = WSASocketW(FROM_PROTOCOL_INFO, 235 FROM_PROTOCOL_INFO, 236 FROM_PROTOCOL_INFO, 237 info, 0, 0); 238 if (*socket == INVALID_SOCKET) { 239 g_autofree gchar *emsg = g_win32_error_message(WSAGetLastError()); 240 g_dbus_method_invocation_return_error( 241 invocation, 242 DBUS_DISPLAY_ERROR, 243 DBUS_DISPLAY_ERROR_FAILED, 244 "Couldn't create socket: %s", emsg); 245 return false; 246 } 247 248 return true; 249 } 250 #endif 251 252 static gboolean 253 dbus_console_register_listener(DBusDisplayConsole *ddc, 254 GDBusMethodInvocation *invocation, 255 #ifdef G_OS_UNIX 256 GUnixFDList *fd_list, 257 #endif 258 GVariant *arg_listener) 259 { 260 const char *sender = g_dbus_method_invocation_get_sender(invocation); 261 GDBusConnection *listener_conn; 262 g_autoptr(GError) err = NULL; 263 g_autoptr(GSocket) socket = NULL; 264 g_autoptr(GSocketConnection) socket_conn = NULL; 265 g_autofree char *guid = g_dbus_generate_guid(); 266 DBusDisplayListener *listener; 267 int fd; 268 269 #ifdef G_OS_WIN32 270 if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { 271 return DBUS_METHOD_INVOCATION_HANDLED; 272 } 273 #else 274 fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err); 275 if (err) { 276 g_dbus_method_invocation_return_error( 277 invocation, 278 DBUS_DISPLAY_ERROR, 279 DBUS_DISPLAY_ERROR_FAILED, 280 "Couldn't get peer fd: %s", err->message); 281 return DBUS_METHOD_INVOCATION_HANDLED; 282 } 283 #endif 284 285 socket = g_socket_new_from_fd(fd, &err); 286 if (err) { 287 g_dbus_method_invocation_return_error( 288 invocation, 289 DBUS_DISPLAY_ERROR, 290 DBUS_DISPLAY_ERROR_FAILED, 291 "Couldn't make a socket: %s", err->message); 292 #ifdef G_OS_WIN32 293 closesocket(fd); 294 #else 295 close(fd); 296 #endif 297 return DBUS_METHOD_INVOCATION_HANDLED; 298 } 299 socket_conn = g_socket_connection_factory_create_connection(socket); 300 301 qemu_dbus_display1_console_complete_register_listener( 302 ddc->iface, invocation 303 #ifdef G_OS_UNIX 304 , NULL 305 #endif 306 ); 307 308 GDBusConnectionFlags flags = 309 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER; 310 #ifdef WIN32 311 flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; 312 #endif 313 314 listener_conn = g_dbus_connection_new_sync( 315 G_IO_STREAM(socket_conn), 316 guid, 317 flags, 318 NULL, NULL, &err); 319 if (err) { 320 error_report("Failed to setup peer connection: %s", err->message); 321 return DBUS_METHOD_INVOCATION_HANDLED; 322 } 323 324 listener = dbus_display_listener_new(sender, listener_conn, ddc); 325 if (!listener) { 326 return DBUS_METHOD_INVOCATION_HANDLED; 327 } 328 329 g_ptr_array_add(ddc->listeners, listener); 330 g_object_connect(listener_conn, 331 "swapped-signal::closed", listener_vanished_cb, listener, 332 NULL); 333 334 trace_dbus_registered_listener(sender); 335 return DBUS_METHOD_INVOCATION_HANDLED; 336 } 337 338 static gboolean 339 dbus_kbd_press(DBusDisplayConsole *ddc, 340 GDBusMethodInvocation *invocation, 341 guint arg_keycode) 342 { 343 QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode); 344 345 trace_dbus_kbd_press(arg_keycode); 346 347 qkbd_state_key_event(ddc->kbd, qcode, true); 348 349 qemu_dbus_display1_keyboard_complete_press(ddc->iface_kbd, invocation); 350 351 return DBUS_METHOD_INVOCATION_HANDLED; 352 } 353 354 static gboolean 355 dbus_kbd_release(DBusDisplayConsole *ddc, 356 GDBusMethodInvocation *invocation, 357 guint arg_keycode) 358 { 359 QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode); 360 361 trace_dbus_kbd_release(arg_keycode); 362 363 qkbd_state_key_event(ddc->kbd, qcode, false); 364 365 qemu_dbus_display1_keyboard_complete_release(ddc->iface_kbd, invocation); 366 367 return DBUS_METHOD_INVOCATION_HANDLED; 368 } 369 370 static void 371 dbus_kbd_qemu_leds_updated(void *data, int ledstate) 372 { 373 DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(data); 374 375 qemu_dbus_display1_keyboard_set_modifiers(ddc->iface_kbd, ledstate); 376 } 377 378 static gboolean 379 dbus_mouse_rel_motion(DBusDisplayConsole *ddc, 380 GDBusMethodInvocation *invocation, 381 int dx, int dy) 382 { 383 trace_dbus_mouse_rel_motion(dx, dy); 384 385 if (qemu_input_is_absolute(ddc->dcl.con)) { 386 g_dbus_method_invocation_return_error( 387 invocation, DBUS_DISPLAY_ERROR, 388 DBUS_DISPLAY_ERROR_INVALID, 389 "Mouse is not relative"); 390 return DBUS_METHOD_INVOCATION_HANDLED; 391 } 392 393 qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_X, dx); 394 qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_Y, dy); 395 qemu_input_event_sync(); 396 397 qemu_dbus_display1_mouse_complete_rel_motion(ddc->iface_mouse, 398 invocation); 399 400 return DBUS_METHOD_INVOCATION_HANDLED; 401 } 402 403 static gboolean 404 dbus_touch_send_event(DBusDisplayConsole *ddc, 405 GDBusMethodInvocation *invocation, 406 guint kind, uint64_t num_slot, 407 double x, double y) 408 { 409 Error *error = NULL; 410 int width, height; 411 trace_dbus_touch_send_event(kind, num_slot, x, y); 412 413 if (kind != INPUT_MULTI_TOUCH_TYPE_BEGIN && 414 kind != INPUT_MULTI_TOUCH_TYPE_UPDATE && 415 kind != INPUT_MULTI_TOUCH_TYPE_CANCEL && 416 kind != INPUT_MULTI_TOUCH_TYPE_END) 417 { 418 g_dbus_method_invocation_return_error( 419 invocation, DBUS_DISPLAY_ERROR, 420 DBUS_DISPLAY_ERROR_INVALID, 421 "Invalid touch event kind"); 422 return DBUS_METHOD_INVOCATION_HANDLED; 423 } 424 width = qemu_console_get_width(ddc->dcl.con, 0); 425 height = qemu_console_get_height(ddc->dcl.con, 0); 426 427 console_handle_touch_event(ddc->dcl.con, touch_slots, 428 num_slot, width, height, 429 x, y, kind, &error); 430 if (error != NULL) { 431 g_dbus_method_invocation_return_error( 432 invocation, DBUS_DISPLAY_ERROR, 433 DBUS_DISPLAY_ERROR_INVALID, 434 error_get_pretty(error), NULL); 435 error_free(error); 436 } else { 437 qemu_dbus_display1_multi_touch_complete_send_event(ddc->iface_touch, 438 invocation); 439 } 440 return DBUS_METHOD_INVOCATION_HANDLED; 441 } 442 443 static gboolean 444 dbus_mouse_set_pos(DBusDisplayConsole *ddc, 445 GDBusMethodInvocation *invocation, 446 guint x, guint y) 447 { 448 int width, height; 449 450 trace_dbus_mouse_set_pos(x, y); 451 452 if (!qemu_input_is_absolute(ddc->dcl.con)) { 453 g_dbus_method_invocation_return_error( 454 invocation, DBUS_DISPLAY_ERROR, 455 DBUS_DISPLAY_ERROR_INVALID, 456 "Mouse is not absolute"); 457 return DBUS_METHOD_INVOCATION_HANDLED; 458 } 459 460 width = qemu_console_get_width(ddc->dcl.con, 0); 461 height = qemu_console_get_height(ddc->dcl.con, 0); 462 if (x >= width || y >= height) { 463 g_dbus_method_invocation_return_error( 464 invocation, DBUS_DISPLAY_ERROR, 465 DBUS_DISPLAY_ERROR_INVALID, 466 "Invalid mouse position"); 467 return DBUS_METHOD_INVOCATION_HANDLED; 468 } 469 qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_X, x, 0, width); 470 qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_Y, y, 0, height); 471 qemu_input_event_sync(); 472 473 qemu_dbus_display1_mouse_complete_set_abs_position(ddc->iface_mouse, 474 invocation); 475 476 return DBUS_METHOD_INVOCATION_HANDLED; 477 } 478 479 static gboolean 480 dbus_mouse_press(DBusDisplayConsole *ddc, 481 GDBusMethodInvocation *invocation, 482 guint button) 483 { 484 trace_dbus_mouse_press(button); 485 486 qemu_input_queue_btn(ddc->dcl.con, button, true); 487 qemu_input_event_sync(); 488 489 qemu_dbus_display1_mouse_complete_press(ddc->iface_mouse, invocation); 490 491 return DBUS_METHOD_INVOCATION_HANDLED; 492 } 493 494 static gboolean 495 dbus_mouse_release(DBusDisplayConsole *ddc, 496 GDBusMethodInvocation *invocation, 497 guint button) 498 { 499 trace_dbus_mouse_release(button); 500 501 qemu_input_queue_btn(ddc->dcl.con, button, false); 502 qemu_input_event_sync(); 503 504 qemu_dbus_display1_mouse_complete_release(ddc->iface_mouse, invocation); 505 506 return DBUS_METHOD_INVOCATION_HANDLED; 507 } 508 509 static void 510 dbus_mouse_update_is_absolute(DBusDisplayConsole *ddc) 511 { 512 g_object_set(ddc->iface_mouse, 513 "is-absolute", qemu_input_is_absolute(ddc->dcl.con), 514 NULL); 515 } 516 517 static void 518 dbus_mouse_mode_change(Notifier *notify, void *data) 519 { 520 DBusDisplayConsole *ddc = 521 container_of(notify, DBusDisplayConsole, mouse_mode_notifier); 522 523 dbus_mouse_update_is_absolute(ddc); 524 } 525 526 int dbus_display_console_get_index(DBusDisplayConsole *ddc) 527 { 528 return qemu_console_get_index(ddc->dcl.con); 529 } 530 531 DBusDisplayConsole * 532 dbus_display_console_new(DBusDisplay *display, QemuConsole *con) 533 { 534 g_autofree char *path = NULL; 535 g_autofree char *label = NULL; 536 char device_addr[256] = ""; 537 DBusDisplayConsole *ddc; 538 int idx, i; 539 const char *interfaces[] = { 540 "org.qemu.Display1.Keyboard", 541 "org.qemu.Display1.Mouse", 542 "org.qemu.Display1.MultiTouch", 543 NULL 544 }; 545 546 assert(display); 547 assert(con); 548 549 label = qemu_console_get_label(con); 550 idx = qemu_console_get_index(con); 551 path = g_strdup_printf(DBUS_DISPLAY1_ROOT "/Console_%d", idx); 552 ddc = g_object_new(DBUS_DISPLAY_TYPE_CONSOLE, 553 "g-object-path", path, 554 NULL); 555 ddc->display = display; 556 ddc->dcl.con = con; 557 /* handle errors, and skip non graphics? */ 558 qemu_console_fill_device_address( 559 con, device_addr, sizeof(device_addr), NULL); 560 561 ddc->iface = qemu_dbus_display1_console_skeleton_new(); 562 g_object_set(ddc->iface, 563 "label", label, 564 "type", qemu_console_is_graphic(con) ? "Graphic" : "Text", 565 "head", qemu_console_get_head(con), 566 "width", qemu_console_get_width(con, 0), 567 "height", qemu_console_get_height(con, 0), 568 "device-address", device_addr, 569 "interfaces", interfaces, 570 NULL); 571 g_object_connect(ddc->iface, 572 "swapped-signal::handle-register-listener", 573 dbus_console_register_listener, ddc, 574 "swapped-signal::handle-set-uiinfo", 575 dbus_console_set_ui_info, ddc, 576 NULL); 577 g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), 578 G_DBUS_INTERFACE_SKELETON(ddc->iface)); 579 580 ddc->kbd = qkbd_state_init(con); 581 ddc->iface_kbd = qemu_dbus_display1_keyboard_skeleton_new(); 582 qemu_add_led_event_handler(dbus_kbd_qemu_leds_updated, ddc); 583 g_object_connect(ddc->iface_kbd, 584 "swapped-signal::handle-press", dbus_kbd_press, ddc, 585 "swapped-signal::handle-release", dbus_kbd_release, ddc, 586 NULL); 587 g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), 588 G_DBUS_INTERFACE_SKELETON(ddc->iface_kbd)); 589 590 ddc->iface_mouse = qemu_dbus_display1_mouse_skeleton_new(); 591 g_object_connect(ddc->iface_mouse, 592 "swapped-signal::handle-set-abs-position", dbus_mouse_set_pos, ddc, 593 "swapped-signal::handle-rel-motion", dbus_mouse_rel_motion, ddc, 594 "swapped-signal::handle-press", dbus_mouse_press, ddc, 595 "swapped-signal::handle-release", dbus_mouse_release, ddc, 596 NULL); 597 g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), 598 G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse)); 599 600 ddc->iface_touch = qemu_dbus_display1_multi_touch_skeleton_new(); 601 g_object_connect(ddc->iface_touch, 602 "swapped-signal::handle-send-event", dbus_touch_send_event, ddc, 603 NULL); 604 qemu_dbus_display1_multi_touch_set_max_slots(ddc->iface_touch, 605 INPUT_EVENT_SLOTS_MAX); 606 g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), 607 G_DBUS_INTERFACE_SKELETON(ddc->iface_touch)); 608 609 for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) { 610 struct touch_slot *slot = &touch_slots[i]; 611 slot->tracking_id = -1; 612 } 613 614 register_displaychangelistener(&ddc->dcl); 615 ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change; 616 qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier); 617 dbus_mouse_update_is_absolute(ddc); 618 619 return ddc; 620 } 621