]>
Commit | Line | Data |
---|---|---|
6f5943cf | 1 | #include "hw/qdev.h" |
c8b405b6 GH |
2 | #include "sysemu/sysemu.h" |
3 | #include "qapi-types.h" | |
e842c68d | 4 | #include "qmp-commands.h" |
c43ce551 | 5 | #include "trace.h" |
c8b405b6 | 6 | #include "ui/input.h" |
c43ce551 | 7 | #include "ui/console.h" |
c8b405b6 GH |
8 | |
9 | struct QemuInputHandlerState { | |
10 | DeviceState *dev; | |
11 | QemuInputHandler *handler; | |
12 | int id; | |
13 | int events; | |
6f5943cf | 14 | QemuConsole *con; |
c8b405b6 GH |
15 | QTAILQ_ENTRY(QemuInputHandlerState) node; |
16 | }; | |
17 | static QTAILQ_HEAD(, QemuInputHandlerState) handlers = | |
18 | QTAILQ_HEAD_INITIALIZER(handlers); | |
4a33f45e GH |
19 | static NotifierList mouse_mode_notifiers = |
20 | NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); | |
c8b405b6 GH |
21 | |
22 | QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, | |
23 | QemuInputHandler *handler) | |
24 | { | |
25 | QemuInputHandlerState *s = g_new0(QemuInputHandlerState, 1); | |
26 | static int id = 1; | |
27 | ||
28 | s->dev = dev; | |
29 | s->handler = handler; | |
30 | s->id = id++; | |
31 | QTAILQ_INSERT_TAIL(&handlers, s, node); | |
4a33f45e GH |
32 | |
33 | qemu_input_check_mode_change(); | |
c8b405b6 GH |
34 | return s; |
35 | } | |
36 | ||
37 | void qemu_input_handler_activate(QemuInputHandlerState *s) | |
38 | { | |
39 | QTAILQ_REMOVE(&handlers, s, node); | |
40 | QTAILQ_INSERT_HEAD(&handlers, s, node); | |
4a33f45e | 41 | qemu_input_check_mode_change(); |
c8b405b6 GH |
42 | } |
43 | ||
528728fd GH |
44 | void qemu_input_handler_deactivate(QemuInputHandlerState *s) |
45 | { | |
46 | QTAILQ_REMOVE(&handlers, s, node); | |
47 | QTAILQ_INSERT_TAIL(&handlers, s, node); | |
48 | qemu_input_check_mode_change(); | |
49 | } | |
50 | ||
c8b405b6 GH |
51 | void qemu_input_handler_unregister(QemuInputHandlerState *s) |
52 | { | |
53 | QTAILQ_REMOVE(&handlers, s, node); | |
54 | g_free(s); | |
4a33f45e | 55 | qemu_input_check_mode_change(); |
c8b405b6 GH |
56 | } |
57 | ||
6f5943cf GH |
58 | void qemu_input_handler_bind(QemuInputHandlerState *s, |
59 | const char *device_id, int head, | |
60 | Error **errp) | |
61 | { | |
62 | DeviceState *dev; | |
63 | QemuConsole *con; | |
64 | ||
65 | dev = qdev_find_recursive(sysbus_get_default(), device_id); | |
66 | if (dev == NULL) { | |
67 | error_set(errp, QERR_DEVICE_NOT_FOUND, device_id); | |
68 | return; | |
69 | } | |
70 | ||
71 | con = qemu_console_lookup_by_device(dev, head); | |
72 | if (con == NULL) { | |
73 | error_setg(errp, "Device %s is not bound to a QemuConsole", device_id); | |
74 | return; | |
75 | } | |
76 | ||
77 | s->con = con; | |
78 | } | |
79 | ||
c8b405b6 | 80 | static QemuInputHandlerState* |
6f5943cf | 81 | qemu_input_find_handler(uint32_t mask, QemuConsole *con) |
c8b405b6 GH |
82 | { |
83 | QemuInputHandlerState *s; | |
84 | ||
85 | QTAILQ_FOREACH(s, &handlers, node) { | |
6f5943cf GH |
86 | if (s->con == NULL || s->con != con) { |
87 | continue; | |
88 | } | |
89 | if (mask & s->handler->mask) { | |
90 | return s; | |
91 | } | |
92 | } | |
93 | ||
94 | QTAILQ_FOREACH(s, &handlers, node) { | |
95 | if (s->con != NULL) { | |
96 | continue; | |
97 | } | |
c8b405b6 GH |
98 | if (mask & s->handler->mask) { |
99 | return s; | |
100 | } | |
101 | } | |
102 | return NULL; | |
103 | } | |
104 | ||
d3535431 GH |
105 | static void qemu_input_transform_abs_rotate(InputEvent *evt) |
106 | { | |
107 | switch (graphic_rotate) { | |
108 | case 90: | |
109 | if (evt->abs->axis == INPUT_AXIS_X) { | |
110 | evt->abs->axis = INPUT_AXIS_Y; | |
111 | } else if (evt->abs->axis == INPUT_AXIS_Y) { | |
112 | evt->abs->axis = INPUT_AXIS_X; | |
113 | evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; | |
114 | } | |
115 | break; | |
116 | case 180: | |
117 | evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; | |
118 | break; | |
119 | case 270: | |
120 | if (evt->abs->axis == INPUT_AXIS_X) { | |
121 | evt->abs->axis = INPUT_AXIS_Y; | |
122 | evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; | |
123 | } else if (evt->abs->axis == INPUT_AXIS_Y) { | |
124 | evt->abs->axis = INPUT_AXIS_X; | |
125 | } | |
126 | break; | |
127 | } | |
128 | } | |
129 | ||
c43ce551 GH |
130 | static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) |
131 | { | |
132 | const char *name; | |
2386a907 | 133 | int qcode, idx = -1; |
c43ce551 GH |
134 | |
135 | if (src) { | |
136 | idx = qemu_console_get_index(src); | |
137 | } | |
138 | switch (evt->kind) { | |
139 | case INPUT_EVENT_KIND_KEY: | |
140 | switch (evt->key->key->kind) { | |
141 | case KEY_VALUE_KIND_NUMBER: | |
2386a907 GH |
142 | qcode = qemu_input_key_number_to_qcode(evt->key->key->number); |
143 | name = QKeyCode_lookup[qcode]; | |
c43ce551 | 144 | trace_input_event_key_number(idx, evt->key->key->number, |
2386a907 | 145 | name, evt->key->down); |
c43ce551 GH |
146 | break; |
147 | case KEY_VALUE_KIND_QCODE: | |
148 | name = QKeyCode_lookup[evt->key->key->qcode]; | |
149 | trace_input_event_key_qcode(idx, name, evt->key->down); | |
150 | break; | |
151 | case KEY_VALUE_KIND_MAX: | |
152 | /* keep gcc happy */ | |
153 | break; | |
154 | } | |
155 | break; | |
156 | case INPUT_EVENT_KIND_BTN: | |
157 | name = InputButton_lookup[evt->btn->button]; | |
158 | trace_input_event_btn(idx, name, evt->btn->down); | |
159 | break; | |
160 | case INPUT_EVENT_KIND_REL: | |
161 | name = InputAxis_lookup[evt->rel->axis]; | |
162 | trace_input_event_rel(idx, name, evt->rel->value); | |
163 | break; | |
164 | case INPUT_EVENT_KIND_ABS: | |
165 | name = InputAxis_lookup[evt->abs->axis]; | |
166 | trace_input_event_abs(idx, name, evt->abs->value); | |
167 | break; | |
168 | case INPUT_EVENT_KIND_MAX: | |
169 | /* keep gcc happy */ | |
170 | break; | |
171 | } | |
172 | } | |
173 | ||
c8b405b6 GH |
174 | void qemu_input_event_send(QemuConsole *src, InputEvent *evt) |
175 | { | |
176 | QemuInputHandlerState *s; | |
177 | ||
178 | if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { | |
179 | return; | |
180 | } | |
181 | ||
c43ce551 GH |
182 | qemu_input_event_trace(src, evt); |
183 | ||
d3535431 GH |
184 | /* pre processing */ |
185 | if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) { | |
186 | qemu_input_transform_abs_rotate(evt); | |
187 | } | |
188 | ||
189 | /* send event */ | |
6f5943cf | 190 | s = qemu_input_find_handler(1 << evt->kind, src); |
bdcc3a28 GH |
191 | if (!s) { |
192 | return; | |
193 | } | |
c8b405b6 GH |
194 | s->handler->event(s->dev, src, evt); |
195 | s->events++; | |
196 | } | |
197 | ||
198 | void qemu_input_event_sync(void) | |
199 | { | |
200 | QemuInputHandlerState *s; | |
201 | ||
202 | if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { | |
203 | return; | |
204 | } | |
205 | ||
c43ce551 GH |
206 | trace_input_event_sync(); |
207 | ||
c8b405b6 GH |
208 | QTAILQ_FOREACH(s, &handlers, node) { |
209 | if (!s->events) { | |
210 | continue; | |
211 | } | |
212 | if (s->handler->sync) { | |
213 | s->handler->sync(s->dev); | |
214 | } | |
215 | s->events = 0; | |
216 | } | |
217 | } | |
65671475 GH |
218 | |
219 | InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) | |
220 | { | |
221 | InputEvent *evt = g_new0(InputEvent, 1); | |
222 | evt->key = g_new0(InputKeyEvent, 1); | |
223 | evt->kind = INPUT_EVENT_KIND_KEY; | |
224 | evt->key->key = key; | |
225 | evt->key->down = down; | |
226 | return evt; | |
227 | } | |
228 | ||
229 | void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) | |
230 | { | |
231 | InputEvent *evt; | |
232 | evt = qemu_input_event_new_key(key, down); | |
233 | qemu_input_event_send(src, evt); | |
234 | qemu_input_event_sync(); | |
235 | qapi_free_InputEvent(evt); | |
236 | } | |
237 | ||
238 | void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) | |
239 | { | |
240 | KeyValue *key = g_new0(KeyValue, 1); | |
241 | key->kind = KEY_VALUE_KIND_NUMBER; | |
242 | key->number = num; | |
243 | qemu_input_event_send_key(src, key, down); | |
244 | } | |
245 | ||
246 | void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) | |
247 | { | |
248 | KeyValue *key = g_new0(KeyValue, 1); | |
249 | key->kind = KEY_VALUE_KIND_QCODE; | |
250 | key->qcode = q; | |
251 | qemu_input_event_send_key(src, key, down); | |
252 | } | |
43579403 GH |
253 | |
254 | InputEvent *qemu_input_event_new_btn(InputButton btn, bool down) | |
255 | { | |
256 | InputEvent *evt = g_new0(InputEvent, 1); | |
257 | evt->btn = g_new0(InputBtnEvent, 1); | |
258 | evt->kind = INPUT_EVENT_KIND_BTN; | |
259 | evt->btn->button = btn; | |
260 | evt->btn->down = down; | |
261 | return evt; | |
262 | } | |
263 | ||
264 | void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down) | |
265 | { | |
266 | InputEvent *evt; | |
267 | evt = qemu_input_event_new_btn(btn, down); | |
268 | qemu_input_event_send(src, evt); | |
269 | qapi_free_InputEvent(evt); | |
270 | } | |
271 | ||
272 | void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, | |
273 | uint32_t button_old, uint32_t button_new) | |
274 | { | |
275 | InputButton btn; | |
276 | uint32_t mask; | |
277 | ||
278 | for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) { | |
279 | mask = button_map[btn]; | |
280 | if ((button_old & mask) == (button_new & mask)) { | |
281 | continue; | |
282 | } | |
283 | qemu_input_queue_btn(src, btn, button_new & mask); | |
284 | } | |
285 | } | |
286 | ||
502c8db5 GH |
287 | bool qemu_input_is_absolute(void) |
288 | { | |
289 | QemuInputHandlerState *s; | |
290 | ||
6f5943cf GH |
291 | s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS, |
292 | NULL); | |
502c8db5 GH |
293 | return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS); |
294 | } | |
295 | ||
43579403 GH |
296 | int qemu_input_scale_axis(int value, int size_in, int size_out) |
297 | { | |
298 | if (size_in < 2) { | |
299 | return size_out / 2; | |
300 | } | |
301 | return (int64_t)value * (size_out - 1) / (size_in - 1); | |
302 | } | |
303 | ||
304 | InputEvent *qemu_input_event_new_move(InputEventKind kind, | |
305 | InputAxis axis, int value) | |
306 | { | |
307 | InputEvent *evt = g_new0(InputEvent, 1); | |
308 | InputMoveEvent *move = g_new0(InputMoveEvent, 1); | |
309 | ||
310 | evt->kind = kind; | |
311 | evt->data = move; | |
312 | move->axis = axis; | |
313 | move->value = value; | |
314 | return evt; | |
315 | } | |
316 | ||
317 | void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value) | |
318 | { | |
319 | InputEvent *evt; | |
320 | evt = qemu_input_event_new_move(INPUT_EVENT_KIND_REL, axis, value); | |
321 | qemu_input_event_send(src, evt); | |
322 | qapi_free_InputEvent(evt); | |
323 | } | |
324 | ||
325 | void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int size) | |
326 | { | |
327 | InputEvent *evt; | |
328 | int scaled = qemu_input_scale_axis(value, size, INPUT_EVENT_ABS_SIZE); | |
329 | evt = qemu_input_event_new_move(INPUT_EVENT_KIND_ABS, axis, scaled); | |
330 | qemu_input_event_send(src, evt); | |
331 | qapi_free_InputEvent(evt); | |
332 | } | |
4a33f45e GH |
333 | |
334 | void qemu_input_check_mode_change(void) | |
335 | { | |
336 | static int current_is_absolute; | |
337 | int is_absolute; | |
338 | ||
339 | is_absolute = qemu_input_is_absolute(); | |
340 | ||
341 | if (is_absolute != current_is_absolute) { | |
a8dfb1c3 | 342 | trace_input_mouse_mode(is_absolute); |
4a33f45e GH |
343 | notifier_list_notify(&mouse_mode_notifiers, NULL); |
344 | } | |
345 | ||
346 | current_is_absolute = is_absolute; | |
347 | } | |
348 | ||
349 | void qemu_add_mouse_mode_change_notifier(Notifier *notify) | |
350 | { | |
351 | notifier_list_add(&mouse_mode_notifiers, notify); | |
352 | } | |
353 | ||
354 | void qemu_remove_mouse_mode_change_notifier(Notifier *notify) | |
355 | { | |
356 | notifier_remove(notify); | |
357 | } | |
e842c68d GH |
358 | |
359 | MouseInfoList *qmp_query_mice(Error **errp) | |
360 | { | |
361 | MouseInfoList *mice_list = NULL; | |
362 | MouseInfoList *info; | |
363 | QemuInputHandlerState *s; | |
364 | bool current = true; | |
365 | ||
366 | QTAILQ_FOREACH(s, &handlers, node) { | |
367 | if (!(s->handler->mask & | |
368 | (INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS))) { | |
369 | continue; | |
370 | } | |
371 | ||
372 | info = g_new0(MouseInfoList, 1); | |
373 | info->value = g_new0(MouseInfo, 1); | |
374 | info->value->index = s->id; | |
375 | info->value->name = g_strdup(s->handler->name); | |
376 | info->value->absolute = s->handler->mask & INPUT_EVENT_MASK_ABS; | |
377 | info->value->current = current; | |
378 | ||
379 | current = false; | |
380 | info->next = mice_list; | |
381 | mice_list = info; | |
382 | } | |
383 | ||
384 | return mice_list; | |
385 | } | |
70b52f62 GH |
386 | |
387 | void do_mouse_set(Monitor *mon, const QDict *qdict) | |
388 | { | |
389 | QemuInputHandlerState *s; | |
390 | int index = qdict_get_int(qdict, "index"); | |
391 | int found = 0; | |
392 | ||
393 | QTAILQ_FOREACH(s, &handlers, node) { | |
0419f78f HB |
394 | if (s->id != index) { |
395 | continue; | |
70b52f62 | 396 | } |
0419f78f HB |
397 | if (!(s->handler->mask & (INPUT_EVENT_MASK_REL | |
398 | INPUT_EVENT_MASK_ABS))) { | |
399 | error_report("Input device '%s' is not a mouse", s->handler->name); | |
400 | return; | |
401 | } | |
402 | found = 1; | |
403 | qemu_input_handler_activate(s); | |
404 | break; | |
70b52f62 GH |
405 | } |
406 | ||
407 | if (!found) { | |
0419f78f | 408 | error_report("Mouse at index '%d' not found", index); |
70b52f62 GH |
409 | } |
410 | ||
411 | qemu_input_check_mode_change(); | |
412 | } |