@@ -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);