From f9f13b91592877bca1e201cb20877b7876d5cf04 Mon Sep 17 00:00:00 2001 From: Ryan El Kochta Date: Mon, 3 Sep 2018 12:19:51 -0400 Subject: [PATCH] input-linux: customizable grab toggle keys v3 This patch adds a new option to the input-linux object: grab_toggle=key-key-key These keys can be one of the following: * lctrl * rctrl * lalt * ralt * lshift * rshift * backtick * scrolllock The user can pick any combination of these keys. The VM's grab of the evdev device will be toggled when all of the selected keys are pressed at the same time. If grab_toggle is not specified or malformed, the current default of lctrl+rctrl will be used. If scrolllock is selected as one of the grab_toggle keys, it will be entirely disabled and not passed to the guest at all. This is to prevent enabling it while attempting to leave or enter the VM. On the host, scrolllock can be disabled using xmodmap. First, find the modifier that Scroll_Lock is bound to: xmodmap -pm Then, remove Scroll_Lock from it, replacing modX with the modifier: xmodmap -e 'remove modX = Scroll_Lock' If Scroll_Lock is not bound to any modifier, it is already disabled. To save the changes, add them to your xinitrc. Signed-off-by: Ryan El Kochta --- ui/input-linux.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 3 deletions(-) diff --git a/ui/input-linux.c b/ui/input-linux.c index 9720333b2c..2cbc877667 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -12,6 +12,7 @@ #include "sysemu/sysemu.h" #include "ui/input.h" #include "qom/object_interfaces.h" +#include "include/qemu/cutils.h" #include #include "standard-headers/linux/input.h" @@ -63,6 +64,11 @@ struct InputLinux { struct input_event event; int read_offset; + char *grab_toggle; + int grab_toggle_len; + int *grab_toggle_list; + bool skip_scrolllock; + QTAILQ_ENTRY(InputLinux) next; }; @@ -98,6 +104,25 @@ static void input_linux_toggle_grab(InputLinux *il) } } +static bool input_linux_check_grab_toggle(InputLinux *il) +{ + for (unsigned i = 0; i < il->grab_toggle_len; i++) { + if (!il->keydown[il->grab_toggle_list[i]]) { + return false; + } + } + return true; +} + +static bool input_linux_check_skip_scrolllock(InputLinux *il, + struct input_event *event) +{ + if (il->skip_scrolllock && event->code == KEY_SCROLLLOCK) { + return true; + } + return false; +} + static void input_linux_handle_keyboard(InputLinux *il, struct input_event *event) { @@ -128,14 +153,13 @@ static void input_linux_handle_keyboard(InputLinux *il, } /* send event to guest when grab is active */ - if (il->grab_active) { + if (il->grab_active && !input_linux_check_skip_scrolllock(il, event)) { 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]) { + if (input_linux_check_grab_toggle(il)) { il->grab_request = true; } @@ -274,6 +298,15 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) return; } + if(!il->grab_toggle || !il->grab_toggle_len || !il->grab_toggle_list) { + il->skip_scrolllock = false; + il->grab_toggle = NULL; + il->grab_toggle_len = 2; + il->grab_toggle_list = g_new(int, 2); + il->grab_toggle_list[0] = KEY_LEFTCTRL; + il->grab_toggle_list[1] = KEY_RIGHTCTRL; + } + il->fd = open(il->evdev, O_RDWR); if (il->fd < 0) { error_setg_file_open(errp, errno, il->evdev); @@ -410,11 +443,128 @@ static void input_linux_set_repeat(Object *obj, bool value, il->repeat = value; } +static void input_linux_populate_grab_toggle(InputLinux *il) +{ + char *orig, *saveptr, *token; + unsigned cntr = 0; + + /* + * If no grab_toggle string was provided, return. + * This will be cleaned up in the input_linux_complete() + * function and reset to both Ctrl keys. + */ + if (!il->grab_toggle) { + return; + } + + il->skip_scrolllock = false; + + /* Create a new string for strtok_r */ + orig = g_strdup(il->grab_toggle); + + /* + * Iterate through each token in the string, + * separated by dashes, and add it to the il->grab_toggle_list + * array. + * + * Unfortunately this must be done twice in order to + * determine how much memory will be allocated later on. + */ + + /* First iteration */ + il->grab_toggle_len = 0; + saveptr = orig; + while (strtok_r(saveptr, "-", &saveptr)) { + il->grab_toggle_len++; + /* If this overflows, use the default */ + if (il->grab_toggle_len < 0) { + il->grab_toggle_len = 0; + break; + } + } + + /* + * If the count found zero tokens, return an empty list. + * Again, this will be cleaned up in input_linux_complete(). + */ + if (!il->grab_toggle_len) { + g_free(orig); + return; + } + + /* Second scan */ + il->grab_toggle_list = g_new(int, il->grab_toggle_len); + strcpy(orig, il->grab_toggle); + saveptr = orig; + + /* Add each token's key to the list */ + while ((token = strtok_r(saveptr, "-", &saveptr))) { + if (!strcmp(token, "lctrl")) { + il->grab_toggle_list[cntr] = KEY_LEFTCTRL; + } else if (!strcmp(token, "rctrl")) { + il->grab_toggle_list[cntr] = KEY_RIGHTCTRL; + } else if (!strcmp(token, "lalt")) { + il->grab_toggle_list[cntr] = KEY_LEFTALT; + } else if (!strcmp(token, "ralt")) { + il->grab_toggle_list[cntr] = KEY_RIGHTALT; + } else if (!strcmp(token, "lshift")) { + il->grab_toggle_list[cntr] = KEY_LEFTSHIFT; + } else if (!strcmp(token, "rshift")) { + il->grab_toggle_list[cntr] = KEY_RIGHTSHIFT; + } else if (!strcmp(token, "backtick")) { + il->grab_toggle_list[cntr] = KEY_GRAVE; + } else if (!strcmp(token, "scrolllock")) { + il->grab_toggle_list[cntr] = KEY_SCROLLLOCK; + il->skip_scrolllock = true; + } else { + /* + * If any of the parameters are invalid, + * stick to the default of LCtrl+RCtrl. + * + * We could just skip that parameter, but + * that means if you input only invalid parameters, + * there would be no way to escape the VM. + */ + il->grab_toggle_len = 0; + g_free(orig); + g_free(il->grab_toggle_list); + return; + } + + cntr++; + } + g_free(orig); +} + +static char *input_linux_get_grab_toggle(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return g_strdup(il->grab_toggle); +} + +static void input_linux_set_grab_toggle(Object *obj, const char *value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + if (il->grab_toggle) { + error_setg(errp, "grab_toggle property already set"); + return; + } + il->grab_toggle = g_strdup(value); + + input_linux_populate_grab_toggle(il); +} + static void input_linux_instance_init(Object *obj) { object_property_add_str(obj, "evdev", input_linux_get_evdev, input_linux_set_evdev, NULL); + object_property_add_str(obj, "grab_toggle", + input_linux_get_grab_toggle, + input_linux_set_grab_toggle, NULL); object_property_add_bool(obj, "grab_all", input_linux_get_grab_all, input_linux_set_grab_all, NULL); -- 2.18.0