dbus-console.c 16 KB

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