Blob Blame History Raw
From afc46d8f3ba54c0a3b536d1ce05b3f8b4743cf17 Mon Sep 17 00:00:00 2001
From: "Daniel P. Berrange" <berrange@redhat.com>
Date: Fri, 13 Sep 2013 14:22:17 +0100
Subject: [PATCH] Only trigger grab sequence upon release

For

  https://bugzilla.redhat.com/show_bug.cgi?id=1007877

GTK-VNC uses the keyboard shortcut ctrl+alt to toggle inside/outside
the VM (aka release pointer). Now Gnome uses ctrl+alt+arrow_key to
switch virtual desktops. So these two shortcuts collide and gtk-vnc
will grab the key presses as soon as ctrl+alt is pressed and toogle
inside/outside the VM, which makes switching desktops impossible.

The solution is to only trigger the grab sequence when the user
releases the key. So if they press Ctrl+alt and then release it,
we trigger, but if they press Ctrl+alt+left-arrow we don't trigger

https://bugzilla.gnome.org/show_bug.cgi?id=685257

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
(cherry picked from commit af4fc65182f8a51d81fab2d7e46bc4a11a9da452)
---
 src/vncdisplay.c | 50 +++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 41 insertions(+), 9 deletions(-)

diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index 37de008..51a6b0f 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -80,6 +80,7 @@ struct _VncDisplayPrivate
     size_t keycode_maplen;
     const guint16 *keycode_map;
 
+    gboolean vncgrabpending; /* Key sequence detected, waiting for release */
     VncGrabSequence *vncgrabseq; /* the configured key sequence */
     gboolean *vncactiveseq; /* the currently pressed keys */
 };
@@ -784,6 +785,17 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
 }
 
 
+/*
+ * Lets say the grab sequence of Ctrl_L + Alt_L
+ *
+ * We first need to detect when both Ctrl_L and Alt_L are pressed.
+ * When this happens we are "primed" to tigger.
+ *
+ * If any further key is pressed though, we unprime ourselves
+ *
+ * If any key is released while we are primed, then we
+ * trigger.
+ */ 
 static gboolean check_for_grab_key(GtkWidget *widget, int type, int keyval)
 {
     VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
@@ -793,23 +805,42 @@ static gboolean check_for_grab_key(GtkWidget *widget, int type, int keyval)
         return FALSE;
 
     if (type == GDK_KEY_RELEASE) {
+        gboolean active = priv->vncgrabpending;
         /* Any key release resets the whole grab sequence */
         memset(priv->vncactiveseq, 0,
                sizeof(gboolean)*priv->vncgrabseq->nkeysyms);
-
-        return FALSE;
+        priv->vncgrabpending = FALSE;
+        return active;
     } else {
+        gboolean setone = FALSE;
+
         /* Record the new key press */
-        for (i = 0 ; i < priv->vncgrabseq->nkeysyms ; i++)
-            if (priv->vncgrabseq->keysyms[i] == keyval)
+        for (i = 0 ; i < priv->vncgrabseq->nkeysyms ; i++) {
+            if (priv->vncgrabseq->keysyms[i] == keyval) {
                 priv->vncactiveseq[i] = TRUE;
+                setone = TRUE;
+            }
+        }
 
-        /* Return if any key is not pressed */
-        for (i = 0 ; i < priv->vncgrabseq->nkeysyms ; i++)
-            if (priv->vncactiveseq[i] == FALSE)
-                return FALSE;
+        if (setone) {
+            /* Return if any key is not pressed */
+            for (i = 0 ; i < priv->vncgrabseq->nkeysyms ; i++)
+                if (priv->vncactiveseq[i] == FALSE)
+                    return FALSE;
 
-        return TRUE;
+            /* All keys in grab seq are pressed, so prime
+             * to trigger on release
+             */
+            priv->vncgrabpending = TRUE;
+        } else {
+            /* Key not in grab seq, so must reset any pending
+             * grab keys we have */
+            memset(priv->vncactiveseq, 0,
+                   sizeof(gboolean)*priv->vncgrabseq->nkeysyms);
+            priv->vncgrabpending = FALSE;
+        }
+
+        return FALSE;
     }
 }
 
@@ -2238,6 +2269,7 @@ void vnc_display_set_pointer_grab(VncDisplay *obj, gboolean enable)
  */
 void vnc_display_set_grab_keys(VncDisplay *obj, VncGrabSequence *seq)
 {
+    obj->priv->vncgrabpending = FALSE;
     if (obj->priv->vncgrabseq) {
         vnc_grab_sequence_free(obj->priv->vncgrabseq);
         g_free(obj->priv->vncactiveseq);