xref: /openbmc/qemu/ui/input-linux.c (revision c7b11720)
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