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