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 --- v4: - when 'focus in' clear activeseq, because it can wrongly contain keys that were pressed but missed the event they were released 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 | 52 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 47 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..87d2bb0 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) @@ -1457,6 +1485,8 @@ static gboolean focus_in_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UN release_keys(display); if (!d->disable_inputs) spice_gtk_session_sync_keyboard_modifiers(d->gtk_session); + if (d->keyboard_grab_released) + memset(d->activeseq, 0, sizeof(gboolean) * d->grabseq->nkeysyms); update_keyboard_focus(display, true); try_keyboard_grab(display); update_display(display); @@ -1562,6 +1592,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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel