[Spice-devel] [PATCH spice-gtk v3] Release keyboard grab using keyboard shortcut

Pavel Grunt pgrunt at redhat.com
Wed Nov 19 04:39:27 PST 2014


This commit adds the ability to release the keyboard grab when
the release keys (ctrl+alt) are pressed and released. It allows
to use keyboard shortcuts (eg alt+tab, alt+f4) on the client.

The keyboard is grabbed again when the release keys are pressed
and released or when the mouse moves.

https://bugs.freedesktop.org/show_bug.cgi?id=85331
---
v3:
 - don't grab when the keyboard doesn't have the focus
v2:
 - add missing 'release_keys(display)' calls
---
 gtk/spice-widget-priv.h |  2 ++
 gtk/spice-widget.c      | 50 ++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index 9c38e2e..0e1f661 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -109,6 +109,8 @@ struct _SpiceDisplayPrivate {
     guint                   key_delayed_id;
     SpiceGrabSequence         *grabseq; /* the configured key sequence */
     gboolean                *activeseq; /* the currently pressed keys */
+    gboolean                seq_pressed;
+    gboolean                keyboard_grab_released;
     gint                    mark;
 #ifdef WIN32
     HHOOK                   keyboard_hook;
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index ae11073..550d08d 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -719,6 +719,8 @@ static void try_keyboard_grab(SpiceDisplay *display)
         return;
     if (!d->mouse_have_pointer)
         return;
+    if (d->keyboard_grab_released)
+        return;
 
     g_return_if_fail(gtk_widget_is_focus(widget));
 
@@ -1198,6 +1200,9 @@ static void send_key(SpiceDisplay *display, int scancode, SendKeyType type, gboo
     if (d->disable_inputs)
         return;
 
+    if (d->keyboard_grab_released)
+        return;
+
     i = scancode / 32;
     b = scancode % 32;
     m = (1 << b);
@@ -1259,7 +1264,8 @@ static void release_keys(SpiceDisplay *display)
     }
 }
 
-static gboolean check_for_grab_key(SpiceDisplay *display, int type, int keyval)
+static gboolean check_for_grab_key(SpiceDisplay *display, int type, int keyval,
+                                   int check_type, int reset_type)
 {
     SpiceDisplayPrivate *d = display->priv;
     int i;
@@ -1267,13 +1273,13 @@ static gboolean check_for_grab_key(SpiceDisplay *display, int type, int keyval)
     if (!d->grabseq->nkeysyms)
         return FALSE;
 
-    if (type == GDK_KEY_PRESS) {
-        /* Record the new key press */
+    if (type == check_type) {
+        /* Record the new key */
         for (i = 0 ; i < d->grabseq->nkeysyms ; i++)
             if (d->grabseq->keysyms[i] == keyval)
                 d->activeseq[i] = TRUE;
 
-        /* Return if any key is not pressed */
+        /* Return if any key is missing */
         for (i = 0 ; i < d->grabseq->nkeysyms ; i++)
             if (d->activeseq[i] == FALSE)
                 return FALSE;
@@ -1281,9 +1287,10 @@ static gboolean check_for_grab_key(SpiceDisplay *display, int type, int keyval)
         /* resets the whole grab sequence on success */
         memset(d->activeseq, 0, sizeof(gboolean) * d->grabseq->nkeysyms);
         return TRUE;
-    } else if (type == GDK_KEY_RELEASE) {
-        /* Any key release resets the whole grab sequence */
+    } else if (type == reset_type) {
+        /* reset key event type resets the whole grab sequence */
         memset(d->activeseq, 0, sizeof(gboolean) * d->grabseq->nkeysyms);
+        d->seq_pressed = FALSE;
         return FALSE;
     } else
         g_warn_if_reached();
@@ -1291,6 +1298,16 @@ static gboolean check_for_grab_key(SpiceDisplay *display, int type, int keyval)
     return FALSE;
 }
 
+static gboolean check_for_grab_key_pressed(SpiceDisplay *display, int type, int keyval)
+{
+    return check_for_grab_key(display, type, keyval, GDK_KEY_PRESS, GDK_KEY_RELEASE);
+}
+
+static gboolean check_for_grab_key_released(SpiceDisplay *display, int type, int keyval)
+{
+    return check_for_grab_key(display, type, keyval, GDK_KEY_RELEASE, GDK_KEY_PRESS);
+}
+
 static void update_display(SpiceDisplay *display)
 {
 #ifdef G_OS_WIN32
@@ -1321,7 +1338,7 @@ static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
             __FUNCTION__, key->type == GDK_KEY_PRESS ? "press" : "release",
             key->hardware_keycode, key->state, key->group, key->is_modifier);
 
-    if (check_for_grab_key(display, key->type, key->keyval)) {
+    if (!d->seq_pressed && check_for_grab_key_pressed(display, key->type, key->keyval)) {
         g_signal_emit(widget, signals[SPICE_DISPLAY_GRAB_KEY_PRESSED], 0);
 
         if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER) {
@@ -1330,6 +1347,17 @@ static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
             else
                 try_mouse_grab(display);
         }
+        d->seq_pressed = TRUE;
+    } else if (d->seq_pressed && check_for_grab_key_released(display, key->type, key->keyval)) {
+        release_keys(display);
+        if (!d->keyboard_grab_released) {
+            d->keyboard_grab_released = TRUE;
+            try_keyboard_ungrab(display);
+        } else {
+            d->keyboard_grab_released = FALSE;
+            try_keyboard_grab(display);
+        }
+        d->seq_pressed = FALSE;
     }
 
     if (!d->inputs)
@@ -1562,6 +1590,14 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
     if (d->disable_inputs)
         return true;
 
+    d->seq_pressed = FALSE;
+
+    if (d->keyboard_grab_released && d->keyboard_have_focus) {
+        d->keyboard_grab_released = FALSE;
+        release_keys(display);
+        try_keyboard_grab(display);
+    }
+
     spicex_transform_input (display, motion->x, motion->y, &x, &y);
 
     switch (d->mouse_mode) {
-- 
1.9.3



More information about the Spice-devel mailing list