[vfio-users] [PATCH v2 2/3] input: linux evdev support

thibaut noah thibaut.noah at gmail.com
Mon Dec 14 16:44:01 UTC 2015


So basically this remove the need for us to use synergy, how do we apply
the patch to an existing config using libvirt?

2015-12-14 15:18 GMT+01:00 Gerd Hoffmann <kraxel at redhat.com>:

> This patch adds support for reading input events directly from linux
> evdev devices and forward them to the guest.  Unlike virtio-input-host
> which simply passes on all events to the guest without looking at them
> this will interpret the events and feed them into the qemu input
> subsystem.
>
> Therefore this is limited to what the qemu input subsystem and the
> emulated input devices are able to handle.  Also there is no support for
> absolute coordinates (tablet/touchscreen).  So we are talking here about
> basic mouse and keyboard support.
>
> The advantage is that it'll work without virtio-input drivers in the
> guest, the events are delivered to the usual ps/2 or usb input devices
> (depending on what the machine happens to have).  And for keyboards
> qemu is able to switch the keyboard between guest and host on hotkey.
> The hotkey is hard-coded for now (both control keys), initialy the
> guest owns the keyboard.
>
> Probably most useful when assigning vga devices with vfio and using a
> physical monitor instead of vnc/spice/gtk as guest display.
>
> Usage:  Add '-input-linux /dev/input/event<nr>' to the qemu command
> line.  Note that udev has rules which populate /dev/input/by-{id,path}
> with static names, which might be more convinient to use.
>
> Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
> ---
>  include/ui/input.h |   2 +
>  qemu-options.hx    |   9 +++
>  ui/Makefile.objs   |   1 +
>  ui/input-linux.c   | 233
> +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  vl.c               |  11 +++
>  5 files changed, 256 insertions(+)
>  create mode 100644 ui/input-linux.c
>
> diff --git a/include/ui/input.h b/include/ui/input.h
> index d7afd80..443742e 100644
> --- a/include/ui/input.h
> +++ b/include/ui/input.h
> @@ -68,4 +68,6 @@ void qemu_input_check_mode_change(void);
>  void qemu_add_mouse_mode_change_notifier(Notifier *notify);
>  void qemu_remove_mouse_mode_change_notifier(Notifier *notify);
>
> +int input_linux_init(void *opaque, QemuOpts *opts, Error **errp);
> +
>  #endif /* INPUT_H */
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 0eea4ee..a2d7338 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1165,6 +1165,15 @@ STEXI
>  Set the initial graphical resolution and depth (PPC, SPARC only).
>  ETEXI
>
> +DEF("input-linux", 1, QEMU_OPTION_input_linux,
> +    "-input-linux <evdev>\n"
> +    "                Use input device.\n", QEMU_ARCH_ALL)
> +STEXI
> + at item -input-linux @var{dev}
> + at findex -input-linux
> +Use input device.
> +ETEXI
> +
>  DEF("vnc", HAS_ARG, QEMU_OPTION_vnc ,
>      "-vnc display    start a VNC server on display\n", QEMU_ARCH_ALL)
>  STEXI
> diff --git a/ui/Makefile.objs b/ui/Makefile.objs
> index 728393c..dc936f1 100644
> --- a/ui/Makefile.objs
> +++ b/ui/Makefile.objs
> @@ -9,6 +9,7 @@ vnc-obj-y += vnc-jobs.o
>
>  common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
>  common-obj-y += input.o input-keymap.o input-legacy.o
> +common-obj-$(CONFIG_LINUX) += input-linux.o
>  common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
>  common-obj-$(CONFIG_SDL) += sdl.mo x_keymap.o
>  common-obj-$(CONFIG_COCOA) += cocoa.o
> diff --git a/ui/input-linux.c b/ui/input-linux.c
> new file mode 100644
> index 0000000..fdedf0b
> --- /dev/null
> +++ b/ui/input-linux.c
> @@ -0,0 +1,233 @@
> +/*
> + * This work is licensed under the terms of the GNU GPL, version 2 or
> + * (at your option) any later version.  See the COPYING file in the
> + * top-level directory.
> + */
> +
> +#include "qemu-common.h"
> +#include "qemu/config-file.h"
> +#include "qemu/sockets.h"
> +#include "sysemu/sysemu.h"
> +#include "ui/input.h"
> +
> +#include <sys/ioctl.h>
> +#include "standard-headers/linux/input.h"
> +
> +typedef struct InputLinux InputLinux;
> +
> +struct InputLinux {
> +    const char  *evdev;
> +    int         fd;
> +    bool        grab_request;
> +    bool        grab_active;
> +    bool        keydown[KEY_CNT];
> +    int         keycount;
> +    int         wheel;
> +};
> +
> +static void input_linux_toggle_grab(InputLinux *il)
> +{
> +    intptr_t request = !il->grab_active;
> +    int rc;
> +
> +    rc = ioctl(il->fd, EVIOCGRAB, request);
> +    if (rc < 0) {
> +        return;
> +    }
> +    il->grab_active = !il->grab_active;
> +}
> +
> +static void input_linux_event_keyboard(void *opaque)
> +{
> +    InputLinux *il = opaque;
> +    struct input_event event;
> +    int rc;
> +
> +    for (;;) {
> +        rc = read(il->fd, &event, sizeof(event));
> +        if (rc != sizeof(event)) {
> +            break;
> +        }
> +
> +        switch (event.type) {
> +        case EV_KEY:
> +            if (event.value > 1) {
> +                /*
> +                 * ignore autorepeat + unknown key events
> +                 * 0 == up, 1 == down, 2 == autorepeat, other == undefined
> +                 */
> +                continue;
> +            }
> +            /* keep track of key state */
> +            if (!il->keydown[event.code] && event.value) {
> +                il->keydown[event.code] = true;
> +                il->keycount++;
> +            }
> +            if (il->keydown[event.code] && !event.value) {
> +                il->keydown[event.code] = false;
> +                il->keycount--;
> +            }
> +
> +            /* send event to guest when grab is active */
> +            if (il->grab_active) {
> +                int qcode = qemu_input_linux_to_qcode(event.code);
> +                qemu_input_event_send_key_qcode(NULL, qcode, event.value);
> +            }
> +
> +            /* hotkey -> record switch request ... */
> +            if (il->keydown[KEY_LEFTCTRL] &&
> +                il->keydown[KEY_RIGHTCTRL]) {
> +                il->grab_request = true;
> +            }
> +
> +            /*
> +             * ... and do the switch when all keys are lifted, so we
> +             * confuse neither guest nor host with keys which seem to
> +             * be stuck due to missing key-up events.
> +             */
> +            if (il->grab_request && !il->keycount) {
> +                il->grab_request = false;
> +                input_linux_toggle_grab(il);
> +            }
> +            break;
> +        }
> +    }
> +}
> +
> +static void input_linux_event_mouse_button(int button)
> +{
> +    qemu_input_queue_btn(NULL, button, true);
> +    qemu_input_event_sync();
> +    qemu_input_queue_btn(NULL, button, false);
> +    qemu_input_event_sync();
> +}
> +
> +static void input_linux_event_mouse(void *opaque)
> +{
> +    InputLinux *il = opaque;
> +    struct input_event event;
> +    int rc;
> +
> +    for (;;) {
> +        rc = read(il->fd, &event, sizeof(event));
> +        if (rc != sizeof(event)) {
> +            break;
> +        }
> +
> +        switch (event.type) {
> +        case EV_KEY:
> +            switch (event.code) {
> +            case BTN_LEFT:
> +                qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT,
> event.value);
> +                break;
> +            case BTN_RIGHT:
> +                qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT,
> event.value);
> +                break;
> +            case BTN_MIDDLE:
> +                qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE,
> event.value);
> +                break;
> +            case BTN_GEAR_UP:
> +                qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP,
> event.value);
> +                break;
> +            case BTN_GEAR_DOWN:
> +                qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
> +                                     event.value);
> +                break;
> +            };
> +            break;
> +        case EV_REL:
> +            switch (event.code) {
> +            case REL_X:
> +                qemu_input_queue_rel(NULL, INPUT_AXIS_X, event.value);
> +                break;
> +            case REL_Y:
> +                qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event.value);
> +                break;
> +            case REL_WHEEL:
> +                il->wheel = event.value;
> +                break;
> +            }
> +            break;
> +        case EV_SYN:
> +            qemu_input_event_sync();
> +            if (il->wheel != 0) {
> +                input_linux_event_mouse_button((il->wheel > 0)
> +                                               ? INPUT_BUTTON_WHEEL_UP
> +                                               : INPUT_BUTTON_WHEEL_DOWN);
> +                il->wheel = 0;
> +            }
> +            break;
> +        }
> +    }
> +}
> +
> +int input_linux_init(void *opaque, QemuOpts *opts, Error **errp)
> +{
> +    InputLinux *il = g_new0(InputLinux, 1);
> +    uint32_t evtmap;
> +    int rc, ver;
> +
> +    il->evdev = qemu_opt_get(opts, "evdev");
> +    if (!il->evdev) {
> +        error_setg(errp, "no input device specified");
> +        goto err_free;
> +    }
> +
> +    il->fd = open(il->evdev, O_RDWR);
> +    if (il->fd < 0)  {
> +        error_setg_file_open(errp, errno, il->evdev);
> +        goto err_free;
> +    }
> +    qemu_set_nonblock(il->fd);
> +
> +    rc = ioctl(il->fd, EVIOCGVERSION, &ver);
> +    if (rc < 0) {
> +        error_setg(errp, "%s: is not an evdev device", il->evdev);
> +        goto err_close;
> +    }
> +
> +    rc = ioctl(il->fd, EVIOCGBIT(0, sizeof(evtmap)), &evtmap);
> +
> +    if (evtmap & (1 << EV_REL)) {
> +        /* has relative axis -> assume mouse */
> +        qemu_set_fd_handler(il->fd, input_linux_event_mouse, NULL, il);
> +    } else if (evtmap & (1 << EV_ABS)) {
> +        /* has absolute axis -> not supported */
> +        error_setg(errp, "tablet/touchscreen not supported");
> +        goto err_close;
> +    } else if (evtmap & (1 << EV_KEY)) {
> +        /* has keys/buttons (and no axis) -> assume keyboard */
> +        qemu_set_fd_handler(il->fd, input_linux_event_keyboard, NULL, il);
> +    } else {
> +        /* Huh? What is this? */
> +        error_setg(errp, "unknown kind of input device");
> +        goto err_close;
> +    }
> +    input_linux_toggle_grab(il);
> +    return 0;
> +
> +err_close:
> +    close(il->fd);
> +err_free:
> +    g_free(il);
> +    return -1;
> +}
> +
> +static QemuOptsList qemu_input_linux_opts = {
> +    .name = "input-linux",
> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_input_linux_opts.head),
> +    .implied_opt_name = "evdev",
> +    .desc = {
> +        {
> +            .name = "evdev",
> +            .type = QEMU_OPT_STRING,
> +        },
> +        { /* end of list */ }
> +    },
> +};
> +
> +static void input_linux_register_config(void)
> +{
> +    qemu_add_opts(&qemu_input_linux_opts);
> +}
> +machine_init(input_linux_register_config);
> diff --git a/vl.c b/vl.c
> index 4211ff1..993ee44 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -78,6 +78,7 @@ int main(int argc, char **argv)
>  #include "net/slirp.h"
>  #include "monitor/monitor.h"
>  #include "ui/console.h"
> +#include "ui/input.h"
>  #include "sysemu/sysemu.h"
>  #include "sysemu/numa.h"
>  #include "exec/gdbstub.h"
> @@ -3742,6 +3743,12 @@ int main(int argc, char **argv, char **envp)
>  #endif
>                  break;
>              }
> +            case QEMU_OPTION_input_linux:
> +                if
> (!qemu_opts_parse_noisily(qemu_find_opts("input-linux"),
> +                                             optarg, true)) {
> +                    exit(1);
> +                }
> +                break;
>              case QEMU_OPTION_no_acpi:
>                  acpi_enabled = 0;
>                  break;
> @@ -4622,6 +4629,10 @@ int main(int argc, char **argv, char **envp)
>          qemu_spice_display_init();
>      }
>  #endif
> +#ifdef CONFIG_LINUX
> +    qemu_opts_foreach(qemu_find_opts("input-linux"),
> +                      input_linux_init, NULL, &error_fatal);
> +#endif
>
>      if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
>          exit(1);
> --
> 1.8.3.1
>
> _______________________________________________
> vfio-users mailing list
> vfio-users at redhat.com
> https://www.redhat.com/mailman/listinfo/vfio-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/vfio-users/attachments/20151214/d614e219/attachment.htm>


More information about the vfio-users mailing list