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