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