* [Qemu-devel] [PATCH 1/2] input: add qemu_input_qcode_to_linux + qemu_input_linux_to_qcode
@ 2015-11-18 17:55 Gerd Hoffmann
2015-11-18 17:55 ` [Qemu-devel] [PATCH 2/2] input: linux evdev support Gerd Hoffmann
0 siblings, 1 reply; 3+ messages in thread
From: Gerd Hoffmann @ 2015-11-18 17:55 UTC (permalink / raw)
To: qemu-devel; +Cc: Jiri 'Ghormoon' Novak, vfio-users, Gerd Hoffmann
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
include/ui/input.h | 3 ++
ui/input-keymap.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+)
diff --git a/include/ui/input.h b/include/ui/input.h
index d06a12d..d7afd80 100644
--- a/include/ui/input.h
+++ b/include/ui/input.h
@@ -48,6 +48,9 @@ int qemu_input_key_value_to_qcode(const KeyValue *value);
int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
int *codes);
+int qemu_input_qcode_to_linux(unsigned int qcode);
+int qemu_input_linux_to_qcode(unsigned int lnx);
+
InputEvent *qemu_input_event_new_btn(InputButton btn, bool down);
void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down);
void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map,
diff --git a/ui/input-keymap.c b/ui/input-keymap.c
index d36be4b..31f4daa 100644
--- a/ui/input-keymap.c
+++ b/ui/input-keymap.c
@@ -2,6 +2,126 @@
#include "ui/keymaps.h"
#include "ui/input.h"
+#include "standard-headers/linux/input.h"
+
+/* FIXME: duplicate, see hw/input/virtio-input-hid.c */
+static const unsigned int qcode_to_linux[Q_KEY_CODE_MAX] = {
+ [Q_KEY_CODE_ESC] = KEY_ESC,
+ [Q_KEY_CODE_1] = KEY_1,
+ [Q_KEY_CODE_2] = KEY_2,
+ [Q_KEY_CODE_3] = KEY_3,
+ [Q_KEY_CODE_4] = KEY_4,
+ [Q_KEY_CODE_5] = KEY_5,
+ [Q_KEY_CODE_6] = KEY_6,
+ [Q_KEY_CODE_7] = KEY_7,
+ [Q_KEY_CODE_8] = KEY_8,
+ [Q_KEY_CODE_9] = KEY_9,
+ [Q_KEY_CODE_0] = KEY_0,
+ [Q_KEY_CODE_MINUS] = KEY_MINUS,
+ [Q_KEY_CODE_EQUAL] = KEY_EQUAL,
+ [Q_KEY_CODE_BACKSPACE] = KEY_BACKSPACE,
+
+ [Q_KEY_CODE_TAB] = KEY_TAB,
+ [Q_KEY_CODE_Q] = KEY_Q,
+ [Q_KEY_CODE_W] = KEY_W,
+ [Q_KEY_CODE_E] = KEY_E,
+ [Q_KEY_CODE_R] = KEY_R,
+ [Q_KEY_CODE_T] = KEY_T,
+ [Q_KEY_CODE_Y] = KEY_Y,
+ [Q_KEY_CODE_U] = KEY_U,
+ [Q_KEY_CODE_I] = KEY_I,
+ [Q_KEY_CODE_O] = KEY_O,
+ [Q_KEY_CODE_P] = KEY_P,
+ [Q_KEY_CODE_BRACKET_LEFT] = KEY_LEFTBRACE,
+ [Q_KEY_CODE_BRACKET_RIGHT] = KEY_RIGHTBRACE,
+ [Q_KEY_CODE_RET] = KEY_ENTER,
+
+ [Q_KEY_CODE_CTRL] = KEY_LEFTCTRL,
+ [Q_KEY_CODE_A] = KEY_A,
+ [Q_KEY_CODE_S] = KEY_S,
+ [Q_KEY_CODE_D] = KEY_D,
+ [Q_KEY_CODE_F] = KEY_F,
+ [Q_KEY_CODE_G] = KEY_G,
+ [Q_KEY_CODE_H] = KEY_H,
+ [Q_KEY_CODE_J] = KEY_J,
+ [Q_KEY_CODE_K] = KEY_K,
+ [Q_KEY_CODE_L] = KEY_L,
+ [Q_KEY_CODE_SEMICOLON] = KEY_SEMICOLON,
+ [Q_KEY_CODE_APOSTROPHE] = KEY_APOSTROPHE,
+ [Q_KEY_CODE_GRAVE_ACCENT] = KEY_GRAVE,
+
+ [Q_KEY_CODE_SHIFT] = KEY_LEFTSHIFT,
+ [Q_KEY_CODE_BACKSLASH] = KEY_BACKSLASH,
+ [Q_KEY_CODE_LESS] = KEY_102ND,
+ [Q_KEY_CODE_Z] = KEY_Z,
+ [Q_KEY_CODE_X] = KEY_X,
+ [Q_KEY_CODE_C] = KEY_C,
+ [Q_KEY_CODE_V] = KEY_V,
+ [Q_KEY_CODE_B] = KEY_B,
+ [Q_KEY_CODE_N] = KEY_N,
+ [Q_KEY_CODE_M] = KEY_M,
+ [Q_KEY_CODE_COMMA] = KEY_COMMA,
+ [Q_KEY_CODE_DOT] = KEY_DOT,
+ [Q_KEY_CODE_SLASH] = KEY_SLASH,
+ [Q_KEY_CODE_SHIFT_R] = KEY_RIGHTSHIFT,
+
+ [Q_KEY_CODE_ALT] = KEY_LEFTALT,
+ [Q_KEY_CODE_SPC] = KEY_SPACE,
+ [Q_KEY_CODE_CAPS_LOCK] = KEY_CAPSLOCK,
+
+ [Q_KEY_CODE_F1] = KEY_F1,
+ [Q_KEY_CODE_F2] = KEY_F2,
+ [Q_KEY_CODE_F3] = KEY_F3,
+ [Q_KEY_CODE_F4] = KEY_F4,
+ [Q_KEY_CODE_F5] = KEY_F5,
+ [Q_KEY_CODE_F6] = KEY_F6,
+ [Q_KEY_CODE_F7] = KEY_F7,
+ [Q_KEY_CODE_F8] = KEY_F8,
+ [Q_KEY_CODE_F9] = KEY_F9,
+ [Q_KEY_CODE_F10] = KEY_F10,
+ [Q_KEY_CODE_NUM_LOCK] = KEY_NUMLOCK,
+ [Q_KEY_CODE_SCROLL_LOCK] = KEY_SCROLLLOCK,
+
+ [Q_KEY_CODE_KP_0] = KEY_KP0,
+ [Q_KEY_CODE_KP_1] = KEY_KP1,
+ [Q_KEY_CODE_KP_2] = KEY_KP2,
+ [Q_KEY_CODE_KP_3] = KEY_KP3,
+ [Q_KEY_CODE_KP_4] = KEY_KP4,
+ [Q_KEY_CODE_KP_5] = KEY_KP5,
+ [Q_KEY_CODE_KP_6] = KEY_KP6,
+ [Q_KEY_CODE_KP_7] = KEY_KP7,
+ [Q_KEY_CODE_KP_8] = KEY_KP8,
+ [Q_KEY_CODE_KP_9] = KEY_KP9,
+ [Q_KEY_CODE_KP_SUBTRACT] = KEY_KPMINUS,
+ [Q_KEY_CODE_KP_ADD] = KEY_KPPLUS,
+ [Q_KEY_CODE_KP_DECIMAL] = KEY_KPDOT,
+ [Q_KEY_CODE_KP_ENTER] = KEY_KPENTER,
+ [Q_KEY_CODE_KP_DIVIDE] = KEY_KPSLASH,
+ [Q_KEY_CODE_KP_MULTIPLY] = KEY_KPASTERISK,
+
+ [Q_KEY_CODE_F11] = KEY_F11,
+ [Q_KEY_CODE_F12] = KEY_F12,
+
+ [Q_KEY_CODE_CTRL_R] = KEY_RIGHTCTRL,
+ [Q_KEY_CODE_SYSRQ] = KEY_SYSRQ,
+ [Q_KEY_CODE_ALT_R] = KEY_RIGHTALT,
+
+ [Q_KEY_CODE_HOME] = KEY_HOME,
+ [Q_KEY_CODE_UP] = KEY_UP,
+ [Q_KEY_CODE_PGUP] = KEY_PAGEUP,
+ [Q_KEY_CODE_LEFT] = KEY_LEFT,
+ [Q_KEY_CODE_RIGHT] = KEY_RIGHT,
+ [Q_KEY_CODE_END] = KEY_END,
+ [Q_KEY_CODE_DOWN] = KEY_DOWN,
+ [Q_KEY_CODE_PGDN] = KEY_PAGEDOWN,
+ [Q_KEY_CODE_INSERT] = KEY_INSERT,
+ [Q_KEY_CODE_DELETE] = KEY_DELETE,
+
+ [Q_KEY_CODE_META_L] = KEY_LEFTMETA,
+ [Q_KEY_CODE_META_R] = KEY_RIGHTMETA,
+ [Q_KEY_CODE_MENU] = KEY_MENU,
+};
+
static const int qcode_to_number[] = {
[Q_KEY_CODE_SHIFT] = 0x2a,
[Q_KEY_CODE_SHIFT_R] = 0x36,
@@ -136,6 +256,7 @@ static const int qcode_to_number[] = {
};
static int number_to_qcode[0x100];
+static int linux_to_qcode[KEY_CNT];
int qemu_input_key_value_to_number(const KeyValue *value)
{
@@ -200,3 +321,27 @@ int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
return count;
}
+
+int qemu_input_qcode_to_linux(unsigned int qcode)
+{
+ assert(qcode < Q_KEY_CODE_MAX);
+ return qcode_to_linux[qcode];
+}
+
+int qemu_input_linux_to_qcode(unsigned int lnx)
+{
+ static int first = true;
+
+ if (first) {
+ int qcode, number;
+ first = false;
+ for (qcode = 0; qcode < Q_KEY_CODE_MAX; qcode++) {
+ number = qcode_to_linux[qcode];
+ assert(number < KEY_CNT);
+ linux_to_qcode[number] = qcode;
+ }
+ }
+
+ assert(lnx < KEY_CNT);
+ return linux_to_qcode[lnx];
+}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [Qemu-devel] [PATCH 2/2] input: linux evdev support
2015-11-18 17:55 [Qemu-devel] [PATCH 1/2] input: add qemu_input_qcode_to_linux + qemu_input_linux_to_qcode Gerd Hoffmann
@ 2015-11-18 17:55 ` Gerd Hoffmann
2015-11-19 19:00 ` [Qemu-devel] [vfio-users] " Blank Field
0 siblings, 1 reply; 3+ messages in thread
From: Gerd Hoffmann @ 2015-11-18 17:55 UTC (permalink / raw)
To: qemu-devel
Cc: Jiri 'Ghormoon' Novak, Paolo Bonzini, vfio-users,
Gerd Hoffmann
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@redhat.com>
---
include/ui/input.h | 2 +
qemu-options.hx | 9 +++
ui/Makefile.objs | 1 +
ui/input-linux.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++
vl.c | 11 +++
5 files changed, 217 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
+@item -input-linux @var{dev}
+@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..3c8d587
--- /dev/null
+++ b/ui/input-linux.c
@@ -0,0 +1,194 @@
+/*
+ * 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;
+};
+
+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:
+ /* 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(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;
+ };
+ 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;
+ }
+ break;
+ case EV_SYN:
+ qemu_input_event_sync();
+ 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_ABS)) {
+ error_setg(errp, "tablet/touchscreen not supported");
+ goto err_close;
+ } else if (evtmap & (1 << EV_REL)) {
+ qemu_set_fd_handler(il->fd, input_linux_event_mouse, NULL, il);
+ } else {
+ qemu_set_fd_handler(il->fd, input_linux_event_keyboard, NULL, il);
+ }
+ 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 7d993a5..3db515d 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"
@@ -3739,6 +3740,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;
@@ -4619,6 +4626,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
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [Qemu-devel] [vfio-users] [PATCH 2/2] input: linux evdev support
2015-11-18 17:55 ` [Qemu-devel] [PATCH 2/2] input: linux evdev support Gerd Hoffmann
@ 2015-11-19 19:00 ` Blank Field
0 siblings, 0 replies; 3+ messages in thread
From: Blank Field @ 2015-11-19 19:00 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: Jiri 'Ghormoon' Novak, Paolo Bonzini, qemu-devel,
vfio-users
2015-11-18 20:55 GMT+03:00 Gerd Hoffmann <kraxel@redhat.com>:
> 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.
You might already know that there are
-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]
-ctrl-grab use Right-Ctrl to grab mouse (instead of Ctrl-Alt)
options in regular qemu. Maybe it'd be convenient to use this switch
to change the "ungrab" key combination? Or make something similiar?
Personally I use SDL, and i'm pretty much happy with it except random
SegFaults.(i see some connection to disk activity, however).
SDL creates a surface(which is getting decorated by the WM) which
grabs all the input, filters it for ctrl-alt-shift release
combination, and sends it to the VM.
The other minor problem is that SDL scales mouse movements relative to
surface size.
Before SDL i used qemu's default graphics with all the fancy buttons
it provided(it sent the power button event too!), but it doesn't
transfer audio(SDL does that) and sometimes the mouse freezes. But it
specially required a "graphics" device to be attached, even if it's
just -device qxl which isn't touched by the guest system at all.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2015-11-19 19:00 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-11-18 17:55 [Qemu-devel] [PATCH 1/2] input: add qemu_input_qcode_to_linux + qemu_input_linux_to_qcode Gerd Hoffmann
2015-11-18 17:55 ` [Qemu-devel] [PATCH 2/2] input: linux evdev support Gerd Hoffmann
2015-11-19 19:00 ` [Qemu-devel] [vfio-users] " Blank Field
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).