diff mbox series

input-linux: customizable grab toggle keys v3

Message ID 45OPfGmaK-Sz9ZGzbGSmlE0AgSRiVp3mCkNrkJC2k6s4c9wOOcegenAJ6muFQwlGl1gBTMordKEgVU2tv9dThhXMDKaJPSZ6tNTMclo_n1E=@protonmail.com
State New
Headers show
Series input-linux: customizable grab toggle keys v3 | expand

Commit Message

Cameron Esfahani via Sept. 3, 2018, 4:32 p.m. UTC
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.

I have also attached the patch contained in the body of this email
since my mail client does not seem to behave well with patches.
If this is a problem, please let me know.

Signed-off-by: Ryan El Kochta <lightn1ngstr1ke@protonmail.com>
---
ui/input-linux.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 153 insertions(+), 3 deletions(-)

--
2.18.0

Comments

Gerd Hoffmann Sept. 13, 2018, 9:04 a.m. UTC | #1
On Mon, Sep 03, 2018 at 04:32:10PM +0000, Ryan El Kochta wrote:
> This patch adds a new option to the input-linux object:
> 
> grab_toggle=key-key-key

"grab-toggle" (no underscore) please.

I'm still not convinced we need that much flexibility.
I would go for a fixed list of combinations.

Suggestion: add a enum to qapi (see QKeyCode in qapi/ui.json for an
example), that way you'll get a bunch of helper code and lookup tables
generated.  Simplifies the option parsing, you can use
object_property_add_enum() then.

> I have also attached the patch contained in the body of this email
> since my mail client does not seem to behave well with patches.
> If this is a problem, please let me know.

Using 'git send-email' is the best way to send out patch mails.

Must be set up once, but when done it is less work for both sending and
receiving side.

cheers,
  Gerd
diff mbox series

Patch

From f9f13b91592877bca1e201cb20877b7876d5cf04 Mon Sep 17 00:00:00 2001
From: Ryan El Kochta <lightn1ngstr1ke@protonmail.com>
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 <lightn1ngstr1ke@protonmail.com>
---
 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 <sys/ioctl.h>
 #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