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