xref: /openbmc/qemu/ui/input-linux.c (revision a923b471)
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"
20e0d2bd51SGerd Hoffmann 
212e6a64cbSGerd Hoffmann static bool linux_is_button(unsigned int lnx)
222e6a64cbSGerd Hoffmann {
232e6a64cbSGerd Hoffmann     if (lnx < 0x100) {
242e6a64cbSGerd Hoffmann         return false;
252e6a64cbSGerd Hoffmann     }
262e6a64cbSGerd Hoffmann     if (lnx >= 0x160 && lnx < 0x2c0) {
272e6a64cbSGerd Hoffmann         return false;
282e6a64cbSGerd Hoffmann     }
292e6a64cbSGerd Hoffmann     return true;
302e6a64cbSGerd Hoffmann }
312e6a64cbSGerd Hoffmann 
320e066b2cSGerd Hoffmann #define TYPE_INPUT_LINUX "input-linux"
330e066b2cSGerd Hoffmann #define INPUT_LINUX(obj) \
340e066b2cSGerd Hoffmann     OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX)
350e066b2cSGerd Hoffmann #define INPUT_LINUX_GET_CLASS(obj) \
360e066b2cSGerd Hoffmann     OBJECT_GET_CLASS(InputLinuxClass, (obj), TYPE_INPUT_LINUX)
370e066b2cSGerd Hoffmann #define INPUT_LINUX_CLASS(klass) \
380e066b2cSGerd Hoffmann     OBJECT_CLASS_CHECK(InputLinuxClass, (klass), TYPE_INPUT_LINUX)
390e066b2cSGerd Hoffmann 
40e0d2bd51SGerd Hoffmann typedef struct InputLinux InputLinux;
410e066b2cSGerd Hoffmann typedef struct InputLinuxClass InputLinuxClass;
42e0d2bd51SGerd Hoffmann 
43e0d2bd51SGerd Hoffmann struct InputLinux {
440e066b2cSGerd Hoffmann     Object parent;
450e066b2cSGerd Hoffmann 
460e066b2cSGerd Hoffmann     char        *evdev;
47e0d2bd51SGerd Hoffmann     int         fd;
48a6ccabd6SGerd Hoffmann     bool        repeat;
49e0d2bd51SGerd Hoffmann     bool        grab_request;
50e0d2bd51SGerd Hoffmann     bool        grab_active;
5146d921beSGerd Hoffmann     bool        grab_all;
52e0d2bd51SGerd Hoffmann     bool        keydown[KEY_CNT];
53e0d2bd51SGerd Hoffmann     int         keycount;
54e0d2bd51SGerd Hoffmann     int         wheel;
550e066b2cSGerd Hoffmann     bool        initialized;
562e6a64cbSGerd Hoffmann 
572e6a64cbSGerd Hoffmann     bool        has_rel_x;
582e6a64cbSGerd Hoffmann     bool        has_abs_x;
592e6a64cbSGerd Hoffmann     int         num_keys;
602e6a64cbSGerd Hoffmann     int         num_btns;
61d755defdSPhilippe Voinov     int         abs_x_min;
62d755defdSPhilippe Voinov     int         abs_x_max;
63d755defdSPhilippe Voinov     int         abs_y_min;
64d755defdSPhilippe Voinov     int         abs_y_max;
651684907cSJavier Celaya     struct input_event event;
661684907cSJavier Celaya     int         read_offset;
672e6a64cbSGerd Hoffmann 
682657846fSRyan El Kochta     enum GrabToggleKeys grab_toggle;
692657846fSRyan El Kochta 
7046d921beSGerd Hoffmann     QTAILQ_ENTRY(InputLinux) next;
71e0d2bd51SGerd Hoffmann };
72e0d2bd51SGerd Hoffmann 
730e066b2cSGerd Hoffmann struct InputLinuxClass {
740e066b2cSGerd Hoffmann     ObjectClass parent_class;
750e066b2cSGerd Hoffmann };
760e066b2cSGerd Hoffmann 
7746d921beSGerd Hoffmann static QTAILQ_HEAD(, InputLinux) inputs = QTAILQ_HEAD_INITIALIZER(inputs);
7846d921beSGerd Hoffmann 
79e0d2bd51SGerd Hoffmann static void input_linux_toggle_grab(InputLinux *il)
80e0d2bd51SGerd Hoffmann {
81e0d2bd51SGerd Hoffmann     intptr_t request = !il->grab_active;
8246d921beSGerd Hoffmann     InputLinux *item;
83e0d2bd51SGerd Hoffmann     int rc;
84e0d2bd51SGerd Hoffmann 
85e0d2bd51SGerd Hoffmann     rc = ioctl(il->fd, EVIOCGRAB, request);
86e0d2bd51SGerd Hoffmann     if (rc < 0) {
87e0d2bd51SGerd Hoffmann         return;
88e0d2bd51SGerd Hoffmann     }
89e0d2bd51SGerd Hoffmann     il->grab_active = !il->grab_active;
9046d921beSGerd Hoffmann 
9146d921beSGerd Hoffmann     if (!il->grab_all) {
9246d921beSGerd Hoffmann         return;
9346d921beSGerd Hoffmann     }
9446d921beSGerd Hoffmann     QTAILQ_FOREACH(item, &inputs, next) {
9546d921beSGerd Hoffmann         if (item == il || item->grab_all) {
9646d921beSGerd Hoffmann             /* avoid endless loops */
9746d921beSGerd Hoffmann             continue;
9846d921beSGerd Hoffmann         }
9946d921beSGerd Hoffmann         if (item->grab_active != il->grab_active) {
10046d921beSGerd Hoffmann             input_linux_toggle_grab(item);
10146d921beSGerd Hoffmann         }
10246d921beSGerd Hoffmann     }
103e0d2bd51SGerd Hoffmann }
104e0d2bd51SGerd Hoffmann 
1052657846fSRyan El Kochta static bool input_linux_check_toggle(InputLinux *il)
1062657846fSRyan El Kochta {
1072657846fSRyan El Kochta     switch (il->grab_toggle) {
1082657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_CTRL_CTRL:
1092657846fSRyan El Kochta         return il->keydown[KEY_LEFTCTRL] &&
1102657846fSRyan El Kochta             il->keydown[KEY_RIGHTCTRL];
1112657846fSRyan El Kochta 
1122657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_ALT_ALT:
1132657846fSRyan El Kochta         return il->keydown[KEY_LEFTALT] &&
1142657846fSRyan El Kochta             il->keydown[KEY_RIGHTALT];
1152657846fSRyan El Kochta 
116*a923b471SNiklas Haas     case GRAB_TOGGLE_KEYS_SHIFT_SHIFT:
117*a923b471SNiklas Haas         return il->keydown[KEY_LEFTSHIFT] &&
118*a923b471SNiklas Haas             il->keydown[KEY_RIGHTSHIFT];
119*a923b471SNiklas Haas 
1202657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_META_META:
1212657846fSRyan El Kochta         return il->keydown[KEY_LEFTMETA] &&
1222657846fSRyan El Kochta             il->keydown[KEY_RIGHTMETA];
1232657846fSRyan El Kochta 
1242657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_SCROLLLOCK:
1252657846fSRyan El Kochta         return il->keydown[KEY_SCROLLLOCK];
1262657846fSRyan El Kochta 
1272657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK:
1282657846fSRyan El Kochta         return (il->keydown[KEY_LEFTCTRL] ||
1292657846fSRyan El Kochta                 il->keydown[KEY_RIGHTCTRL]) &&
1302657846fSRyan El Kochta             il->keydown[KEY_SCROLLLOCK];
1312657846fSRyan El Kochta 
1322657846fSRyan El Kochta     case GRAB_TOGGLE_KEYS__MAX:
1332657846fSRyan El Kochta         /* avoid gcc error */
1342657846fSRyan El Kochta         break;
1352657846fSRyan El Kochta     }
1362657846fSRyan El Kochta     return false;
1372657846fSRyan El Kochta }
1382657846fSRyan El Kochta 
1392657846fSRyan El Kochta static bool input_linux_should_skip(InputLinux *il,
1402657846fSRyan El Kochta                                     struct input_event *event)
1412657846fSRyan El Kochta {
1422657846fSRyan El Kochta     return (il->grab_toggle == GRAB_TOGGLE_KEYS_SCROLLLOCK ||
1432657846fSRyan El Kochta             il->grab_toggle == GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK) &&
1442657846fSRyan El Kochta             event->code == KEY_SCROLLLOCK;
1452657846fSRyan El Kochta }
1462657846fSRyan El Kochta 
1472330e9e7SGerd Hoffmann static void input_linux_handle_keyboard(InputLinux *il,
1482330e9e7SGerd Hoffmann                                         struct input_event *event)
149e0d2bd51SGerd Hoffmann {
1502330e9e7SGerd Hoffmann     if (event->type == EV_KEY) {
1512330e9e7SGerd Hoffmann         if (event->value > 2 || (event->value > 1 && !il->repeat)) {
152e0d2bd51SGerd Hoffmann             /*
153e0d2bd51SGerd Hoffmann              * ignore autorepeat + unknown key events
154e0d2bd51SGerd Hoffmann              * 0 == up, 1 == down, 2 == autorepeat, other == undefined
155e0d2bd51SGerd Hoffmann              */
1562330e9e7SGerd Hoffmann             return;
157e0d2bd51SGerd Hoffmann         }
1582330e9e7SGerd Hoffmann         if (event->code >= KEY_CNT) {
15981b00c96SGerd Hoffmann             /*
16081b00c96SGerd Hoffmann              * Should not happen.  But better safe than sorry,
16181b00c96SGerd Hoffmann              * and we make Coverity happy too.
16281b00c96SGerd Hoffmann              */
1632330e9e7SGerd Hoffmann             return;
16481b00c96SGerd Hoffmann         }
1652330e9e7SGerd Hoffmann 
166e0d2bd51SGerd Hoffmann         /* keep track of key state */
1672330e9e7SGerd Hoffmann         if (!il->keydown[event->code] && event->value) {
1682330e9e7SGerd Hoffmann             il->keydown[event->code] = true;
169e0d2bd51SGerd Hoffmann             il->keycount++;
170e0d2bd51SGerd Hoffmann         }
1712330e9e7SGerd Hoffmann         if (il->keydown[event->code] && !event->value) {
1722330e9e7SGerd Hoffmann             il->keydown[event->code] = false;
173e0d2bd51SGerd Hoffmann             il->keycount--;
174e0d2bd51SGerd Hoffmann         }
175e0d2bd51SGerd Hoffmann 
176e0d2bd51SGerd Hoffmann         /* send event to guest when grab is active */
1772657846fSRyan El Kochta         if (il->grab_active && !input_linux_should_skip(il, event)) {
1782330e9e7SGerd Hoffmann             int qcode = qemu_input_linux_to_qcode(event->code);
1792330e9e7SGerd Hoffmann             qemu_input_event_send_key_qcode(NULL, qcode, event->value);
180e0d2bd51SGerd Hoffmann         }
181e0d2bd51SGerd Hoffmann 
182e0d2bd51SGerd Hoffmann         /* hotkey -> record switch request ... */
1832657846fSRyan El Kochta         if (input_linux_check_toggle(il)) {
184e0d2bd51SGerd Hoffmann             il->grab_request = true;
185e0d2bd51SGerd Hoffmann         }
186e0d2bd51SGerd Hoffmann 
187e0d2bd51SGerd Hoffmann         /*
188e0d2bd51SGerd Hoffmann          * ... and do the switch when all keys are lifted, so we
189e0d2bd51SGerd Hoffmann          * confuse neither guest nor host with keys which seem to
190e0d2bd51SGerd Hoffmann          * be stuck due to missing key-up events.
191e0d2bd51SGerd Hoffmann          */
192e0d2bd51SGerd Hoffmann         if (il->grab_request && !il->keycount) {
193e0d2bd51SGerd Hoffmann             il->grab_request = false;
194e0d2bd51SGerd Hoffmann             input_linux_toggle_grab(il);
195e0d2bd51SGerd Hoffmann         }
1962330e9e7SGerd Hoffmann     }
1972330e9e7SGerd Hoffmann }
1982330e9e7SGerd Hoffmann 
199e0d2bd51SGerd Hoffmann static void input_linux_event_mouse_button(int button)
200e0d2bd51SGerd Hoffmann {
201e0d2bd51SGerd Hoffmann     qemu_input_queue_btn(NULL, button, true);
202e0d2bd51SGerd Hoffmann     qemu_input_event_sync();
203e0d2bd51SGerd Hoffmann     qemu_input_queue_btn(NULL, button, false);
204e0d2bd51SGerd Hoffmann     qemu_input_event_sync();
205e0d2bd51SGerd Hoffmann }
206e0d2bd51SGerd Hoffmann 
207d4df42c4SGerd Hoffmann static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
208d4df42c4SGerd Hoffmann {
209d4df42c4SGerd Hoffmann     if (!il->grab_active) {
210d4df42c4SGerd Hoffmann         return;
211d4df42c4SGerd Hoffmann     }
212d4df42c4SGerd Hoffmann 
213d4df42c4SGerd Hoffmann     switch (event->type) {
214d4df42c4SGerd Hoffmann     case EV_KEY:
215d4df42c4SGerd Hoffmann         switch (event->code) {
216d4df42c4SGerd Hoffmann         case BTN_LEFT:
217d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value);
218d4df42c4SGerd Hoffmann             break;
219d4df42c4SGerd Hoffmann         case BTN_RIGHT:
220d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value);
221d4df42c4SGerd Hoffmann             break;
222d4df42c4SGerd Hoffmann         case BTN_MIDDLE:
223d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event->value);
224d4df42c4SGerd Hoffmann             break;
225d4df42c4SGerd Hoffmann         case BTN_GEAR_UP:
226d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event->value);
227d4df42c4SGerd Hoffmann             break;
228d4df42c4SGerd Hoffmann         case BTN_GEAR_DOWN:
229d4df42c4SGerd Hoffmann             qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
230d4df42c4SGerd Hoffmann                                  event->value);
231d4df42c4SGerd Hoffmann             break;
2321266b68cSFabian Lesniak         case BTN_SIDE:
2331266b68cSFabian Lesniak             qemu_input_queue_btn(NULL, INPUT_BUTTON_SIDE, event->value);
2341266b68cSFabian Lesniak             break;
2351266b68cSFabian Lesniak         case BTN_EXTRA:
2361266b68cSFabian Lesniak             qemu_input_queue_btn(NULL, INPUT_BUTTON_EXTRA, event->value);
2371266b68cSFabian Lesniak             break;
238d4df42c4SGerd Hoffmann         };
239d4df42c4SGerd Hoffmann         break;
240d4df42c4SGerd Hoffmann     case EV_REL:
241d4df42c4SGerd Hoffmann         switch (event->code) {
242d4df42c4SGerd Hoffmann         case REL_X:
243d4df42c4SGerd Hoffmann             qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value);
244d4df42c4SGerd Hoffmann             break;
245d4df42c4SGerd Hoffmann         case REL_Y:
246d4df42c4SGerd Hoffmann             qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value);
247d4df42c4SGerd Hoffmann             break;
248d4df42c4SGerd Hoffmann         case REL_WHEEL:
249d4df42c4SGerd Hoffmann             il->wheel = event->value;
250d4df42c4SGerd Hoffmann             break;
251d4df42c4SGerd Hoffmann         }
252d4df42c4SGerd Hoffmann         break;
253d755defdSPhilippe Voinov     case EV_ABS:
254d755defdSPhilippe Voinov         switch (event->code) {
255d755defdSPhilippe Voinov         case ABS_X:
256d755defdSPhilippe Voinov             qemu_input_queue_abs(NULL, INPUT_AXIS_X, event->value,
257d755defdSPhilippe Voinov                                  il->abs_x_min, il->abs_x_max);
258d755defdSPhilippe Voinov             break;
259d755defdSPhilippe Voinov         case ABS_Y:
260d755defdSPhilippe Voinov             qemu_input_queue_abs(NULL, INPUT_AXIS_Y, event->value,
261d755defdSPhilippe Voinov                                  il->abs_y_min, il->abs_y_max);
262d755defdSPhilippe Voinov             break;
263d755defdSPhilippe Voinov         }
264d755defdSPhilippe Voinov         break;
265d4df42c4SGerd Hoffmann     case EV_SYN:
266d4df42c4SGerd Hoffmann         qemu_input_event_sync();
267d4df42c4SGerd Hoffmann         if (il->wheel != 0) {
268d4df42c4SGerd Hoffmann             input_linux_event_mouse_button((il->wheel > 0)
269d4df42c4SGerd Hoffmann                                            ? INPUT_BUTTON_WHEEL_UP
270d4df42c4SGerd Hoffmann                                            : INPUT_BUTTON_WHEEL_DOWN);
271d4df42c4SGerd Hoffmann             il->wheel = 0;
272d4df42c4SGerd Hoffmann         }
273d4df42c4SGerd Hoffmann         break;
274d4df42c4SGerd Hoffmann     }
275d4df42c4SGerd Hoffmann }
276d4df42c4SGerd Hoffmann 
2772e6a64cbSGerd Hoffmann static void input_linux_event(void *opaque)
278e0d2bd51SGerd Hoffmann {
279e0d2bd51SGerd Hoffmann     InputLinux *il = opaque;
280e0d2bd51SGerd Hoffmann     int rc;
2811684907cSJavier Celaya     int read_size;
2821684907cSJavier Celaya     uint8_t *p = (uint8_t *)&il->event;
283e0d2bd51SGerd Hoffmann 
284e0d2bd51SGerd Hoffmann     for (;;) {
2851684907cSJavier Celaya         read_size = sizeof(il->event) - il->read_offset;
2861684907cSJavier Celaya         rc = read(il->fd, &p[il->read_offset], read_size);
2871684907cSJavier Celaya         if (rc != read_size) {
288e0d2bd51SGerd Hoffmann             if (rc < 0 && errno != EAGAIN) {
289e0d2bd51SGerd Hoffmann                 fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno));
290e0d2bd51SGerd Hoffmann                 qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
291e0d2bd51SGerd Hoffmann                 close(il->fd);
2921684907cSJavier Celaya             } else if (rc > 0) {
2931684907cSJavier Celaya                 il->read_offset += rc;
294e0d2bd51SGerd Hoffmann             }
295e0d2bd51SGerd Hoffmann             break;
296e0d2bd51SGerd Hoffmann         }
2971684907cSJavier Celaya         il->read_offset = 0;
298e0d2bd51SGerd Hoffmann 
2992e6a64cbSGerd Hoffmann         if (il->num_keys) {
3001684907cSJavier Celaya             input_linux_handle_keyboard(il, &il->event);
3012e6a64cbSGerd Hoffmann         }
302d755defdSPhilippe Voinov         if ((il->has_rel_x || il->has_abs_x) && il->num_btns) {
3031684907cSJavier Celaya             input_linux_handle_mouse(il, &il->event);
304e0d2bd51SGerd Hoffmann         }
305e0d2bd51SGerd Hoffmann     }
3062e6a64cbSGerd Hoffmann }
307e0d2bd51SGerd Hoffmann 
3080e066b2cSGerd Hoffmann static void input_linux_complete(UserCreatable *uc, Error **errp)
309e0d2bd51SGerd Hoffmann {
3100e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(uc);
3112a57c55fSGerd Hoffmann     uint8_t evtmap, relmap, absmap;
3122a57c55fSGerd Hoffmann     uint8_t keymap[KEY_CNT / 8], keystate[KEY_CNT / 8];
3132e6a64cbSGerd Hoffmann     unsigned int i;
314e0d2bd51SGerd Hoffmann     int rc, ver;
315d755defdSPhilippe Voinov     struct input_absinfo absinfo;
316e0d2bd51SGerd Hoffmann 
317e0d2bd51SGerd Hoffmann     if (!il->evdev) {
318e0d2bd51SGerd Hoffmann         error_setg(errp, "no input device specified");
3190e066b2cSGerd Hoffmann         return;
320e0d2bd51SGerd Hoffmann     }
321e0d2bd51SGerd Hoffmann 
322e0d2bd51SGerd Hoffmann     il->fd = open(il->evdev, O_RDWR);
323e0d2bd51SGerd Hoffmann     if (il->fd < 0)  {
324e0d2bd51SGerd Hoffmann         error_setg_file_open(errp, errno, il->evdev);
3250e066b2cSGerd Hoffmann         return;
326e0d2bd51SGerd Hoffmann     }
327e0d2bd51SGerd Hoffmann     qemu_set_nonblock(il->fd);
328e0d2bd51SGerd Hoffmann 
329e0d2bd51SGerd Hoffmann     rc = ioctl(il->fd, EVIOCGVERSION, &ver);
330e0d2bd51SGerd Hoffmann     if (rc < 0) {
331e0d2bd51SGerd Hoffmann         error_setg(errp, "%s: is not an evdev device", il->evdev);
332e0d2bd51SGerd Hoffmann         goto err_close;
333e0d2bd51SGerd Hoffmann     }
334e0d2bd51SGerd Hoffmann 
335e0d2bd51SGerd Hoffmann     rc = ioctl(il->fd, EVIOCGBIT(0, sizeof(evtmap)), &evtmap);
336ce47d3d4SGerd Hoffmann     if (rc < 0) {
337ce47d3d4SGerd Hoffmann         error_setg(errp, "%s: failed to read event bits", il->evdev);
338ce47d3d4SGerd Hoffmann         goto err_close;
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);
3442e6a64cbSGerd Hoffmann         if (relmap & (1 << REL_X)) {
3452e6a64cbSGerd Hoffmann             il->has_rel_x = true;
346ce47d3d4SGerd Hoffmann         }
347ce47d3d4SGerd Hoffmann     }
348ce47d3d4SGerd Hoffmann 
349ce47d3d4SGerd Hoffmann     if (evtmap & (1 << EV_ABS)) {
350ce47d3d4SGerd Hoffmann         absmap = 0;
3512e6a64cbSGerd Hoffmann         rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
3522e6a64cbSGerd Hoffmann         if (absmap & (1 << ABS_X)) {
3532e6a64cbSGerd Hoffmann             il->has_abs_x = true;
354d755defdSPhilippe Voinov             rc = ioctl(il->fd, EVIOCGABS(ABS_X), &absinfo);
355d755defdSPhilippe Voinov             il->abs_x_min = absinfo.minimum;
356d755defdSPhilippe Voinov             il->abs_x_max = absinfo.maximum;
357d755defdSPhilippe Voinov             rc = ioctl(il->fd, EVIOCGABS(ABS_Y), &absinfo);
358d755defdSPhilippe Voinov             il->abs_y_min = absinfo.minimum;
359d755defdSPhilippe Voinov             il->abs_y_max = absinfo.maximum;
360ce47d3d4SGerd Hoffmann         }
361ce47d3d4SGerd Hoffmann     }
362ce47d3d4SGerd Hoffmann 
3632e6a64cbSGerd Hoffmann     if (evtmap & (1 << EV_KEY)) {
3642e6a64cbSGerd Hoffmann         memset(keymap, 0, sizeof(keymap));
3652e6a64cbSGerd Hoffmann         rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap);
3662a57c55fSGerd Hoffmann         rc = ioctl(il->fd, EVIOCGKEY(sizeof(keystate)), keystate);
3672e6a64cbSGerd Hoffmann         for (i = 0; i < KEY_CNT; i++) {
3682e6a64cbSGerd Hoffmann             if (keymap[i / 8] & (1 << (i % 8))) {
3692e6a64cbSGerd Hoffmann                 if (linux_is_button(i)) {
3702e6a64cbSGerd Hoffmann                     il->num_btns++;
371e0d2bd51SGerd Hoffmann                 } else {
3722e6a64cbSGerd Hoffmann                     il->num_keys++;
373e0d2bd51SGerd Hoffmann                 }
3742a57c55fSGerd Hoffmann                 if (keystate[i / 8] & (1 << (i % 8))) {
3752a57c55fSGerd Hoffmann                     il->keydown[i] = true;
3762a57c55fSGerd Hoffmann                     il->keycount++;
3772a57c55fSGerd Hoffmann                 }
3782e6a64cbSGerd Hoffmann             }
3792e6a64cbSGerd Hoffmann         }
3802e6a64cbSGerd Hoffmann     }
3812e6a64cbSGerd Hoffmann 
3822e6a64cbSGerd Hoffmann     qemu_set_fd_handler(il->fd, input_linux_event, NULL, il);
3832a57c55fSGerd Hoffmann     if (il->keycount) {
3842a57c55fSGerd Hoffmann         /* delay grab until all keys are released */
3852a57c55fSGerd Hoffmann         il->grab_request = true;
3862a57c55fSGerd Hoffmann     } else {
387e0d2bd51SGerd Hoffmann         input_linux_toggle_grab(il);
3882a57c55fSGerd Hoffmann     }
38946d921beSGerd Hoffmann     QTAILQ_INSERT_TAIL(&inputs, il, next);
3900e066b2cSGerd Hoffmann     il->initialized = true;
3910e066b2cSGerd Hoffmann     return;
392e0d2bd51SGerd Hoffmann 
393e0d2bd51SGerd Hoffmann err_close:
394e0d2bd51SGerd Hoffmann     close(il->fd);
3950e066b2cSGerd Hoffmann     return;
396e0d2bd51SGerd Hoffmann }
397e0d2bd51SGerd Hoffmann 
3980e066b2cSGerd Hoffmann static void input_linux_instance_finalize(Object *obj)
399e0d2bd51SGerd Hoffmann {
4000e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4010e066b2cSGerd Hoffmann 
4020e066b2cSGerd Hoffmann     if (il->initialized) {
4030e066b2cSGerd Hoffmann         QTAILQ_REMOVE(&inputs, il, next);
4040e066b2cSGerd Hoffmann         close(il->fd);
4050e066b2cSGerd Hoffmann     }
4060e066b2cSGerd Hoffmann     g_free(il->evdev);
4070e066b2cSGerd Hoffmann }
4080e066b2cSGerd Hoffmann 
4090e066b2cSGerd Hoffmann static char *input_linux_get_evdev(Object *obj, Error **errp)
4100e066b2cSGerd Hoffmann {
4110e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4120e066b2cSGerd Hoffmann 
4130e066b2cSGerd Hoffmann     return g_strdup(il->evdev);
4140e066b2cSGerd Hoffmann }
4150e066b2cSGerd Hoffmann 
4160e066b2cSGerd Hoffmann static void input_linux_set_evdev(Object *obj, const char *value,
4170e066b2cSGerd Hoffmann                                   Error **errp)
4180e066b2cSGerd Hoffmann {
4190e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4200e066b2cSGerd Hoffmann 
4210e066b2cSGerd Hoffmann     if (il->evdev) {
4220e066b2cSGerd Hoffmann         error_setg(errp, "evdev property already set");
4230e066b2cSGerd Hoffmann         return;
4240e066b2cSGerd Hoffmann     }
4250e066b2cSGerd Hoffmann     il->evdev = g_strdup(value);
4260e066b2cSGerd Hoffmann }
4270e066b2cSGerd Hoffmann 
4280e066b2cSGerd Hoffmann static bool input_linux_get_grab_all(Object *obj, Error **errp)
4290e066b2cSGerd Hoffmann {
4300e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4310e066b2cSGerd Hoffmann 
4320e066b2cSGerd Hoffmann     return il->grab_all;
4330e066b2cSGerd Hoffmann }
4340e066b2cSGerd Hoffmann 
4350e066b2cSGerd Hoffmann static void input_linux_set_grab_all(Object *obj, bool value,
4360e066b2cSGerd Hoffmann                                    Error **errp)
4370e066b2cSGerd Hoffmann {
4380e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4390e066b2cSGerd Hoffmann 
4400e066b2cSGerd Hoffmann     il->grab_all = value;
4410e066b2cSGerd Hoffmann }
4420e066b2cSGerd Hoffmann 
4430e066b2cSGerd Hoffmann static bool input_linux_get_repeat(Object *obj, Error **errp)
4440e066b2cSGerd Hoffmann {
4450e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4460e066b2cSGerd Hoffmann 
4470e066b2cSGerd Hoffmann     return il->repeat;
4480e066b2cSGerd Hoffmann }
4490e066b2cSGerd Hoffmann 
4500e066b2cSGerd Hoffmann static void input_linux_set_repeat(Object *obj, bool value,
4510e066b2cSGerd Hoffmann                                    Error **errp)
4520e066b2cSGerd Hoffmann {
4530e066b2cSGerd Hoffmann     InputLinux *il = INPUT_LINUX(obj);
4540e066b2cSGerd Hoffmann 
4550e066b2cSGerd Hoffmann     il->repeat = value;
4560e066b2cSGerd Hoffmann }
4570e066b2cSGerd Hoffmann 
4582657846fSRyan El Kochta static int input_linux_get_grab_toggle(Object *obj, Error **errp)
4592657846fSRyan El Kochta {
4602657846fSRyan El Kochta     InputLinux *il = INPUT_LINUX(obj);
4612657846fSRyan El Kochta 
4622657846fSRyan El Kochta     return il->grab_toggle;
4632657846fSRyan El Kochta }
4642657846fSRyan El Kochta 
4652657846fSRyan El Kochta static void input_linux_set_grab_toggle(Object *obj, int value,
4662657846fSRyan El Kochta                                        Error **errp)
4672657846fSRyan El Kochta {
4682657846fSRyan El Kochta     InputLinux *il = INPUT_LINUX(obj);
4692657846fSRyan El Kochta 
4702657846fSRyan El Kochta     il->grab_toggle = value;
4712657846fSRyan El Kochta }
4722657846fSRyan El Kochta 
4730e066b2cSGerd Hoffmann static void input_linux_instance_init(Object *obj)
4740e066b2cSGerd Hoffmann {
4750e066b2cSGerd Hoffmann     object_property_add_str(obj, "evdev",
4760e066b2cSGerd Hoffmann                             input_linux_get_evdev,
4770e066b2cSGerd Hoffmann                             input_linux_set_evdev, NULL);
4780e066b2cSGerd Hoffmann     object_property_add_bool(obj, "grab_all",
4790e066b2cSGerd Hoffmann                              input_linux_get_grab_all,
4800e066b2cSGerd Hoffmann                              input_linux_set_grab_all, NULL);
4810e066b2cSGerd Hoffmann     object_property_add_bool(obj, "repeat",
4820e066b2cSGerd Hoffmann                              input_linux_get_repeat,
4830e066b2cSGerd Hoffmann                              input_linux_set_repeat, NULL);
4842657846fSRyan El Kochta     object_property_add_enum(obj, "grab-toggle", "GrabToggleKeys",
4852657846fSRyan El Kochta                              &GrabToggleKeys_lookup,
4862657846fSRyan El Kochta                              input_linux_get_grab_toggle,
4872657846fSRyan El Kochta                              input_linux_set_grab_toggle, NULL);
4880e066b2cSGerd Hoffmann }
4890e066b2cSGerd Hoffmann 
4900e066b2cSGerd Hoffmann static void input_linux_class_init(ObjectClass *oc, void *data)
4910e066b2cSGerd Hoffmann {
4920e066b2cSGerd Hoffmann     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
4930e066b2cSGerd Hoffmann 
4940e066b2cSGerd Hoffmann     ucc->complete = input_linux_complete;
4950e066b2cSGerd Hoffmann }
4960e066b2cSGerd Hoffmann 
4970e066b2cSGerd Hoffmann static const TypeInfo input_linux_info = {
4980e066b2cSGerd Hoffmann     .name = TYPE_INPUT_LINUX,
4990e066b2cSGerd Hoffmann     .parent = TYPE_OBJECT,
5000e066b2cSGerd Hoffmann     .class_size = sizeof(InputLinuxClass),
5010e066b2cSGerd Hoffmann     .class_init = input_linux_class_init,
5020e066b2cSGerd Hoffmann     .instance_size = sizeof(InputLinux),
5030e066b2cSGerd Hoffmann     .instance_init = input_linux_instance_init,
5040e066b2cSGerd Hoffmann     .instance_finalize = input_linux_instance_finalize,
5050e066b2cSGerd Hoffmann     .interfaces = (InterfaceInfo[]) {
5060e066b2cSGerd Hoffmann         { TYPE_USER_CREATABLE },
5070e066b2cSGerd Hoffmann         { }
5080e066b2cSGerd Hoffmann     }
509e0d2bd51SGerd Hoffmann };
510e0d2bd51SGerd Hoffmann 
5110e066b2cSGerd Hoffmann static void register_types(void)
512e0d2bd51SGerd Hoffmann {
5130e066b2cSGerd Hoffmann     type_register_static(&input_linux_info);
514e0d2bd51SGerd Hoffmann }
5150e066b2cSGerd Hoffmann 
5160e066b2cSGerd Hoffmann type_init(register_types);
517