[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