xref: /openbmc/qemu/ui/input-linux.c (revision db1015e9)
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"
20*db1015e9SEduardo Habkost #include "qom/object.h"
21e0d2bd51SGerd Hoffmann 
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"
34*db1015e9SEduardo Habkost typedef struct InputLinux InputLinux;
35*db1015e9SEduardo Habkost typedef struct InputLinuxClass InputLinuxClass;
360e066b2cSGerd Hoffmann #define INPUT_LINUX(obj) \
370e066b2cSGerd Hoffmann     OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX)
380e066b2cSGerd Hoffmann #define INPUT_LINUX_GET_CLASS(obj) \
390e066b2cSGerd Hoffmann     OBJECT_GET_CLASS(InputLinuxClass, (obj), TYPE_INPUT_LINUX)
400e066b2cSGerd Hoffmann #define INPUT_LINUX_CLASS(klass) \
410e066b2cSGerd Hoffmann     OBJECT_CLASS_CHECK(InputLinuxClass, (klass), TYPE_INPUT_LINUX)
420e066b2cSGerd Hoffmann 
43e0d2bd51SGerd Hoffmann 
44e0d2bd51SGerd Hoffmann struct InputLinux {
450e066b2cSGerd Hoffmann     Object parent;
460e066b2cSGerd Hoffmann 
470e066b2cSGerd Hoffmann     char        *evdev;
48e0d2bd51SGerd Hoffmann     int         fd;
49a6ccabd6SGerd Hoffmann     bool        repeat;
50e0d2bd51SGerd Hoffmann     bool        grab_request;
51e0d2bd51SGerd Hoffmann     bool        grab_active;
5246d921beSGerd Hoffmann     bool        grab_all;
53e0d2bd51SGerd Hoffmann     bool        keydown[KEY_CNT];
54e0d2bd51SGerd Hoffmann     int         keycount;
55e0d2bd51SGerd Hoffmann     int         wheel;
560e066b2cSGerd Hoffmann     bool        initialized;
572e6a64cbSGerd Hoffmann 
582e6a64cbSGerd Hoffmann     bool        has_rel_x;
592e6a64cbSGerd Hoffmann     bool        has_abs_x;
602e6a64cbSGerd Hoffmann     int         num_keys;
612e6a64cbSGerd Hoffmann     int         num_btns;
62d755defdSPhilippe Voinov     int         abs_x_min;
63d755defdSPhilippe Voinov     int         abs_x_max;
64d755defdSPhilippe Voinov     int         abs_y_min;
65d755defdSPhilippe Voinov     int         abs_y_max;
661684907cSJavier Celaya     struct input_event event;
671684907cSJavier Celaya     int         read_offset;
682e6a64cbSGerd Hoffmann 
692657846fSRyan El Kochta     enum GrabToggleKeys grab_toggle;
702657846fSRyan El Kochta 
7146d921beSGerd Hoffmann     QTAILQ_ENTRY(InputLinux) next;
72e0d2bd51SGerd Hoffmann };
73e0d2bd51SGerd Hoffmann 
740e066b2cSGerd Hoffmann struct InputLinuxClass {
750e066b2cSGerd Hoffmann     ObjectClass parent_class;
760e066b2cSGerd Hoffmann };
770e066b2cSGerd Hoffmann 
7846d921beSGerd Hoffmann static QTAILQ_HEAD(, InputLinux) inputs = QTAILQ_HEAD_INITIALIZER(inputs);
7946d921beSGerd Hoffmann 
80e0d2bd51SGerd Hoffmann static void input_linux_toggle_grab(InputLinux *il)
81e0d2bd51SGerd Hoffmann {
82e0d2bd51SGerd Hoffmann     intptr_t request = !il->grab_active;
8346d921beSGerd Hoffmann     InputLinux *item;
84e0d2bd51SGerd Hoffmann     int rc;
85e0d2bd51SGerd Hoffmann 
86e0d2bd51SGerd Hoffmann     rc = ioctl(il->fd, EVIOCGRAB, request);
87e0d2bd51SGerd Hoffmann     if (rc < 0) {
88e0d2bd51SGerd Hoffmann         return;
89e0d2bd51SGerd Hoffmann     }
90e0d2bd51SGerd Hoffmann     il->grab_active = !il->grab_active;
9146d921beSGerd Hoffmann 
9246d921beSGerd Hoffmann     if (!il->grab_all) {
9346d921beSGerd Hoffmann         return;
9446d921beSGerd Hoffmann     }
9546d921beSGerd Hoffmann     QTAILQ_FOREACH(item, &inputs, next) {
9646d921beSGerd Hoffmann         if (item == il || item->grab_all) {
9746d921beSGerd Hoffmann             /* avoid endless loops */
9846d921beSGerd Hoffmann             continue;
9946d921beSGerd Hoffmann         }
10046d921beSGerd Hoffmann         if (item->grab_active != il->grab_active) {
10146d921beSGerd Hoffmann             input_linux_toggle_grab(item);
10246d921beSGerd Hoffmann         }
10346d921beSGerd Hoffmann     }
104e0d2bd51SGerd Hoffmann }
105e0d2bd51SGerd Hoffmann 
1062657846fSRyan El Kochta static bool input_linux_check_toggle(InputLinux *il)
1072657846fSRyan El Kochta {
1082657846fSRyan El Kochta     switch (il->grab_toggle) {
1092657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_CTRL_CTRL:
1102657846fSRyan El Kochta         return il->keydown[KEY_LEFTCTRL] &&
1112657846fSRyan El Kochta             il->keydown[KEY_RIGHTCTRL];
1122657846fSRyan El Kochta 
1132657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_ALT_ALT:
1142657846fSRyan El Kochta         return il->keydown[KEY_LEFTALT] &&
1152657846fSRyan El Kochta             il->keydown[KEY_RIGHTALT];
1162657846fSRyan El Kochta 
117a923b471SNiklas Haas     case GRAB_TOGGLE_KEYS_SHIFT_SHIFT:
118a923b471SNiklas Haas         return il->keydown[KEY_LEFTSHIFT] &&
119a923b471SNiklas Haas             il->keydown[KEY_RIGHTSHIFT];
120a923b471SNiklas Haas 
1212657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_META_META:
1222657846fSRyan El Kochta         return il->keydown[KEY_LEFTMETA] &&
1232657846fSRyan El Kochta             il->keydown[KEY_RIGHTMETA];
1242657846fSRyan El Kochta 
1252657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_SCROLLLOCK:
1262657846fSRyan El Kochta         return il->keydown[KEY_SCROLLLOCK];
1272657846fSRyan El Kochta 
1282657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK:
1292657846fSRyan El Kochta         return (il->keydown[KEY_LEFTCTRL] ||
1302657846fSRyan El Kochta                 il->keydown[KEY_RIGHTCTRL]) &&
1312657846fSRyan El Kochta             il->keydown[KEY_SCROLLLOCK];
1322657846fSRyan El Kochta 
1332657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS__MAX:
1342657846fSRyan El Kochta         /* avoid gcc error */
1352657846fSRyan El Kochta         break;
1362657846fSRyan El Kochta     }
1372657846fSRyan El Kochta     return false;
1382657846fSRyan El Kochta }
1392657846fSRyan El Kochta 
1402657846fSRyan El Kochta static bool input_linux_should_skip(InputLinux *il,
1412657846fSRyan El Kochta                                     struct input_event *event)
1422657846fSRyan El Kochta {
1432657846fSRyan El Kochta     return (il->grab_toggle == GRAB_TOGGLE_KEYS_SCROLLLOCK ||
1442657846fSRyan El Kochta             il->grab_toggle == GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK) &&
1452657846fSRyan El Kochta             event->code == KEY_SCROLLLOCK;
1462657846fSRyan El Kochta }
1472657846fSRyan El Kochta 
1482330e9e7SGerd Hoffmann static void input_linux_handle_keyboard(InputLinux *il,
1492330e9e7SGerd Hoffmann                                         struct input_event *event)
150e0d2bd51SGerd Hoffmann {
1512330e9e7SGerd Hoffmann     if (event->type == EV_KEY) {
1522330e9e7SGerd Hoffmann         if (event->value > 2 || (event->value > 1 && !il->repeat)) {
153e0d2bd51SGerd Hoffmann             /*
154e0d2bd51SGerd Hoffmann              * ignore autorepeat + unknown key events
155e0d2bd51SGerd Hoffmann              * 0 == up, 1 == down, 2 == autorepeat, other == undefined
156e0d2bd51SGerd Hoffmann              */
1572330e9e7SGerd Hoffmann             return;
158e0d2bd51SGerd Hoffmann         }
1592330e9e7SGerd Hoffmann         if (event->code >= KEY_CNT) {
16081b00c96SGerd Hoffmann             /*
16181b00c96SGerd Hoffmann              * Should not happen.  But better safe than sorry,
16281b00c96SGerd Hoffmann              * and we make Coverity happy too.
16381b00c96SGerd Hoffmann              */
1642330e9e7SGerd Hoffmann             return;
16581b00c96SGerd Hoffmann         }
1662330e9e7SGerd Hoffmann 
167e0d2bd51SGerd Hoffmann         /* keep track of key state */
1682330e9e7SGerd Hoffmann         if (!il->keydown[event->code] && event->value) {
1692330e9e7SGerd Hoffmann             il->keydown[event->code] = true;
170e0d2bd51SGerd Hoffmann             il->keycount++;
171e0d2bd51SGerd Hoffmann         }
1722330e9e7SGerd Hoffmann         if (il->keydown[event->code] && !event->value) {
1732330e9e7SGerd Hoffmann             il->keydown[event->code] = false;
174e0d2bd51SGerd Hoffmann             il->keycount--;
175e0d2bd51SGerd Hoffmann         }
176e0d2bd51SGerd Hoffmann 
177e0d2bd51SGerd Hoffmann         /* send event to guest when grab is active */
1782657846fSRyan El Kochta         if (il->grab_active && !input_linux_should_skip(il, event)) {
1792330e9e7SGerd Hoffmann             int qcode = qemu_input_linux_to_qcode(event->code);
1802330e9e7SGerd Hoffmann             qemu_input_event_send_key_qcode(NULL, qcode, event->value);
181e0d2bd51SGerd Hoffmann         }
182e0d2bd51SGerd Hoffmann 
183e0d2bd51SGerd Hoffmann         /* hotkey -> record switch request ... */
1842657846fSRyan El Kochta         if (input_linux_check_toggle(il)) {
185e0d2bd51SGerd Hoffmann             il->grab_request = true;
186e0d2bd51SGerd Hoffmann         }
187e0d2bd51SGerd Hoffmann 
188e0d2bd51SGerd Hoffmann         /*
189e0d2bd51SGerd Hoffmann          * ... and do the switch when all keys are lifted, so we
190e0d2bd51SGerd Hoffmann          * confuse neither guest nor host with keys which seem to
191e0d2bd51SGerd Hoffmann          * be stuck due to missing key-up events.
192e0d2bd51SGerd Hoffmann          */
193e0d2bd51SGerd Hoffmann         if (il->grab_request && !il->keycount) {
194e0d2bd51SGerd Hoffmann             il->grab_request = false;
195e0d2bd51SGerd Hoffmann             input_linux_toggle_grab(il);
196e0d2bd51SGerd Hoffmann         }
1972330e9e7SGerd Hoffmann     }
1982330e9e7SGerd Hoffmann }
1992330e9e7SGerd Hoffmann 
200e0d2bd51SGerd Hoffmann static void input_linux_event_mouse_button(int button)
201e0d2bd51SGerd Hoffmann {
202e0d2bd51SGerd Hoffmann     qemu_input_queue_btn(NULL, button, true);
203e0d2bd51SGerd Hoffmann     qemu_input_event_sync();
204e0d2bd51SGerd Hoffmann     qemu_input_queue_btn(NULL, button, false);
205e0d2bd51SGerd Hoffmann     qemu_input_event_sync();
206e0d2bd51SGerd Hoffmann }
207e0d2bd51SGerd Hoffmann 
208d4df42c4SGerd Hoffmann static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
209d4df42c4SGerd Hoffmann {
210d4df42c4SGerd Hoffmann     if (!il->grab_active) {
211d4df42c4SGerd Hoffmann         return;
212d4df42c4SGerd Hoffmann     }
213d4df42c4SGerd Hoffmann 
214d4df42c4SGerd Hoffmann     switch (event->type) {
215d4df42c4SGerd Hoffmann     case EV_KEY:
216d4df42c4SGerd Hoffmann         switch (event->code) {
217d4df42c4SGerd Hoffmann         case BTN_LEFT:
218d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value);
219d4df42c4SGerd Hoffmann             break;
220d4df42c4SGerd Hoffmann         case BTN_RIGHT:
221d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value);
222d4df42c4SGerd Hoffmann             break;
223d4df42c4SGerd Hoffmann         case BTN_MIDDLE:
224d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event->value);
225d4df42c4SGerd Hoffmann             break;
226d4df42c4SGerd Hoffmann         case BTN_GEAR_UP:
227d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event->value);
228d4df42c4SGerd Hoffmann             break;
229d4df42c4SGerd Hoffmann         case BTN_GEAR_DOWN:
230d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
231d4df42c4SGerd Hoffmann                                  event->value);
232d4df42c4SGerd Hoffmann             break;
2331266b68cSFabian Lesniak         case BTN_SIDE:
2341266b68cSFabian Lesniak             qemu_input_queue_btn(NULL, INPUT_BUTTON_SIDE, event->value);
2351266b68cSFabian Lesniak             break;
2361266b68cSFabian Lesniak         case BTN_EXTRA:
2371266b68cSFabian Lesniak             qemu_input_queue_btn(NULL, INPUT_BUTTON_EXTRA, event->value);
2381266b68cSFabian Lesniak             break;
239d4df42c4SGerd Hoffmann         };
240d4df42c4SGerd Hoffmann         break;
241d4df42c4SGerd Hoffmann     case EV_REL:
242d4df42c4SGerd Hoffmann         switch (event->code) {
243d4df42c4SGerd Hoffmann         case REL_X:
244d4df42c4SGerd Hoffmann             qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value);
245d4df42c4SGerd Hoffmann             break;
246d4df42c4SGerd Hoffmann         case REL_Y:
247d4df42c4SGerd Hoffmann             qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value);
248d4df42c4SGerd Hoffmann             break;
249d4df42c4SGerd Hoffmann         case REL_WHEEL:
250d4df42c4SGerd Hoffmann             il->wheel = event->value;
251d4df42c4SGerd Hoffmann             break;
252d4df42c4SGerd Hoffmann         }
253d4df42c4SGerd Hoffmann         break;
254d755defdSPhilippe Voinov     case EV_ABS:
255d755defdSPhilippe Voinov         switch (event->code) {
256d755defdSPhilippe Voinov         case ABS_X:
257d755defdSPhilippe Voinov             qemu_input_queue_abs(NULL, INPUT_AXIS_X, event->value,
258d755defdSPhilippe Voinov                                  il->abs_x_min, il->abs_x_max);
259d755defdSPhilippe Voinov             break;
260d755defdSPhilippe Voinov         case ABS_Y:
261d755defdSPhilippe Voinov             qemu_input_queue_abs(NULL, INPUT_AXIS_Y, event->value,
262d755defdSPhilippe Voinov                                  il->abs_y_min, il->abs_y_max);
263d755defdSPhilippe Voinov             break;
264d755defdSPhilippe Voinov         }
265d755defdSPhilippe Voinov         break;
266d4df42c4SGerd Hoffmann     case EV_SYN:
267d4df42c4SGerd Hoffmann         qemu_input_event_sync();
268d4df42c4SGerd Hoffmann         if (il->wheel != 0) {
269d4df42c4SGerd Hoffmann             input_linux_event_mouse_button((il->wheel > 0)
270d4df42c4SGerd Hoffmann                                            ? INPUT_BUTTON_WHEEL_UP
271d4df42c4SGerd Hoffmann                                            : INPUT_BUTTON_WHEEL_DOWN);
272d4df42c4SGerd Hoffmann             il->wheel = 0;
273d4df42c4SGerd Hoffmann         }
274d4df42c4SGerd Hoffmann         break;
275d4df42c4SGerd Hoffmann     }
276d4df42c4SGerd Hoffmann }
277d4df42c4SGerd Hoffmann 
2782e6a64cbSGerd Hoffmann static void input_linux_event(void *opaque)
279e0d2bd51SGerd Hoffmann {
280e0d2bd51SGerd Hoffmann     InputLinux *il = opaque;
281e0d2bd51SGerd Hoffmann     int rc;
2821684907cSJavier Celaya     int read_size;
2831684907cSJavier Celaya     uint8_t *p = (uint8_t *)&il->event;
284e0d2bd51SGerd Hoffmann 
285e0d2bd51SGerd Hoffmann     for (;;) {
2861684907cSJavier Celaya         read_size = sizeof(il->event) - il->read_offset;
2871684907cSJavier Celaya         rc = read(il->fd, &p[il->read_offset], read_size);
2881684907cSJavier Celaya         if (rc != read_size) {
289e0d2bd51SGerd Hoffmann             if (rc < 0 && errno != EAGAIN) {
290e0d2bd51SGerd Hoffmann                 fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno));
291e0d2bd51SGerd Hoffmann                 qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
292e0d2bd51SGerd Hoffmann                 close(il->fd);
2931684907cSJavier Celaya             } else if (rc > 0) {
2941684907cSJavier Celaya                 il->read_offset += rc;
295e0d2bd51SGerd Hoffmann             }
296e0d2bd51SGerd Hoffmann             break;
297e0d2bd51SGerd Hoffmann         }
2981684907cSJavier Celaya         il->read_offset = 0;
299e0d2bd51SGerd Hoffmann 
3002e6a64cbSGerd Hoffmann         if (il->num_keys) {
3011684907cSJavier Celaya             input_linux_handle_keyboard(il, &il->event);
3022e6a64cbSGerd Hoffmann         }
303d755defdSPhilippe Voinov         if ((il->has_rel_x || il->has_abs_x) && il->num_btns) {
3041684907cSJavier Celaya             input_linux_handle_mouse(il, &il->event);
305e0d2bd51SGerd Hoffmann         }
306e0d2bd51SGerd Hoffmann     }
3072e6a64cbSGerd Hoffmann }
308e0d2bd51SGerd Hoffmann 
3090e066b2cSGerd Hoffmann static void input_linux_complete(UserCreatable *uc, Error **errp)
310e0d2bd51SGerd Hoffmann {
3110e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(uc);
3122a57c55fSGerd Hoffmann     uint8_t evtmap, relmap, absmap;
3132a57c55fSGerd Hoffmann     uint8_t keymap[KEY_CNT / 8], keystate[KEY_CNT / 8];
3142e6a64cbSGerd Hoffmann     unsigned int i;
315e0d2bd51SGerd Hoffmann     int rc, ver;
316d755defdSPhilippe Voinov     struct input_absinfo absinfo;
317e0d2bd51SGerd Hoffmann 
318e0d2bd51SGerd Hoffmann     if (!il->evdev) {
319e0d2bd51SGerd Hoffmann         error_setg(errp, "no input device specified");
3200e066b2cSGerd Hoffmann         return;
321e0d2bd51SGerd Hoffmann     }
322e0d2bd51SGerd Hoffmann 
323e0d2bd51SGerd Hoffmann     il->fd = open(il->evdev, O_RDWR);
324e0d2bd51SGerd Hoffmann     if (il->fd < 0)  {
325e0d2bd51SGerd Hoffmann         error_setg_file_open(errp, errno, il->evdev);
3260e066b2cSGerd Hoffmann         return;
327e0d2bd51SGerd Hoffmann     }
328e0d2bd51SGerd Hoffmann     qemu_set_nonblock(il->fd);
329e0d2bd51SGerd Hoffmann 
330e0d2bd51SGerd Hoffmann     rc = ioctl(il->fd, EVIOCGVERSION, &ver);
331e0d2bd51SGerd Hoffmann     if (rc < 0) {
332e0d2bd51SGerd Hoffmann         error_setg(errp, "%s: is not an evdev device", il->evdev);
333e0d2bd51SGerd Hoffmann         goto err_close;
334e0d2bd51SGerd Hoffmann     }
335e0d2bd51SGerd Hoffmann 
336e0d2bd51SGerd Hoffmann     rc = ioctl(il->fd, EVIOCGBIT(0, sizeof(evtmap)), &evtmap);
337ce47d3d4SGerd Hoffmann     if (rc < 0) {
338112c37a6SPhilippe Mathieu-Daudé         goto err_read_event_bits;
339ce47d3d4SGerd Hoffmann     }
340e0d2bd51SGerd Hoffmann 
341e0d2bd51SGerd Hoffmann     if (evtmap & (1 << EV_REL)) {
342ce47d3d4SGerd Hoffmann         relmap = 0;
3432e6a64cbSGerd Hoffmann         rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
344112c37a6SPhilippe Mathieu-Daudé         if (rc < 0) {
345112c37a6SPhilippe Mathieu-Daudé             goto err_read_event_bits;
346112c37a6SPhilippe Mathieu-Daudé         }
3472e6a64cbSGerd Hoffmann         if (relmap & (1 << REL_X)) {
3482e6a64cbSGerd Hoffmann             il->has_rel_x = true;
349ce47d3d4SGerd Hoffmann         }
350ce47d3d4SGerd Hoffmann     }
351ce47d3d4SGerd Hoffmann 
352ce47d3d4SGerd Hoffmann     if (evtmap & (1 << EV_ABS)) {
353ce47d3d4SGerd Hoffmann         absmap = 0;
3542e6a64cbSGerd Hoffmann         rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
355112c37a6SPhilippe Mathieu-Daudé         if (rc < 0) {
356112c37a6SPhilippe Mathieu-Daudé             goto err_read_event_bits;
357112c37a6SPhilippe Mathieu-Daudé         }
3582e6a64cbSGerd Hoffmann         if (absmap & (1 << ABS_X)) {
3592e6a64cbSGerd Hoffmann             il->has_abs_x = true;
360d755defdSPhilippe Voinov             rc = ioctl(il->fd, EVIOCGABS(ABS_X), &absinfo);
361112c37a6SPhilippe Mathieu-Daudé             if (rc < 0) {
362112c37a6SPhilippe Mathieu-Daudé                 error_setg(errp, "%s: failed to get get absolute X value",
363112c37a6SPhilippe Mathieu-Daudé                            il->evdev);
364112c37a6SPhilippe Mathieu-Daudé                 goto err_close;
365112c37a6SPhilippe Mathieu-Daudé             }
366d755defdSPhilippe Voinov             il->abs_x_min = absinfo.minimum;
367d755defdSPhilippe Voinov             il->abs_x_max = absinfo.maximum;
368d755defdSPhilippe Voinov             rc = ioctl(il->fd, EVIOCGABS(ABS_Y), &absinfo);
369112c37a6SPhilippe Mathieu-Daudé             if (rc < 0) {
370112c37a6SPhilippe Mathieu-Daudé                 error_setg(errp, "%s: failed to get get absolute Y value",
371112c37a6SPhilippe Mathieu-Daudé                            il->evdev);
372112c37a6SPhilippe Mathieu-Daudé                 goto err_close;
373112c37a6SPhilippe Mathieu-Daudé             }
374d755defdSPhilippe Voinov             il->abs_y_min = absinfo.minimum;
375d755defdSPhilippe Voinov             il->abs_y_max = absinfo.maximum;
376ce47d3d4SGerd Hoffmann         }
377ce47d3d4SGerd Hoffmann     }
378ce47d3d4SGerd Hoffmann 
3792e6a64cbSGerd Hoffmann     if (evtmap & (1 << EV_KEY)) {
3802e6a64cbSGerd Hoffmann         memset(keymap, 0, sizeof(keymap));
3812e6a64cbSGerd Hoffmann         rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap);
382112c37a6SPhilippe Mathieu-Daudé         if (rc < 0) {
383112c37a6SPhilippe Mathieu-Daudé             goto err_read_event_bits;
384112c37a6SPhilippe Mathieu-Daudé         }
3852a57c55fSGerd Hoffmann         rc = ioctl(il->fd, EVIOCGKEY(sizeof(keystate)), keystate);
386112c37a6SPhilippe Mathieu-Daudé         if (rc < 0) {
387112c37a6SPhilippe Mathieu-Daudé             error_setg(errp, "%s: failed to get global key state", il->evdev);
388112c37a6SPhilippe Mathieu-Daudé             goto err_close;
389112c37a6SPhilippe Mathieu-Daudé         }
3902e6a64cbSGerd Hoffmann         for (i = 0; i < KEY_CNT; i++) {
3912e6a64cbSGerd Hoffmann             if (keymap[i / 8] & (1 << (i % 8))) {
3922e6a64cbSGerd Hoffmann                 if (linux_is_button(i)) {
3932e6a64cbSGerd Hoffmann                     il->num_btns++;
394e0d2bd51SGerd Hoffmann                 } else {
3952e6a64cbSGerd Hoffmann                     il->num_keys++;
396e0d2bd51SGerd Hoffmann                 }
3972a57c55fSGerd Hoffmann                 if (keystate[i / 8] & (1 << (i % 8))) {
3982a57c55fSGerd Hoffmann                     il->keydown[i] = true;
3992a57c55fSGerd Hoffmann                     il->keycount++;
4002a57c55fSGerd Hoffmann                 }
4012e6a64cbSGerd Hoffmann             }
4022e6a64cbSGerd Hoffmann         }
4032e6a64cbSGerd Hoffmann     }
4042e6a64cbSGerd Hoffmann 
4052e6a64cbSGerd Hoffmann     qemu_set_fd_handler(il->fd, input_linux_event, NULL, il);
4062a57c55fSGerd Hoffmann     if (il->keycount) {
4072a57c55fSGerd Hoffmann         /* delay grab until all keys are released */
4082a57c55fSGerd Hoffmann         il->grab_request = true;
4092a57c55fSGerd Hoffmann     } else {
410e0d2bd51SGerd Hoffmann         input_linux_toggle_grab(il);
4112a57c55fSGerd Hoffmann     }
41246d921beSGerd Hoffmann     QTAILQ_INSERT_TAIL(&inputs, il, next);
4130e066b2cSGerd Hoffmann     il->initialized = true;
4140e066b2cSGerd Hoffmann     return;
415e0d2bd51SGerd Hoffmann 
416112c37a6SPhilippe Mathieu-Daudé err_read_event_bits:
417112c37a6SPhilippe Mathieu-Daudé     error_setg(errp, "%s: failed to read event bits", il->evdev);
418112c37a6SPhilippe Mathieu-Daudé 
419e0d2bd51SGerd Hoffmann err_close:
420e0d2bd51SGerd Hoffmann     close(il->fd);
4210e066b2cSGerd Hoffmann     return;
422e0d2bd51SGerd Hoffmann }
423e0d2bd51SGerd Hoffmann 
4240e066b2cSGerd Hoffmann static void input_linux_instance_finalize(Object *obj)
425e0d2bd51SGerd Hoffmann {
4260e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4270e066b2cSGerd Hoffmann 
4280e066b2cSGerd Hoffmann     if (il->initialized) {
4290e066b2cSGerd Hoffmann         QTAILQ_REMOVE(&inputs, il, next);
4300e066b2cSGerd Hoffmann         close(il->fd);
4310e066b2cSGerd Hoffmann     }
4320e066b2cSGerd Hoffmann     g_free(il->evdev);
4330e066b2cSGerd Hoffmann }
4340e066b2cSGerd Hoffmann 
4350e066b2cSGerd Hoffmann static char *input_linux_get_evdev(Object *obj, Error **errp)
4360e066b2cSGerd Hoffmann {
4370e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4380e066b2cSGerd Hoffmann 
4390e066b2cSGerd Hoffmann     return g_strdup(il->evdev);
4400e066b2cSGerd Hoffmann }
4410e066b2cSGerd Hoffmann 
4420e066b2cSGerd Hoffmann static void input_linux_set_evdev(Object *obj, const char *value,
4430e066b2cSGerd Hoffmann                                   Error **errp)
4440e066b2cSGerd Hoffmann {
4450e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4460e066b2cSGerd Hoffmann 
4470e066b2cSGerd Hoffmann     if (il->evdev) {
4480e066b2cSGerd Hoffmann         error_setg(errp, "evdev property already set");
4490e066b2cSGerd Hoffmann         return;
4500e066b2cSGerd Hoffmann     }
4510e066b2cSGerd Hoffmann     il->evdev = g_strdup(value);
4520e066b2cSGerd Hoffmann }
4530e066b2cSGerd Hoffmann 
4540e066b2cSGerd Hoffmann static bool input_linux_get_grab_all(Object *obj, Error **errp)
4550e066b2cSGerd Hoffmann {
4560e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4570e066b2cSGerd Hoffmann 
4580e066b2cSGerd Hoffmann     return il->grab_all;
4590e066b2cSGerd Hoffmann }
4600e066b2cSGerd Hoffmann 
4610e066b2cSGerd Hoffmann static void input_linux_set_grab_all(Object *obj, bool value,
4620e066b2cSGerd Hoffmann                                    Error **errp)
4630e066b2cSGerd Hoffmann {
4640e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4650e066b2cSGerd Hoffmann 
4660e066b2cSGerd Hoffmann     il->grab_all = value;
4670e066b2cSGerd Hoffmann }
4680e066b2cSGerd Hoffmann 
4690e066b2cSGerd Hoffmann static bool input_linux_get_repeat(Object *obj, Error **errp)
4700e066b2cSGerd Hoffmann {
4710e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4720e066b2cSGerd Hoffmann 
4730e066b2cSGerd Hoffmann     return il->repeat;
4740e066b2cSGerd Hoffmann }
4750e066b2cSGerd Hoffmann 
4760e066b2cSGerd Hoffmann static void input_linux_set_repeat(Object *obj, bool value,
4770e066b2cSGerd Hoffmann                                    Error **errp)
4780e066b2cSGerd Hoffmann {
4790e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4800e066b2cSGerd Hoffmann 
4810e066b2cSGerd Hoffmann     il->repeat = value;
4820e066b2cSGerd Hoffmann }
4830e066b2cSGerd Hoffmann 
4842657846fSRyan El Kochta static int input_linux_get_grab_toggle(Object *obj, Error **errp)
4852657846fSRyan El Kochta {
4862657846fSRyan El Kochta     InputLinux *il = INPUT_LINUX(obj);
4872657846fSRyan El Kochta 
4882657846fSRyan El Kochta     return il->grab_toggle;
4892657846fSRyan El Kochta }
4902657846fSRyan El Kochta 
4912657846fSRyan El Kochta static void input_linux_set_grab_toggle(Object *obj, int value,
4922657846fSRyan El Kochta                                        Error **errp)
4932657846fSRyan El Kochta {
4942657846fSRyan El Kochta     InputLinux *il = INPUT_LINUX(obj);
4952657846fSRyan El Kochta 
4962657846fSRyan El Kochta     il->grab_toggle = value;
4972657846fSRyan El Kochta }
4982657846fSRyan El Kochta 
4990e066b2cSGerd Hoffmann static void input_linux_instance_init(Object *obj)
5000e066b2cSGerd Hoffmann {
5010e066b2cSGerd Hoffmann     object_property_add_str(obj, "evdev",
5020e066b2cSGerd Hoffmann                             input_linux_get_evdev,
503d2623129SMarkus Armbruster                             input_linux_set_evdev);
5040e066b2cSGerd Hoffmann     object_property_add_bool(obj, "grab_all",
5050e066b2cSGerd Hoffmann                              input_linux_get_grab_all,
506d2623129SMarkus Armbruster                              input_linux_set_grab_all);
5070e066b2cSGerd Hoffmann     object_property_add_bool(obj, "repeat",
5080e066b2cSGerd Hoffmann                              input_linux_get_repeat,
509d2623129SMarkus Armbruster                              input_linux_set_repeat);
5102657846fSRyan El Kochta     object_property_add_enum(obj, "grab-toggle", "GrabToggleKeys",
5112657846fSRyan El Kochta                              &GrabToggleKeys_lookup,
5122657846fSRyan El Kochta                              input_linux_get_grab_toggle,
513d2623129SMarkus Armbruster                              input_linux_set_grab_toggle);
5140e066b2cSGerd Hoffmann }
5150e066b2cSGerd Hoffmann 
5160e066b2cSGerd Hoffmann static void input_linux_class_init(ObjectClass *oc, void *data)
5170e066b2cSGerd Hoffmann {
5180e066b2cSGerd Hoffmann     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
5190e066b2cSGerd Hoffmann 
5200e066b2cSGerd Hoffmann     ucc->complete = input_linux_complete;
5210e066b2cSGerd Hoffmann }
5220e066b2cSGerd Hoffmann 
5230e066b2cSGerd Hoffmann static const TypeInfo input_linux_info = {
5240e066b2cSGerd Hoffmann     .name = TYPE_INPUT_LINUX,
5250e066b2cSGerd Hoffmann     .parent = TYPE_OBJECT,
5260e066b2cSGerd Hoffmann     .class_size = sizeof(InputLinuxClass),
5270e066b2cSGerd Hoffmann     .class_init = input_linux_class_init,
5280e066b2cSGerd Hoffmann     .instance_size = sizeof(InputLinux),
5290e066b2cSGerd Hoffmann     .instance_init = input_linux_instance_init,
5300e066b2cSGerd Hoffmann     .instance_finalize = input_linux_instance_finalize,
5310e066b2cSGerd Hoffmann     .interfaces = (InterfaceInfo[]) {
5320e066b2cSGerd Hoffmann         { TYPE_USER_CREATABLE },
5330e066b2cSGerd Hoffmann         { }
5340e066b2cSGerd Hoffmann     }
535e0d2bd51SGerd Hoffmann };
536e0d2bd51SGerd Hoffmann 
5370e066b2cSGerd Hoffmann static void register_types(void)
538e0d2bd51SGerd Hoffmann {
5390e066b2cSGerd Hoffmann     type_register_static(&input_linux_info);
540e0d2bd51SGerd Hoffmann }
5410e066b2cSGerd Hoffmann 
5420e066b2cSGerd Hoffmann type_init(register_types);
543