This patch introduces a new string option to the input-linux commandline: toggle_keys=[num]:[num]:[num] ...
Separated by colons, toggle_keys allows the user to specify the key IDs of all the keys desired for switching between the guest and host. These key IDs can be found in: include/standard-headers/linux/input-event-codes.h and are prefixed with KEY_. If the option is malformed or not specified, it will use the current default of KEY_LEFTCTRL + KEY_RIGHTCTRL. --- ui/input-linux.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 2 deletions(-) diff --git a/ui/input-linux.c b/ui/input-linux.c index 9720333b2c..38878f0836 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 <sys/ioctl.h> #include "standard-headers/linux/input.h" @@ -63,6 +64,10 @@ struct InputLinux { struct input_event event; int read_offset; + char *toggle_keys; + int toggle_keys_len; + int *toggle_keys_list; + QTAILQ_ENTRY(InputLinux) next; }; @@ -98,6 +103,16 @@ static void input_linux_toggle_grab(InputLinux *il) } } +static bool input_linux_check_toggle_keys(InputLinux *il) +{ + for (unsigned i = 0; i < il->toggle_keys_len; i++) { + if (!il->keydown[il->toggle_keys_list[i]]) { + return false; + } + } + return true; +} + static void input_linux_handle_keyboard(InputLinux *il, struct input_event *event) { @@ -134,8 +149,7 @@ static void input_linux_handle_keyboard(InputLinux *il, } /* hotkey -> record switch request ... */ - if (il->keydown[KEY_LEFTCTRL] && - il->keydown[KEY_RIGHTCTRL]) { + if (input_linux_check_toggle_keys(il)) { il->grab_request = true; } @@ -274,6 +288,18 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) return; } + /* + * If the toggle_keys char was not provided, set + * the default of KEY_LEFTCTRL and KEY_RIGHTCTRL + */ + if (!il->toggle_keys || !il->toggle_keys_list || !il->toggle_keys_len) { + il->toggle_keys = NULL; + il->toggle_keys_len = 2; + il->toggle_keys_list = g_new(int, 2); + il->toggle_keys_list[0] = KEY_LEFTCTRL; + il->toggle_keys_list[1] = KEY_RIGHTCTRL; + } + il->fd = open(il->evdev, O_RDWR); if (il->fd < 0) { error_setg_file_open(errp, errno, il->evdev); @@ -359,6 +385,11 @@ static void input_linux_instance_finalize(Object *obj) close(il->fd); } g_free(il->evdev); + + if (il->toggle_keys) { + g_free(il->toggle_keys); + } + g_free(il->toggle_keys_list); } static char *input_linux_get_evdev(Object *obj, Error **errp) @@ -410,11 +441,112 @@ static void input_linux_set_repeat(Object *obj, bool value, il->repeat = value; } +static void input_linux_populate_toggle_keys(InputLinux *il) +{ + /* + * If no toggle_keys was provided, return zero keys. + * This will be cleaned up in the input_linux_complete() + * function and reset to both Ctrl keys. + */ + if (!il->toggle_keys) { + il->toggle_keys_len = 0; + il->toggle_keys_list = NULL; + return; + } + + /* + * Iterate through each token in the toggle_keys string, + * separated by colons, and add it to the il->toggle_keys_list + * array. + * + * Unfortunately this must be done twice in order to + * determine how much memory will be allocated later on. + */ + + /* First scan */ + il->toggle_keys_len = 0; + + char *orig, *token, *saveptr; + + orig = g_strdup(il->toggle_keys); + saveptr = orig; + + while (strtok_r(saveptr, ":", &saveptr)) { + il->toggle_keys_len++; + } + + /* + * If the count found zero tokens, return an empty list. + * Again, this will be cleaned up in input_linux_complete(). + */ + if (!il->toggle_keys_len) { + il->toggle_keys_list = NULL; + g_free(orig); + return; + } + + /* Second scan */ + il->toggle_keys_list = g_new(int, il->toggle_keys_len); + + strcpy(orig, il->toggle_keys); + saveptr = orig; + unsigned cntr = 0; + + /* Add each token's int representation to the list */ + while ((token = strtok_r(saveptr, ":", &saveptr))) { + long val = 0; + int strtol_ret = qemu_strtol(token, NULL, 10, &val); + + /* + * Check to ensure (a) qemu_strtol() did not fail + * and (b) the integer it returned is within the + * range of possible keys + */ + if (strtol_ret > 0 || !(val > 0 && val < KEY_CNT)) { + g_free(il->toggle_keys_list); + g_free(orig); + il->toggle_keys_len = 0; + il->toggle_keys_list = NULL; + return; + } + + il->toggle_keys_list[cntr] = val; + cntr++; + } + + g_free(orig); +} + +static void input_linux_set_toggle_keys(Object *obj, const char *value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + if (il->toggle_keys) { + error_setg(errp, "toggle_keys property already set"); + return; + } + + il->toggle_keys = g_strdup(value); + + input_linux_populate_toggle_keys(il); +} + +static char *input_linux_get_toggle_keys(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return g_strdup(il->toggle_keys); +} + 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, "toggle_keys", + input_linux_get_toggle_keys, + input_linux_set_toggle_keys, NULL); object_property_add_bool(obj, "grab_all", input_linux_get_grab_all, input_linux_set_grab_all, NULL); -- 2.18.0