1e0d2bd51SGerd Hoffmann /*
2e0d2bd51SGerd Hoffmann * This work is licensed under the terms of the GNU GPL, version 2 or
3e0d2bd51SGerd Hoffmann * (at your option) any later version. See the COPYING file in the
4e0d2bd51SGerd Hoffmann * top-level directory.
5e0d2bd51SGerd Hoffmann */
6e0d2bd51SGerd Hoffmann
7e0d2bd51SGerd Hoffmann #include "qemu/osdep.h"
8da34e65cSMarkus Armbruster #include "qapi/error.h"
9e0d2bd51SGerd Hoffmann #include "qemu/config-file.h"
10db725815SMarkus Armbruster #include "qemu/main-loop.h"
110b8fa32fSMarkus Armbruster #include "qemu/module.h"
12e0d2bd51SGerd Hoffmann #include "qemu/sockets.h"
13e0d2bd51SGerd Hoffmann #include "ui/input.h"
140e066b2cSGerd Hoffmann #include "qom/object_interfaces.h"
152657846fSRyan El Kochta #include "sysemu/iothread.h"
162657846fSRyan El Kochta #include "block/aio.h"
17e0d2bd51SGerd Hoffmann
18e0d2bd51SGerd Hoffmann #include <sys/ioctl.h>
19e0d2bd51SGerd Hoffmann #include "standard-headers/linux/input.h"
20db1015e9SEduardo Habkost #include "qom/object.h"
21e0d2bd51SGerd Hoffmann
linux_is_button(unsigned int lnx)222e6a64cbSGerd Hoffmann static bool linux_is_button(unsigned int lnx)
232e6a64cbSGerd Hoffmann {
242e6a64cbSGerd Hoffmann if (lnx < 0x100) {
252e6a64cbSGerd Hoffmann return false;
262e6a64cbSGerd Hoffmann }
272e6a64cbSGerd Hoffmann if (lnx >= 0x160 && lnx < 0x2c0) {
282e6a64cbSGerd Hoffmann return false;
292e6a64cbSGerd Hoffmann }
302e6a64cbSGerd Hoffmann return true;
312e6a64cbSGerd Hoffmann }
322e6a64cbSGerd Hoffmann
330e066b2cSGerd Hoffmann #define TYPE_INPUT_LINUX "input-linux"
3430b5707cSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(InputLinux,
35c734cd40SEduardo Habkost INPUT_LINUX)
360e066b2cSGerd Hoffmann
37e0d2bd51SGerd Hoffmann
38e0d2bd51SGerd Hoffmann struct InputLinux {
390e066b2cSGerd Hoffmann Object parent;
400e066b2cSGerd Hoffmann
410e066b2cSGerd Hoffmann char *evdev;
42e0d2bd51SGerd Hoffmann int fd;
43a6ccabd6SGerd Hoffmann bool repeat;
44e0d2bd51SGerd Hoffmann bool grab_request;
45e0d2bd51SGerd Hoffmann bool grab_active;
4646d921beSGerd Hoffmann bool grab_all;
47e0d2bd51SGerd Hoffmann bool keydown[KEY_CNT];
48e0d2bd51SGerd Hoffmann int keycount;
49e0d2bd51SGerd Hoffmann int wheel;
500e066b2cSGerd Hoffmann bool initialized;
512e6a64cbSGerd Hoffmann
522e6a64cbSGerd Hoffmann bool has_rel_x;
532e6a64cbSGerd Hoffmann bool has_abs_x;
542e6a64cbSGerd Hoffmann int num_keys;
552e6a64cbSGerd Hoffmann int num_btns;
56d755defdSPhilippe Voinov int abs_x_min;
57d755defdSPhilippe Voinov int abs_x_max;
58d755defdSPhilippe Voinov int abs_y_min;
59d755defdSPhilippe Voinov int abs_y_max;
601684907cSJavier Celaya struct input_event event;
611684907cSJavier Celaya int read_offset;
622e6a64cbSGerd Hoffmann
632657846fSRyan El Kochta enum GrabToggleKeys grab_toggle;
642657846fSRyan El Kochta
6546d921beSGerd Hoffmann QTAILQ_ENTRY(InputLinux) next;
66e0d2bd51SGerd Hoffmann };
67e0d2bd51SGerd Hoffmann
680e066b2cSGerd Hoffmann
6946d921beSGerd Hoffmann static QTAILQ_HEAD(, InputLinux) inputs = QTAILQ_HEAD_INITIALIZER(inputs);
7046d921beSGerd Hoffmann
input_linux_toggle_grab(InputLinux * il)71e0d2bd51SGerd Hoffmann static void input_linux_toggle_grab(InputLinux *il)
72e0d2bd51SGerd Hoffmann {
73e0d2bd51SGerd Hoffmann intptr_t request = !il->grab_active;
7446d921beSGerd Hoffmann InputLinux *item;
75e0d2bd51SGerd Hoffmann int rc;
76e0d2bd51SGerd Hoffmann
77e0d2bd51SGerd Hoffmann rc = ioctl(il->fd, EVIOCGRAB, request);
78e0d2bd51SGerd Hoffmann if (rc < 0) {
79e0d2bd51SGerd Hoffmann return;
80e0d2bd51SGerd Hoffmann }
81e0d2bd51SGerd Hoffmann il->grab_active = !il->grab_active;
8246d921beSGerd Hoffmann
8346d921beSGerd Hoffmann if (!il->grab_all) {
8446d921beSGerd Hoffmann return;
8546d921beSGerd Hoffmann }
8646d921beSGerd Hoffmann QTAILQ_FOREACH(item, &inputs, next) {
8746d921beSGerd Hoffmann if (item == il || item->grab_all) {
8846d921beSGerd Hoffmann /* avoid endless loops */
8946d921beSGerd Hoffmann continue;
9046d921beSGerd Hoffmann }
9146d921beSGerd Hoffmann if (item->grab_active != il->grab_active) {
9246d921beSGerd Hoffmann input_linux_toggle_grab(item);
9346d921beSGerd Hoffmann }
9446d921beSGerd Hoffmann }
95e0d2bd51SGerd Hoffmann }
96e0d2bd51SGerd Hoffmann
input_linux_check_toggle(InputLinux * il)972657846fSRyan El Kochta static bool input_linux_check_toggle(InputLinux *il)
982657846fSRyan El Kochta {
992657846fSRyan El Kochta switch (il->grab_toggle) {
1002657846fSRyan El Kochta case GRAB_TOGGLE_KEYS_CTRL_CTRL:
1012657846fSRyan El Kochta return il->keydown[KEY_LEFTCTRL] &&
1022657846fSRyan El Kochta il->keydown[KEY_RIGHTCTRL];
1032657846fSRyan El Kochta
1042657846fSRyan El Kochta case GRAB_TOGGLE_KEYS_ALT_ALT:
1052657846fSRyan El Kochta return il->keydown[KEY_LEFTALT] &&
1062657846fSRyan El Kochta il->keydown[KEY_RIGHTALT];
1072657846fSRyan El Kochta
108a923b471SNiklas Haas case GRAB_TOGGLE_KEYS_SHIFT_SHIFT:
109a923b471SNiklas Haas return il->keydown[KEY_LEFTSHIFT] &&
110a923b471SNiklas Haas il->keydown[KEY_RIGHTSHIFT];
111a923b471SNiklas Haas
1122657846fSRyan El Kochta case GRAB_TOGGLE_KEYS_META_META:
1132657846fSRyan El Kochta return il->keydown[KEY_LEFTMETA] &&
1142657846fSRyan El Kochta il->keydown[KEY_RIGHTMETA];
1152657846fSRyan El Kochta
1162657846fSRyan El Kochta case GRAB_TOGGLE_KEYS_SCROLLLOCK:
1172657846fSRyan El Kochta return il->keydown[KEY_SCROLLLOCK];
1182657846fSRyan El Kochta
1192657846fSRyan El Kochta case GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK:
1202657846fSRyan El Kochta return (il->keydown[KEY_LEFTCTRL] ||
1212657846fSRyan El Kochta il->keydown[KEY_RIGHTCTRL]) &&
1222657846fSRyan El Kochta il->keydown[KEY_SCROLLLOCK];
1232657846fSRyan El Kochta
1242657846fSRyan El Kochta case GRAB_TOGGLE_KEYS__MAX:
1252657846fSRyan El Kochta /* avoid gcc error */
1262657846fSRyan El Kochta break;
1272657846fSRyan El Kochta }
1282657846fSRyan El Kochta return false;
1292657846fSRyan El Kochta }
1302657846fSRyan El Kochta
input_linux_should_skip(InputLinux * il,struct input_event * event)1312657846fSRyan El Kochta static bool input_linux_should_skip(InputLinux *il,
1322657846fSRyan El Kochta struct input_event *event)
1332657846fSRyan El Kochta {
1342657846fSRyan El Kochta return (il->grab_toggle == GRAB_TOGGLE_KEYS_SCROLLLOCK ||
1352657846fSRyan El Kochta il->grab_toggle == GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK) &&
1362657846fSRyan El Kochta event->code == KEY_SCROLLLOCK;
1372657846fSRyan El Kochta }
1382657846fSRyan El Kochta
input_linux_handle_keyboard(InputLinux * il,struct input_event * event)1392330e9e7SGerd Hoffmann static void input_linux_handle_keyboard(InputLinux *il,
1402330e9e7SGerd Hoffmann struct input_event *event)
141e0d2bd51SGerd Hoffmann {
1422330e9e7SGerd Hoffmann if (event->type == EV_KEY) {
1432330e9e7SGerd Hoffmann if (event->value > 2 || (event->value > 1 && !il->repeat)) {
144e0d2bd51SGerd Hoffmann /*
145e0d2bd51SGerd Hoffmann * ignore autorepeat + unknown key events
146e0d2bd51SGerd Hoffmann * 0 == up, 1 == down, 2 == autorepeat, other == undefined
147e0d2bd51SGerd Hoffmann */
1482330e9e7SGerd Hoffmann return;
149e0d2bd51SGerd Hoffmann }
1502330e9e7SGerd Hoffmann if (event->code >= KEY_CNT) {
15181b00c96SGerd Hoffmann /*
15281b00c96SGerd Hoffmann * Should not happen. But better safe than sorry,
15381b00c96SGerd Hoffmann * and we make Coverity happy too.
15481b00c96SGerd Hoffmann */
1552330e9e7SGerd Hoffmann return;
15681b00c96SGerd Hoffmann }
1572330e9e7SGerd Hoffmann
158e0d2bd51SGerd Hoffmann /* keep track of key state */
1592330e9e7SGerd Hoffmann if (!il->keydown[event->code] && event->value) {
1602330e9e7SGerd Hoffmann il->keydown[event->code] = true;
161e0d2bd51SGerd Hoffmann il->keycount++;
162e0d2bd51SGerd Hoffmann }
1632330e9e7SGerd Hoffmann if (il->keydown[event->code] && !event->value) {
1642330e9e7SGerd Hoffmann il->keydown[event->code] = false;
165e0d2bd51SGerd Hoffmann il->keycount--;
166e0d2bd51SGerd Hoffmann }
167e0d2bd51SGerd Hoffmann
168e0d2bd51SGerd Hoffmann /* send event to guest when grab is active */
1692657846fSRyan El Kochta if (il->grab_active && !input_linux_should_skip(il, event)) {
1702330e9e7SGerd Hoffmann int qcode = qemu_input_linux_to_qcode(event->code);
1712330e9e7SGerd Hoffmann qemu_input_event_send_key_qcode(NULL, qcode, event->value);
172e0d2bd51SGerd Hoffmann }
173e0d2bd51SGerd Hoffmann
174e0d2bd51SGerd Hoffmann /* hotkey -> record switch request ... */
1752657846fSRyan El Kochta if (input_linux_check_toggle(il)) {
176e0d2bd51SGerd Hoffmann il->grab_request = true;
177e0d2bd51SGerd Hoffmann }
178e0d2bd51SGerd Hoffmann
179e0d2bd51SGerd Hoffmann /*
180e0d2bd51SGerd Hoffmann * ... and do the switch when all keys are lifted, so we
181e0d2bd51SGerd Hoffmann * confuse neither guest nor host with keys which seem to
182e0d2bd51SGerd Hoffmann * be stuck due to missing key-up events.
183e0d2bd51SGerd Hoffmann */
184e0d2bd51SGerd Hoffmann if (il->grab_request && !il->keycount) {
185e0d2bd51SGerd Hoffmann il->grab_request = false;
186e0d2bd51SGerd Hoffmann input_linux_toggle_grab(il);
187e0d2bd51SGerd Hoffmann }
1882330e9e7SGerd Hoffmann }
1892330e9e7SGerd Hoffmann }
1902330e9e7SGerd Hoffmann
input_linux_event_mouse_button(int button)191e0d2bd51SGerd Hoffmann static void input_linux_event_mouse_button(int button)
192e0d2bd51SGerd Hoffmann {
193e0d2bd51SGerd Hoffmann qemu_input_queue_btn(NULL, button, true);
194e0d2bd51SGerd Hoffmann qemu_input_event_sync();
195e0d2bd51SGerd Hoffmann qemu_input_queue_btn(NULL, button, false);
196e0d2bd51SGerd Hoffmann qemu_input_event_sync();
197e0d2bd51SGerd Hoffmann }
198e0d2bd51SGerd Hoffmann
input_linux_handle_mouse(InputLinux * il,struct input_event * event)199d4df42c4SGerd Hoffmann static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
200d4df42c4SGerd Hoffmann {
201d4df42c4SGerd Hoffmann if (!il->grab_active) {
202d4df42c4SGerd Hoffmann return;
203d4df42c4SGerd Hoffmann }
204d4df42c4SGerd Hoffmann
205d4df42c4SGerd Hoffmann switch (event->type) {
206d4df42c4SGerd Hoffmann case EV_KEY:
207d4df42c4SGerd Hoffmann switch (event->code) {
208d4df42c4SGerd Hoffmann case BTN_LEFT:
209d4df42c4SGerd Hoffmann qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value);
210d4df42c4SGerd Hoffmann break;
211d4df42c4SGerd Hoffmann case BTN_RIGHT:
212d4df42c4SGerd Hoffmann qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value);
213d4df42c4SGerd Hoffmann break;
214d4df42c4SGerd Hoffmann case BTN_MIDDLE:
215d4df42c4SGerd Hoffmann qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event->value);
216d4df42c4SGerd Hoffmann break;
217d4df42c4SGerd Hoffmann case BTN_GEAR_UP:
218d4df42c4SGerd Hoffmann qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event->value);
219d4df42c4SGerd Hoffmann break;
220d4df42c4SGerd Hoffmann case BTN_GEAR_DOWN:
221d4df42c4SGerd Hoffmann qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
222d4df42c4SGerd Hoffmann event->value);
223d4df42c4SGerd Hoffmann break;
2241266b68cSFabian Lesniak case BTN_SIDE:
2251266b68cSFabian Lesniak qemu_input_queue_btn(NULL, INPUT_BUTTON_SIDE, event->value);
2261266b68cSFabian Lesniak break;
2271266b68cSFabian Lesniak case BTN_EXTRA:
2281266b68cSFabian Lesniak qemu_input_queue_btn(NULL, INPUT_BUTTON_EXTRA, event->value);
2291266b68cSFabian Lesniak break;
230d4df42c4SGerd Hoffmann };
231d4df42c4SGerd Hoffmann break;
232d4df42c4SGerd Hoffmann case EV_REL:
233d4df42c4SGerd Hoffmann switch (event->code) {
234d4df42c4SGerd Hoffmann case REL_X:
235d4df42c4SGerd Hoffmann qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value);
236d4df42c4SGerd Hoffmann break;
237d4df42c4SGerd Hoffmann case REL_Y:
238d4df42c4SGerd Hoffmann qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value);
239d4df42c4SGerd Hoffmann break;
240d4df42c4SGerd Hoffmann case REL_WHEEL:
241d4df42c4SGerd Hoffmann il->wheel = event->value;
242d4df42c4SGerd Hoffmann break;
243d4df42c4SGerd Hoffmann }
244d4df42c4SGerd Hoffmann break;
245d755defdSPhilippe Voinov case EV_ABS:
246d755defdSPhilippe Voinov switch (event->code) {
247d755defdSPhilippe Voinov case ABS_X:
248d755defdSPhilippe Voinov qemu_input_queue_abs(NULL, INPUT_AXIS_X, event->value,
249d755defdSPhilippe Voinov il->abs_x_min, il->abs_x_max);
250d755defdSPhilippe Voinov break;
251d755defdSPhilippe Voinov case ABS_Y:
252d755defdSPhilippe Voinov qemu_input_queue_abs(NULL, INPUT_AXIS_Y, event->value,
253d755defdSPhilippe Voinov il->abs_y_min, il->abs_y_max);
254d755defdSPhilippe Voinov break;
255d755defdSPhilippe Voinov }
256d755defdSPhilippe Voinov break;
257d4df42c4SGerd Hoffmann case EV_SYN:
258d4df42c4SGerd Hoffmann qemu_input_event_sync();
259d4df42c4SGerd Hoffmann if (il->wheel != 0) {
260d4df42c4SGerd Hoffmann input_linux_event_mouse_button((il->wheel > 0)
261d4df42c4SGerd Hoffmann ? INPUT_BUTTON_WHEEL_UP
262d4df42c4SGerd Hoffmann : INPUT_BUTTON_WHEEL_DOWN);
263d4df42c4SGerd Hoffmann il->wheel = 0;
264d4df42c4SGerd Hoffmann }
265d4df42c4SGerd Hoffmann break;
266d4df42c4SGerd Hoffmann }
267d4df42c4SGerd Hoffmann }
268d4df42c4SGerd Hoffmann
input_linux_event(void * opaque)2692e6a64cbSGerd Hoffmann static void input_linux_event(void *opaque)
270e0d2bd51SGerd Hoffmann {
271e0d2bd51SGerd Hoffmann InputLinux *il = opaque;
272e0d2bd51SGerd Hoffmann int rc;
2731684907cSJavier Celaya int read_size;
2741684907cSJavier Celaya uint8_t *p = (uint8_t *)&il->event;
275e0d2bd51SGerd Hoffmann
276e0d2bd51SGerd Hoffmann for (;;) {
2771684907cSJavier Celaya read_size = sizeof(il->event) - il->read_offset;
2781684907cSJavier Celaya rc = read(il->fd, &p[il->read_offset], read_size);
2791684907cSJavier Celaya if (rc != read_size) {
280e0d2bd51SGerd Hoffmann if (rc < 0 && errno != EAGAIN) {
281e0d2bd51SGerd Hoffmann fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno));
282e0d2bd51SGerd Hoffmann qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
283e0d2bd51SGerd Hoffmann close(il->fd);
2841684907cSJavier Celaya } else if (rc > 0) {
2851684907cSJavier Celaya il->read_offset += rc;
286e0d2bd51SGerd Hoffmann }
287e0d2bd51SGerd Hoffmann break;
288e0d2bd51SGerd Hoffmann }
2891684907cSJavier Celaya il->read_offset = 0;
290e0d2bd51SGerd Hoffmann
2912e6a64cbSGerd Hoffmann if (il->num_keys) {
2921684907cSJavier Celaya input_linux_handle_keyboard(il, &il->event);
2932e6a64cbSGerd Hoffmann }
294d755defdSPhilippe Voinov if ((il->has_rel_x || il->has_abs_x) && il->num_btns) {
2951684907cSJavier Celaya input_linux_handle_mouse(il, &il->event);
296e0d2bd51SGerd Hoffmann }
297e0d2bd51SGerd Hoffmann }
2982e6a64cbSGerd Hoffmann }
299e0d2bd51SGerd Hoffmann
input_linux_complete(UserCreatable * uc,Error ** errp)3000e066b2cSGerd Hoffmann static void input_linux_complete(UserCreatable *uc, Error **errp)
301e0d2bd51SGerd Hoffmann {
3020e066b2cSGerd Hoffmann InputLinux *il = INPUT_LINUX(uc);
3032a57c55fSGerd Hoffmann uint8_t evtmap, relmap, absmap;
3042a57c55fSGerd Hoffmann uint8_t keymap[KEY_CNT / 8], keystate[KEY_CNT / 8];
3052e6a64cbSGerd Hoffmann unsigned int i;
306e0d2bd51SGerd Hoffmann int rc, ver;
307d755defdSPhilippe Voinov struct input_absinfo absinfo;
308e0d2bd51SGerd Hoffmann
309e0d2bd51SGerd Hoffmann if (!il->evdev) {
310e0d2bd51SGerd Hoffmann error_setg(errp, "no input device specified");
3110e066b2cSGerd Hoffmann return;
312e0d2bd51SGerd Hoffmann }
313e0d2bd51SGerd Hoffmann
314e0d2bd51SGerd Hoffmann il->fd = open(il->evdev, O_RDWR);
315e0d2bd51SGerd Hoffmann if (il->fd < 0) {
316e0d2bd51SGerd Hoffmann error_setg_file_open(errp, errno, il->evdev);
3170e066b2cSGerd Hoffmann return;
318e0d2bd51SGerd Hoffmann }
319*c7b11720SMarc-André Lureau if (!g_unix_set_fd_nonblocking(il->fd, true, NULL)) {
320*c7b11720SMarc-André Lureau error_setg_errno(errp, errno, "Failed to set FD nonblocking");
321*c7b11720SMarc-André Lureau return;
322*c7b11720SMarc-André Lureau }
323e0d2bd51SGerd Hoffmann
324e0d2bd51SGerd Hoffmann rc = ioctl(il->fd, EVIOCGVERSION, &ver);
325e0d2bd51SGerd Hoffmann if (rc < 0) {
326e0d2bd51SGerd Hoffmann error_setg(errp, "%s: is not an evdev device", il->evdev);
327e0d2bd51SGerd Hoffmann goto err_close;
328e0d2bd51SGerd Hoffmann }
329e0d2bd51SGerd Hoffmann
330e0d2bd51SGerd Hoffmann rc = ioctl(il->fd, EVIOCGBIT(0, sizeof(evtmap)), &evtmap);
331ce47d3d4SGerd Hoffmann if (rc < 0) {
332112c37a6SPhilippe Mathieu-Daudé goto err_read_event_bits;
333ce47d3d4SGerd Hoffmann }
334e0d2bd51SGerd Hoffmann
335e0d2bd51SGerd Hoffmann if (evtmap & (1 << EV_REL)) {
336ce47d3d4SGerd Hoffmann relmap = 0;
3372e6a64cbSGerd Hoffmann rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
338112c37a6SPhilippe Mathieu-Daudé if (rc < 0) {
339112c37a6SPhilippe Mathieu-Daudé goto err_read_event_bits;
340112c37a6SPhilippe Mathieu-Daudé }
3412e6a64cbSGerd Hoffmann if (relmap & (1 << REL_X)) {
3422e6a64cbSGerd Hoffmann il->has_rel_x = true;
343ce47d3d4SGerd Hoffmann }
344ce47d3d4SGerd Hoffmann }
345ce47d3d4SGerd Hoffmann
346ce47d3d4SGerd Hoffmann if (evtmap & (1 << EV_ABS)) {
347ce47d3d4SGerd Hoffmann absmap = 0;
3482e6a64cbSGerd Hoffmann rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
349112c37a6SPhilippe Mathieu-Daudé if (rc < 0) {
350112c37a6SPhilippe Mathieu-Daudé goto err_read_event_bits;
351112c37a6SPhilippe Mathieu-Daudé }
3522e6a64cbSGerd Hoffmann if (absmap & (1 << ABS_X)) {
3532e6a64cbSGerd Hoffmann il->has_abs_x = true;
354d755defdSPhilippe Voinov rc = ioctl(il->fd, EVIOCGABS(ABS_X), &absinfo);
355112c37a6SPhilippe Mathieu-Daudé if (rc < 0) {
356112c37a6SPhilippe Mathieu-Daudé error_setg(errp, "%s: failed to get get absolute X value",
357112c37a6SPhilippe Mathieu-Daudé il->evdev);
358112c37a6SPhilippe Mathieu-Daudé goto err_close;
359112c37a6SPhilippe Mathieu-Daudé }
360d755defdSPhilippe Voinov il->abs_x_min = absinfo.minimum;
361d755defdSPhilippe Voinov il->abs_x_max = absinfo.maximum;
362d755defdSPhilippe Voinov rc = ioctl(il->fd, EVIOCGABS(ABS_Y), &absinfo);
363112c37a6SPhilippe Mathieu-Daudé if (rc < 0) {
364112c37a6SPhilippe Mathieu-Daudé error_setg(errp, "%s: failed to get get absolute Y value",
365112c37a6SPhilippe Mathieu-Daudé il->evdev);
366112c37a6SPhilippe Mathieu-Daudé goto err_close;
367112c37a6SPhilippe Mathieu-Daudé }
368d755defdSPhilippe Voinov il->abs_y_min = absinfo.minimum;
369d755defdSPhilippe Voinov il->abs_y_max = absinfo.maximum;
370ce47d3d4SGerd Hoffmann }
371ce47d3d4SGerd Hoffmann }
372ce47d3d4SGerd Hoffmann
3732e6a64cbSGerd Hoffmann if (evtmap & (1 << EV_KEY)) {
3742e6a64cbSGerd Hoffmann memset(keymap, 0, sizeof(keymap));
3752e6a64cbSGerd Hoffmann rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap);
376112c37a6SPhilippe Mathieu-Daudé if (rc < 0) {
377112c37a6SPhilippe Mathieu-Daudé goto err_read_event_bits;
378112c37a6SPhilippe Mathieu-Daudé }
3792a57c55fSGerd Hoffmann rc = ioctl(il->fd, EVIOCGKEY(sizeof(keystate)), keystate);
380112c37a6SPhilippe Mathieu-Daudé if (rc < 0) {
381112c37a6SPhilippe Mathieu-Daudé error_setg(errp, "%s: failed to get global key state", il->evdev);
382112c37a6SPhilippe Mathieu-Daudé goto err_close;
383112c37a6SPhilippe Mathieu-Daudé }
3842e6a64cbSGerd Hoffmann for (i = 0; i < KEY_CNT; i++) {
3852e6a64cbSGerd Hoffmann if (keymap[i / 8] & (1 << (i % 8))) {
3862e6a64cbSGerd Hoffmann if (linux_is_button(i)) {
3872e6a64cbSGerd Hoffmann il->num_btns++;
388e0d2bd51SGerd Hoffmann } else {
3892e6a64cbSGerd Hoffmann il->num_keys++;
390e0d2bd51SGerd Hoffmann }
3912a57c55fSGerd Hoffmann if (keystate[i / 8] & (1 << (i % 8))) {
3922a57c55fSGerd Hoffmann il->keydown[i] = true;
3932a57c55fSGerd Hoffmann il->keycount++;
3942a57c55fSGerd Hoffmann }
3952e6a64cbSGerd Hoffmann }
3962e6a64cbSGerd Hoffmann }
3972e6a64cbSGerd Hoffmann }
3982e6a64cbSGerd Hoffmann
3992e6a64cbSGerd Hoffmann qemu_set_fd_handler(il->fd, input_linux_event, NULL, il);
4002a57c55fSGerd Hoffmann if (il->keycount) {
4012a57c55fSGerd Hoffmann /* delay grab until all keys are released */
4022a57c55fSGerd Hoffmann il->grab_request = true;
4032a57c55fSGerd Hoffmann } else {
404e0d2bd51SGerd Hoffmann input_linux_toggle_grab(il);
4052a57c55fSGerd Hoffmann }
40646d921beSGerd Hoffmann QTAILQ_INSERT_TAIL(&inputs, il, next);
4070e066b2cSGerd Hoffmann il->initialized = true;
4080e066b2cSGerd Hoffmann return;
409e0d2bd51SGerd Hoffmann
410112c37a6SPhilippe Mathieu-Daudé err_read_event_bits:
411112c37a6SPhilippe Mathieu-Daudé error_setg(errp, "%s: failed to read event bits", il->evdev);
412112c37a6SPhilippe Mathieu-Daudé
413e0d2bd51SGerd Hoffmann err_close:
414e0d2bd51SGerd Hoffmann close(il->fd);
4150e066b2cSGerd Hoffmann return;
416e0d2bd51SGerd Hoffmann }
417e0d2bd51SGerd Hoffmann
input_linux_instance_finalize(Object * obj)4180e066b2cSGerd Hoffmann static void input_linux_instance_finalize(Object *obj)
419e0d2bd51SGerd Hoffmann {
4200e066b2cSGerd Hoffmann InputLinux *il = INPUT_LINUX(obj);
4210e066b2cSGerd Hoffmann
4220e066b2cSGerd Hoffmann if (il->initialized) {
4230e066b2cSGerd Hoffmann QTAILQ_REMOVE(&inputs, il, next);
42433d72145SColin Xu qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
4250e066b2cSGerd Hoffmann close(il->fd);
4260e066b2cSGerd Hoffmann }
4270e066b2cSGerd Hoffmann g_free(il->evdev);
4280e066b2cSGerd Hoffmann }
4290e066b2cSGerd Hoffmann
input_linux_get_evdev(Object * obj,Error ** errp)4300e066b2cSGerd Hoffmann static char *input_linux_get_evdev(Object *obj, Error **errp)
4310e066b2cSGerd Hoffmann {
4320e066b2cSGerd Hoffmann InputLinux *il = INPUT_LINUX(obj);
4330e066b2cSGerd Hoffmann
4340e066b2cSGerd Hoffmann return g_strdup(il->evdev);
4350e066b2cSGerd Hoffmann }
4360e066b2cSGerd Hoffmann
input_linux_set_evdev(Object * obj,const char * value,Error ** errp)4370e066b2cSGerd Hoffmann static void input_linux_set_evdev(Object *obj, const char *value,
4380e066b2cSGerd Hoffmann Error **errp)
4390e066b2cSGerd Hoffmann {
4400e066b2cSGerd Hoffmann InputLinux *il = INPUT_LINUX(obj);
4410e066b2cSGerd Hoffmann
4420e066b2cSGerd Hoffmann if (il->evdev) {
4430e066b2cSGerd Hoffmann error_setg(errp, "evdev property already set");
4440e066b2cSGerd Hoffmann return;
4450e066b2cSGerd Hoffmann }
4460e066b2cSGerd Hoffmann il->evdev = g_strdup(value);
4470e066b2cSGerd Hoffmann }
4480e066b2cSGerd Hoffmann
input_linux_get_grab_all(Object * obj,Error ** errp)4490e066b2cSGerd Hoffmann static bool input_linux_get_grab_all(Object *obj, Error **errp)
4500e066b2cSGerd Hoffmann {
4510e066b2cSGerd Hoffmann InputLinux *il = INPUT_LINUX(obj);
4520e066b2cSGerd Hoffmann
4530e066b2cSGerd Hoffmann return il->grab_all;
4540e066b2cSGerd Hoffmann }
4550e066b2cSGerd Hoffmann
input_linux_set_grab_all(Object * obj,bool value,Error ** errp)4560e066b2cSGerd Hoffmann static void input_linux_set_grab_all(Object *obj, bool value,
4570e066b2cSGerd Hoffmann Error **errp)
4580e066b2cSGerd Hoffmann {
4590e066b2cSGerd Hoffmann InputLinux *il = INPUT_LINUX(obj);
4600e066b2cSGerd Hoffmann
4610e066b2cSGerd Hoffmann il->grab_all = value;
4620e066b2cSGerd Hoffmann }
4630e066b2cSGerd Hoffmann
input_linux_get_repeat(Object * obj,Error ** errp)4640e066b2cSGerd Hoffmann static bool input_linux_get_repeat(Object *obj, Error **errp)
4650e066b2cSGerd Hoffmann {
4660e066b2cSGerd Hoffmann InputLinux *il = INPUT_LINUX(obj);
4670e066b2cSGerd Hoffmann
4680e066b2cSGerd Hoffmann return il->repeat;
4690e066b2cSGerd Hoffmann }
4700e066b2cSGerd Hoffmann
input_linux_set_repeat(Object * obj,bool value,Error ** errp)4710e066b2cSGerd Hoffmann static void input_linux_set_repeat(Object *obj, bool value,
4720e066b2cSGerd Hoffmann Error **errp)
4730e066b2cSGerd Hoffmann {
4740e066b2cSGerd Hoffmann InputLinux *il = INPUT_LINUX(obj);
4750e066b2cSGerd Hoffmann
4760e066b2cSGerd Hoffmann il->repeat = value;
4770e066b2cSGerd Hoffmann }
4780e066b2cSGerd Hoffmann
input_linux_get_grab_toggle(Object * obj,Error ** errp)4792657846fSRyan El Kochta static int input_linux_get_grab_toggle(Object *obj, Error **errp)
4802657846fSRyan El Kochta {
4812657846fSRyan El Kochta InputLinux *il = INPUT_LINUX(obj);
4822657846fSRyan El Kochta
4832657846fSRyan El Kochta return il->grab_toggle;
4842657846fSRyan El Kochta }
4852657846fSRyan El Kochta
input_linux_set_grab_toggle(Object * obj,int value,Error ** errp)4862657846fSRyan El Kochta static void input_linux_set_grab_toggle(Object *obj, int value,
4872657846fSRyan El Kochta Error **errp)
4882657846fSRyan El Kochta {
4892657846fSRyan El Kochta InputLinux *il = INPUT_LINUX(obj);
4902657846fSRyan El Kochta
4912657846fSRyan El Kochta il->grab_toggle = value;
4922657846fSRyan El Kochta }
4932657846fSRyan El Kochta
input_linux_instance_init(Object * obj)4940e066b2cSGerd Hoffmann static void input_linux_instance_init(Object *obj)
4950e066b2cSGerd Hoffmann {
4960e066b2cSGerd Hoffmann }
4970e066b2cSGerd Hoffmann
input_linux_class_init(ObjectClass * oc,void * data)4980e066b2cSGerd Hoffmann static void input_linux_class_init(ObjectClass *oc, void *data)
4990e066b2cSGerd Hoffmann {
5000e066b2cSGerd Hoffmann UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
5010e066b2cSGerd Hoffmann
5020e066b2cSGerd Hoffmann ucc->complete = input_linux_complete;
5037da4e3bbSEduardo Habkost
5047da4e3bbSEduardo Habkost object_class_property_add_str(oc, "evdev",
5057da4e3bbSEduardo Habkost input_linux_get_evdev,
5067da4e3bbSEduardo Habkost input_linux_set_evdev);
5077da4e3bbSEduardo Habkost object_class_property_add_bool(oc, "grab_all",
5087da4e3bbSEduardo Habkost input_linux_get_grab_all,
5097da4e3bbSEduardo Habkost input_linux_set_grab_all);
5107da4e3bbSEduardo Habkost object_class_property_add_bool(oc, "repeat",
5117da4e3bbSEduardo Habkost input_linux_get_repeat,
5127da4e3bbSEduardo Habkost input_linux_set_repeat);
5137da4e3bbSEduardo Habkost object_class_property_add_enum(oc, "grab-toggle", "GrabToggleKeys",
5147da4e3bbSEduardo Habkost &GrabToggleKeys_lookup,
5157da4e3bbSEduardo Habkost input_linux_get_grab_toggle,
5167da4e3bbSEduardo Habkost input_linux_set_grab_toggle);
5170e066b2cSGerd Hoffmann }
5180e066b2cSGerd Hoffmann
5190e066b2cSGerd Hoffmann static const TypeInfo input_linux_info = {
5200e066b2cSGerd Hoffmann .name = TYPE_INPUT_LINUX,
5210e066b2cSGerd Hoffmann .parent = TYPE_OBJECT,
5220e066b2cSGerd Hoffmann .class_init = input_linux_class_init,
5230e066b2cSGerd Hoffmann .instance_size = sizeof(InputLinux),
5240e066b2cSGerd Hoffmann .instance_init = input_linux_instance_init,
5250e066b2cSGerd Hoffmann .instance_finalize = input_linux_instance_finalize,
5260e066b2cSGerd Hoffmann .interfaces = (InterfaceInfo[]) {
5270e066b2cSGerd Hoffmann { TYPE_USER_CREATABLE },
5280e066b2cSGerd Hoffmann { }
5290e066b2cSGerd Hoffmann }
530e0d2bd51SGerd Hoffmann };
531e0d2bd51SGerd Hoffmann
register_types(void)5320e066b2cSGerd Hoffmann static void register_types(void)
533e0d2bd51SGerd Hoffmann {
5340e066b2cSGerd Hoffmann type_register_static(&input_linux_info);
535e0d2bd51SGerd Hoffmann }
5360e066b2cSGerd Hoffmann
5370e066b2cSGerd Hoffmann type_init(register_types);
538