master
c 225 lines 5.27 KB
Raw
1 /*
2 * Keyboard and mouse input dispatch via D-Bus.
3 *
4 * Copyright (C) 2026 Red Hat, Inc.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10
11 #include "ui/dbus-display1.h"
12 #include "ui/input.h"
13 #include "trace.h"
14 #include "qemu-vnc.h"
15
16 static NotifierList mouse_mode_notifiers =
17 NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
18 static NotifierList led_notifiers =
19 NOTIFIER_LIST_INITIALIZER(led_notifiers);
20
21 /* Track the target console for pending mouse events (used by sync) */
22 static QemuConsole *mouse_target;
23
24 /*
25 * The D-Bus Keyboard.Modifiers property uses the same
26 * bit layout as QEMU's LED constants.
27 */
28 static guint modifiers;
29
30 void qemu_input_led_notifier_add(Notifier *n)
31 {
32 notifier_list_add(&led_notifiers, n);
33 }
34
35 void qemu_input_led_notifier_remove(Notifier *n)
36 {
37 notifier_remove(n);
38 }
39
40 uint32_t qemu_input_get_leds_mask(const QemuConsole *con)
41 {
42 return modifiers;
43 }
44
45 static void
46 on_keyboard_modifiers_changed(GObject *gobject, GParamSpec *pspec,
47 gpointer user_data)
48 {
49 modifiers = qemu_dbus_display1_keyboard_get_modifiers(
50 QEMU_DBUS_DISPLAY1_KEYBOARD(gobject));
51
52 notifier_list_notify(&led_notifiers, NULL);
53 }
54
55 void qemu_add_mouse_mode_change_notifier(Notifier *notify)
56 {
57 notifier_list_add(&mouse_mode_notifiers, notify);
58 }
59
60 void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
61 {
62 notifier_remove(notify);
63 }
64
65 void qemu_input_event_send_key_delay(uint32_t delay_ms)
66 {
67 }
68
69 void qemu_input_event_send_key_linux(QemuConsole *src, unsigned int lnx,
70 bool down)
71 {
72 QemuDBusDisplay1Keyboard *kbd;
73 guint qnum;
74
75 trace_qemu_vnc_key_event(lnx, down);
76
77 if (!src) {
78 return;
79 }
80 kbd = console_get_keyboard(src);
81 if (!kbd) {
82 return;
83 }
84
85 if (lnx >= qemu_input_map_linux_to_qnum_len) {
86 return;
87 }
88 qnum = qemu_input_map_linux_to_qnum[lnx];
89
90 if (down) {
91 qemu_dbus_display1_keyboard_call_press(
92 kbd, qnum,
93 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
94 } else {
95 qemu_dbus_display1_keyboard_call_release(
96 kbd, qnum,
97 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
98 }
99 }
100
101 static guint abs_x, abs_y;
102 static bool abs_pending;
103 static gint rel_dx, rel_dy;
104 static bool rel_pending;
105
106 void qemu_input_queue_abs(QemuConsole *src, InputAxis axis,
107 int value, int min_in, int max_in)
108 {
109 if (axis == INPUT_AXIS_X) {
110 abs_x = value;
111 } else if (axis == INPUT_AXIS_Y) {
112 abs_y = value;
113 }
114 abs_pending = true;
115 mouse_target = src;
116 }
117
118 void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value)
119 {
120 if (axis == INPUT_AXIS_X) {
121 rel_dx += value;
122 } else if (axis == INPUT_AXIS_Y) {
123 rel_dy += value;
124 }
125 rel_pending = true;
126 mouse_target = src;
127 }
128
129 void qemu_input_event_sync(void)
130 {
131 QemuDBusDisplay1Mouse *mouse;
132
133 if (!mouse_target) {
134 return;
135 }
136
137 mouse = console_get_mouse(mouse_target);
138 if (!mouse) {
139 abs_pending = false;
140 rel_pending = false;
141 return;
142 }
143
144 if (abs_pending) {
145 trace_qemu_vnc_input_abs(abs_x, abs_y);
146 abs_pending = false;
147 qemu_dbus_display1_mouse_call_set_abs_position(
148 mouse, abs_x, abs_y,
149 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
150 }
151
152 if (rel_pending) {
153 trace_qemu_vnc_input_rel(rel_dx, rel_dy);
154 rel_pending = false;
155 qemu_dbus_display1_mouse_call_rel_motion(
156 mouse, rel_dx, rel_dy,
157 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
158 rel_dx = 0;
159 rel_dy = 0;
160 }
161 }
162
163 bool qemu_input_is_absolute(const QemuConsole *con)
164 {
165 QemuDBusDisplay1Mouse *mouse;
166
167 if (!con) {
168 return false;
169 }
170 mouse = console_get_mouse(con);
171
172 if (!mouse) {
173 return false;
174 }
175 return qemu_dbus_display1_mouse_get_is_absolute(mouse);
176 }
177
178 static void
179 on_mouse_is_absolute_changed(GObject *gobject, GParamSpec *pspec,
180 gpointer user_data)
181 {
182 notifier_list_notify(&mouse_mode_notifiers, NULL);
183 }
184
185 void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map,
186 uint32_t button_old, uint32_t button_new)
187 {
188 QemuDBusDisplay1Mouse *mouse;
189 uint32_t changed;
190 int i;
191
192 if (!src) {
193 return;
194 }
195 mouse = console_get_mouse(src);
196 if (!mouse) {
197 return;
198 }
199
200 changed = button_old ^ button_new;
201 for (i = 0; i < 32; i++) {
202 if (!(changed & (1u << i))) {
203 continue;
204 }
205 trace_qemu_vnc_input_btn(i, !!(button_new & (1u << i)));
206 if (button_new & (1u << i)) {
207 qemu_dbus_display1_mouse_call_press(
208 mouse, i,
209 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
210 } else {
211 qemu_dbus_display1_mouse_call_release(
212 mouse, i,
213 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
214 }
215 }
216 }
217
218 void input_setup(QemuDBusDisplay1Keyboard *kbd,
219 QemuDBusDisplay1Mouse *mouse)
220 {
221 g_signal_connect(kbd, "notify::modifiers",
222 G_CALLBACK(on_keyboard_modifiers_changed), NULL);
223 g_signal_connect(mouse, "notify::is-absolute",
224 G_CALLBACK(on_mouse_is_absolute_changed), NULL);
225 }