diff mbox series

input-linux: customizable toggle keys

Message ID 8f7eYzw9BgwUeH_z45Rgcwu8DVy8fSamlQ-f-VsR2pj9TgnAJBMrCoZjxzT6XN5XojiHMMEbOOgy-gHU0KuLiSD7YerZJPI8TZYaBdhCCIs=@protonmail.com (mailing list archive)
State New, archived
Headers show
Series input-linux: customizable toggle keys | expand

Commit Message

Denis V. Lunev" via Aug. 23, 2018, 8:46 p.m. UTC
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(-)

--
2.18.0
diff mbox series

Patch

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