qemu/ui/input-linux.c
<<
>>
Prefs
   1/*
   2 * This work is licensed under the terms of the GNU GPL, version 2 or
   3 * (at your option) any later version.  See the COPYING file in the
   4 * top-level directory.
   5 */
   6
   7#include "qemu/osdep.h"
   8#include "qapi/error.h"
   9#include "qemu/config-file.h"
  10#include "qemu/module.h"
  11#include "qemu/sockets.h"
  12#include "sysemu/sysemu.h"
  13#include "ui/input.h"
  14#include "qom/object_interfaces.h"
  15#include "sysemu/iothread.h"
  16#include "block/aio.h"
  17
  18#include <sys/ioctl.h>
  19#include "standard-headers/linux/input.h"
  20
  21static bool linux_is_button(unsigned int lnx)
  22{
  23    if (lnx < 0x100) {
  24        return false;
  25    }
  26    if (lnx >= 0x160 && lnx < 0x2c0) {
  27        return false;
  28    }
  29    return true;
  30}
  31
  32#define TYPE_INPUT_LINUX "input-linux"
  33#define INPUT_LINUX(obj) \
  34    OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX)
  35#define INPUT_LINUX_GET_CLASS(obj) \
  36    OBJECT_GET_CLASS(InputLinuxClass, (obj), TYPE_INPUT_LINUX)
  37#define INPUT_LINUX_CLASS(klass) \
  38    OBJECT_CLASS_CHECK(InputLinuxClass, (klass), TYPE_INPUT_LINUX)
  39
  40typedef struct InputLinux InputLinux;
  41typedef struct InputLinuxClass InputLinuxClass;
  42
  43struct InputLinux {
  44    Object parent;
  45
  46    char        *evdev;
  47    int         fd;
  48    bool        repeat;
  49    bool        grab_request;
  50    bool        grab_active;
  51    bool        grab_all;
  52    bool        keydown[KEY_CNT];
  53    int         keycount;
  54    int         wheel;
  55    bool        initialized;
  56
  57    bool        has_rel_x;
  58    bool        has_abs_x;
  59    int         num_keys;
  60    int         num_btns;
  61    int         abs_x_min;
  62    int         abs_x_max;
  63    int         abs_y_min;
  64    int         abs_y_max;
  65    struct input_event event;
  66    int         read_offset;
  67
  68    enum GrabToggleKeys grab_toggle;
  69
  70    QTAILQ_ENTRY(InputLinux) next;
  71};
  72
  73struct InputLinuxClass {
  74    ObjectClass parent_class;
  75};
  76
  77static QTAILQ_HEAD(, InputLinux) inputs = QTAILQ_HEAD_INITIALIZER(inputs);
  78
  79static void input_linux_toggle_grab(InputLinux *il)
  80{
  81    intptr_t request = !il->grab_active;
  82    InputLinux *item;
  83    int rc;
  84
  85    rc = ioctl(il->fd, EVIOCGRAB, request);
  86    if (rc < 0) {
  87        return;
  88    }
  89    il->grab_active = !il->grab_active;
  90
  91    if (!il->grab_all) {
  92        return;
  93    }
  94    QTAILQ_FOREACH(item, &inputs, next) {
  95        if (item == il || item->grab_all) {
  96            /* avoid endless loops */
  97            continue;
  98        }
  99        if (item->grab_active != il->grab_active) {
 100            input_linux_toggle_grab(item);
 101        }
 102    }
 103}
 104
 105static bool input_linux_check_toggle(InputLinux *il)
 106{
 107    switch (il->grab_toggle) {
 108    case GRAB_TOGGLE_KEYS_CTRL_CTRL:
 109        return il->keydown[KEY_LEFTCTRL] &&
 110            il->keydown[KEY_RIGHTCTRL];
 111
 112    case GRAB_TOGGLE_KEYS_ALT_ALT:
 113        return il->keydown[KEY_LEFTALT] &&
 114            il->keydown[KEY_RIGHTALT];
 115
 116    case GRAB_TOGGLE_KEYS_META_META:
 117        return il->keydown[KEY_LEFTMETA] &&
 118            il->keydown[KEY_RIGHTMETA];
 119
 120    case GRAB_TOGGLE_KEYS_SCROLLLOCK:
 121        return il->keydown[KEY_SCROLLLOCK];
 122
 123    case GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK:
 124        return (il->keydown[KEY_LEFTCTRL] ||
 125                il->keydown[KEY_RIGHTCTRL]) &&
 126            il->keydown[KEY_SCROLLLOCK];
 127
 128    case GRAB_TOGGLE_KEYS__MAX:
 129        /* avoid gcc error */
 130        break;
 131    }
 132    return false;
 133}
 134
 135static bool input_linux_should_skip(InputLinux *il,
 136                                    struct input_event *event)
 137{
 138    return (il->grab_toggle == GRAB_TOGGLE_KEYS_SCROLLLOCK ||
 139            il->grab_toggle == GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK) &&
 140            event->code == KEY_SCROLLLOCK;
 141}
 142
 143static void input_linux_handle_keyboard(InputLinux *il,
 144                                        struct input_event *event)
 145{
 146    if (event->type == EV_KEY) {
 147        if (event->value > 2 || (event->value > 1 && !il->repeat)) {
 148            /*
 149             * ignore autorepeat + unknown key events
 150             * 0 == up, 1 == down, 2 == autorepeat, other == undefined
 151             */
 152            return;
 153        }
 154        if (event->code >= KEY_CNT) {
 155            /*
 156             * Should not happen.  But better safe than sorry,
 157             * and we make Coverity happy too.
 158             */
 159            return;
 160        }
 161
 162        /* keep track of key state */
 163        if (!il->keydown[event->code] && event->value) {
 164            il->keydown[event->code] = true;
 165            il->keycount++;
 166        }
 167        if (il->keydown[event->code] && !event->value) {
 168            il->keydown[event->code] = false;
 169            il->keycount--;
 170        }
 171
 172        /* send event to guest when grab is active */
 173        if (il->grab_active && !input_linux_should_skip(il, event)) {
 174            int qcode = qemu_input_linux_to_qcode(event->code);
 175            qemu_input_event_send_key_qcode(NULL, qcode, event->value);
 176        }
 177
 178        /* hotkey -> record switch request ... */
 179        if (input_linux_check_toggle(il)) {
 180            il->grab_request = true;
 181        }
 182
 183        /*
 184         * ... and do the switch when all keys are lifted, so we
 185         * confuse neither guest nor host with keys which seem to
 186         * be stuck due to missing key-up events.
 187         */
 188        if (il->grab_request && !il->keycount) {
 189            il->grab_request = false;
 190            input_linux_toggle_grab(il);
 191        }
 192    }
 193}
 194
 195static void input_linux_event_mouse_button(int button)
 196{
 197    qemu_input_queue_btn(NULL, button, true);
 198    qemu_input_event_sync();
 199    qemu_input_queue_btn(NULL, button, false);
 200    qemu_input_event_sync();
 201}
 202
 203static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
 204{
 205    if (!il->grab_active) {
 206        return;
 207    }
 208
 209    switch (event->type) {
 210    case EV_KEY:
 211        switch (event->code) {
 212        case BTN_LEFT:
 213            qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value);
 214            break;
 215        case BTN_RIGHT:
 216            qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value);
 217            break;
 218        case BTN_MIDDLE:
 219            qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event->value);
 220            break;
 221        case BTN_GEAR_UP:
 222            qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event->value);
 223            break;
 224        case BTN_GEAR_DOWN:
 225            qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
 226                                 event->value);
 227            break;
 228        case BTN_SIDE:
 229            qemu_input_queue_btn(NULL, INPUT_BUTTON_SIDE, event->value);
 230            break;
 231        case BTN_EXTRA:
 232            qemu_input_queue_btn(NULL, INPUT_BUTTON_EXTRA, event->value);
 233            break;
 234        };
 235        break;
 236    case EV_REL:
 237        switch (event->code) {
 238        case REL_X:
 239            qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value);
 240            break;
 241        case REL_Y:
 242            qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value);
 243            break;
 244        case REL_WHEEL:
 245            il->wheel = event->value;
 246            break;
 247        }
 248        break;
 249    case EV_ABS:
 250        switch (event->code) {
 251        case ABS_X:
 252            qemu_input_queue_abs(NULL, INPUT_AXIS_X, event->value,
 253                                 il->abs_x_min, il->abs_x_max);
 254            break;
 255        case ABS_Y:
 256            qemu_input_queue_abs(NULL, INPUT_AXIS_Y, event->value,
 257                                 il->abs_y_min, il->abs_y_max);
 258            break;
 259        }
 260        break;
 261    case EV_SYN:
 262        qemu_input_event_sync();
 263        if (il->wheel != 0) {
 264            input_linux_event_mouse_button((il->wheel > 0)
 265                                           ? INPUT_BUTTON_WHEEL_UP
 266                                           : INPUT_BUTTON_WHEEL_DOWN);
 267            il->wheel = 0;
 268        }
 269        break;
 270    }
 271}
 272
 273static void input_linux_event(void *opaque)
 274{
 275    InputLinux *il = opaque;
 276    int rc;
 277    int read_size;
 278    uint8_t *p = (uint8_t *)&il->event;
 279
 280    for (;;) {
 281        read_size = sizeof(il->event) - il->read_offset;
 282        rc = read(il->fd, &p[il->read_offset], read_size);
 283        if (rc != read_size) {
 284            if (rc < 0 && errno != EAGAIN) {
 285                fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno));
 286                qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
 287                close(il->fd);
 288            } else if (rc > 0) {
 289                il->read_offset += rc;
 290            }
 291            break;
 292        }
 293        il->read_offset = 0;
 294
 295        if (il->num_keys) {
 296            input_linux_handle_keyboard(il, &il->event);
 297        }
 298        if ((il->has_rel_x || il->has_abs_x) && il->num_btns) {
 299            input_linux_handle_mouse(il, &il->event);
 300        }
 301    }
 302}
 303
 304static void input_linux_complete(UserCreatable *uc, Error **errp)
 305{
 306    InputLinux *il = INPUT_LINUX(uc);
 307    uint8_t evtmap, relmap, absmap;
 308    uint8_t keymap[KEY_CNT / 8], keystate[KEY_CNT / 8];
 309    unsigned int i;
 310    int rc, ver;
 311    struct input_absinfo absinfo;
 312
 313    if (!il->evdev) {
 314        error_setg(errp, "no input device specified");
 315        return;
 316    }
 317
 318    il->fd = open(il->evdev, O_RDWR);
 319    if (il->fd < 0)  {
 320        error_setg_file_open(errp, errno, il->evdev);
 321        return;
 322    }
 323    qemu_set_nonblock(il->fd);
 324
 325    rc = ioctl(il->fd, EVIOCGVERSION, &ver);
 326    if (rc < 0) {
 327        error_setg(errp, "%s: is not an evdev device", il->evdev);
 328        goto err_close;
 329    }
 330
 331    rc = ioctl(il->fd, EVIOCGBIT(0, sizeof(evtmap)), &evtmap);
 332    if (rc < 0) {
 333        error_setg(errp, "%s: failed to read event bits", il->evdev);
 334        goto err_close;
 335    }
 336
 337    if (evtmap & (1 << EV_REL)) {
 338        relmap = 0;
 339        rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
 340        if (relmap & (1 << REL_X)) {
 341            il->has_rel_x = true;
 342        }
 343    }
 344
 345    if (evtmap & (1 << EV_ABS)) {
 346        absmap = 0;
 347        rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
 348        if (absmap & (1 << ABS_X)) {
 349            il->has_abs_x = true;
 350            rc = ioctl(il->fd, EVIOCGABS(ABS_X), &absinfo);
 351            il->abs_x_min = absinfo.minimum;
 352            il->abs_x_max = absinfo.maximum;
 353            rc = ioctl(il->fd, EVIOCGABS(ABS_Y), &absinfo);
 354            il->abs_y_min = absinfo.minimum;
 355            il->abs_y_max = absinfo.maximum;
 356        }
 357    }
 358
 359    if (evtmap & (1 << EV_KEY)) {
 360        memset(keymap, 0, sizeof(keymap));
 361        rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap);
 362        rc = ioctl(il->fd, EVIOCGKEY(sizeof(keystate)), keystate);
 363        for (i = 0; i < KEY_CNT; i++) {
 364            if (keymap[i / 8] & (1 << (i % 8))) {
 365                if (linux_is_button(i)) {
 366                    il->num_btns++;
 367                } else {
 368                    il->num_keys++;
 369                }
 370                if (keystate[i / 8] & (1 << (i % 8))) {
 371                    il->keydown[i] = true;
 372                    il->keycount++;
 373                }
 374            }
 375        }
 376    }
 377
 378    qemu_set_fd_handler(il->fd, input_linux_event, NULL, il);
 379    if (il->keycount) {
 380        /* delay grab until all keys are released */
 381        il->grab_request = true;
 382    } else {
 383        input_linux_toggle_grab(il);
 384    }
 385    QTAILQ_INSERT_TAIL(&inputs, il, next);
 386    il->initialized = true;
 387    return;
 388
 389err_close:
 390    close(il->fd);
 391    return;
 392}
 393
 394static void input_linux_instance_finalize(Object *obj)
 395{
 396    InputLinux *il = INPUT_LINUX(obj);
 397
 398    if (il->initialized) {
 399        QTAILQ_REMOVE(&inputs, il, next);
 400        close(il->fd);
 401    }
 402    g_free(il->evdev);
 403}
 404
 405static char *input_linux_get_evdev(Object *obj, Error **errp)
 406{
 407    InputLinux *il = INPUT_LINUX(obj);
 408
 409    return g_strdup(il->evdev);
 410}
 411
 412static void input_linux_set_evdev(Object *obj, const char *value,
 413                                  Error **errp)
 414{
 415    InputLinux *il = INPUT_LINUX(obj);
 416
 417    if (il->evdev) {
 418        error_setg(errp, "evdev property already set");
 419        return;
 420    }
 421    il->evdev = g_strdup(value);
 422}
 423
 424static bool input_linux_get_grab_all(Object *obj, Error **errp)
 425{
 426    InputLinux *il = INPUT_LINUX(obj);
 427
 428    return il->grab_all;
 429}
 430
 431static void input_linux_set_grab_all(Object *obj, bool value,
 432                                   Error **errp)
 433{
 434    InputLinux *il = INPUT_LINUX(obj);
 435
 436    il->grab_all = value;
 437}
 438
 439static bool input_linux_get_repeat(Object *obj, Error **errp)
 440{
 441    InputLinux *il = INPUT_LINUX(obj);
 442
 443    return il->repeat;
 444}
 445
 446static void input_linux_set_repeat(Object *obj, bool value,
 447                                   Error **errp)
 448{
 449    InputLinux *il = INPUT_LINUX(obj);
 450
 451    il->repeat = value;
 452}
 453
 454static int input_linux_get_grab_toggle(Object *obj, Error **errp)
 455{
 456    InputLinux *il = INPUT_LINUX(obj);
 457
 458    return il->grab_toggle;
 459}
 460
 461static void input_linux_set_grab_toggle(Object *obj, int value,
 462                                       Error **errp)
 463{
 464    InputLinux *il = INPUT_LINUX(obj);
 465
 466    il->grab_toggle = value;
 467}
 468
 469static void input_linux_instance_init(Object *obj)
 470{
 471    object_property_add_str(obj, "evdev",
 472                            input_linux_get_evdev,
 473                            input_linux_set_evdev, NULL);
 474    object_property_add_bool(obj, "grab_all",
 475                             input_linux_get_grab_all,
 476                             input_linux_set_grab_all, NULL);
 477    object_property_add_bool(obj, "repeat",
 478                             input_linux_get_repeat,
 479                             input_linux_set_repeat, NULL);
 480    object_property_add_enum(obj, "grab-toggle", "GrabToggleKeys",
 481                             &GrabToggleKeys_lookup,
 482                             input_linux_get_grab_toggle,
 483                             input_linux_set_grab_toggle, NULL);
 484}
 485
 486static void input_linux_class_init(ObjectClass *oc, void *data)
 487{
 488    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
 489
 490    ucc->complete = input_linux_complete;
 491}
 492
 493static const TypeInfo input_linux_info = {
 494    .name = TYPE_INPUT_LINUX,
 495    .parent = TYPE_OBJECT,
 496    .class_size = sizeof(InputLinuxClass),
 497    .class_init = input_linux_class_init,
 498    .instance_size = sizeof(InputLinux),
 499    .instance_init = input_linux_instance_init,
 500    .instance_finalize = input_linux_instance_finalize,
 501    .interfaces = (InterfaceInfo[]) {
 502        { TYPE_USER_CREATABLE },
 503        { }
 504    }
 505};
 506
 507static void register_types(void)
 508{
 509    type_register_static(&input_linux_info);
 510}
 511
 512type_init(register_types);
 513