[Spice-devel] [spice-gtk EXP] Handle mismatching CapsLock

Frediano Ziglio fziglio at redhat.com
Fri Apr 22 14:43:03 UTC 2016


Do not sync if we don't have the focus.
If user press the CapsLock but got a mismatch (guest did not reply as
expected) do not try to fix the caps lock.

This patch is really experimental. For instance contains printf
as debugging. Also I think the keyboard logic would be best moved
entirely to SpiceGtkSession.

Despite however style looks to work very well with different keyboard
layouts and combinations. I would like to have it tested from the
user experience more that code.

Tested:
- reboot turn again CapsLock to on if client is on;
- English client and English guest;
- English client and Japanese guest;
- Japanese client with English guest;
- Japanese client with Japanese guest.

The only issue that I found is that when the client is focused
again the CapsLock is emulated and if guest layout is Japanese
this could cause input mode to be changed.

---
 src/spice-gtk-session-priv.h |  3 ++-
 src/spice-gtk-session.c      | 54 ++++++++++++++++++++++++++++++++++++++++++--
 src/spice-widget.c           |  9 +++++++-
 3 files changed, 62 insertions(+), 4 deletions(-)

diff --git a/src/spice-gtk-session-priv.h b/src/spice-gtk-session-priv.h
index b2b6206..d5493ab 100644
--- a/src/spice-gtk-session-priv.h
+++ b/src/spice-gtk-session-priv.h
@@ -25,13 +25,14 @@ G_BEGIN_DECLS
 void spice_gtk_session_request_auto_usbredir(SpiceGtkSession *self,
                                              gboolean state);
 gboolean spice_gtk_session_get_read_only(SpiceGtkSession *self);
-void spice_gtk_session_sync_keyboard_modifiers(SpiceGtkSession *self);
 void spice_gtk_session_set_pointer_grabbed(SpiceGtkSession *self, gboolean grabbed);
 gboolean spice_gtk_session_get_pointer_grabbed(SpiceGtkSession *self);
 void spice_gtk_session_set_keyboard_has_focus(SpiceGtkSession *self, gboolean keyboard_has_focus);
 void spice_gtk_session_set_mouse_has_pointer(SpiceGtkSession *self, gboolean  mouse_has_pointer);
 gboolean spice_gtk_session_get_keyboard_has_focus(SpiceGtkSession *self);
 gboolean spice_gtk_session_get_mouse_has_pointer(SpiceGtkSession *self);
+void spice_gtk_session_set_focus(SpiceGtkSession *self, gboolean focus);
+void spice_gtk_session_capslock_changed(SpiceGtkSession *self);
 
 G_END_DECLS
 
diff --git a/src/spice-gtk-session.c b/src/spice-gtk-session.c
index 4201ee0..74c84f7 100644
--- a/src/spice-gtk-session.c
+++ b/src/spice-gtk-session.c
@@ -66,6 +66,10 @@ struct _SpiceGtkSessionPrivate {
     gboolean                pointer_grabbed;
     gboolean                keyboard_has_focus;
     gboolean                mouse_has_pointer;
+    /** the session has a window with the focus */
+    gboolean                has_focus;
+    /** last time a caps lock was pressed */
+    gint64                  last_capslock_change;
 };
 
 /**
@@ -103,6 +107,7 @@ static void channel_new(SpiceSession *session, SpiceChannel *channel,
 static void channel_destroy(SpiceSession *session, SpiceChannel *channel,
                             gpointer user_data);
 static gboolean read_only(SpiceGtkSession *self);
+static void spice_gtk_session_sync_keyboard_modifiers(SpiceGtkSession *self);
 
 /* ------------------------------------------------------------------ */
 /* gobject glue                                                       */
@@ -178,6 +183,12 @@ static guint32 get_keyboard_lock_modifiers(void)
     return modifiers;
 }
 
+static gboolean spice_gtk_capslock_is_expected(SpiceGtkSession *self)
+{
+    /* expect a change in 3 seconds from press/release */
+    return g_get_monotonic_time() - self->priv->last_capslock_change < 3000000;
+}
+
 static void spice_gtk_session_sync_keyboard_modifiers_for_channel(SpiceGtkSession *self,
                                                                   SpiceInputsChannel* inputs,
                                                                   gboolean force)
@@ -189,6 +200,20 @@ static void spice_gtk_session_sync_keyboard_modifiers_for_channel(SpiceGtkSessio
     g_object_get(inputs, "key-modifiers", &guest_modifiers, NULL);
     client_modifiers = get_keyboard_lock_modifiers();
 
+printf("sync force %d client %d guest %d has_focus %d\n", 
+        force, client_modifiers&SPICE_INPUTS_CAPS_LOCK, guest_modifiers&SPICE_INPUTS_CAPS_LOCK,
+        self->priv->has_focus);
+    /* detect if we should swicth the caps lock */
+    if (!force) {
+        if (!self->priv->has_focus)
+            return;
+        if (spice_gtk_capslock_is_expected(self)) {
+printf("not syncing as caps expected but mismatch\n");
+            client_modifiers = (client_modifiers & ~SPICE_INPUTS_CAPS_LOCK)
+                               | (guest_modifiers & SPICE_INPUTS_CAPS_LOCK);
+        }
+    }
+
     if (force || client_modifiers != guest_modifiers) {
         CHANNEL_DEBUG(inputs, "client_modifiers:0x%x, guest_modifiers:0x%x",
                       client_modifiers, guest_modifiers);
@@ -1199,8 +1224,7 @@ void spice_gtk_session_paste_from_guest(SpiceGtkSession *self)
     s->clip_hasdata[selection] = FALSE;
 }
 
-G_GNUC_INTERNAL
-void spice_gtk_session_sync_keyboard_modifiers(SpiceGtkSession *self)
+static void spice_gtk_session_sync_keyboard_modifiers(SpiceGtkSession *self)
 {
     GList *l = NULL, *channels = spice_session_get_channels(self->priv->session);
 
@@ -1223,6 +1247,32 @@ void spice_gtk_session_set_pointer_grabbed(SpiceGtkSession *self, gboolean grabb
 }
 
 G_GNUC_INTERNAL
+void spice_gtk_session_set_focus(SpiceGtkSession *self, gboolean focus)
+{
+    g_return_if_fail(SPICE_IS_GTK_SESSION(self));
+
+    /* TODO detect switch between monitors */
+
+    /* user switched to the viewer, try to syncronize the keyboard
+     * modifiers */
+    if (focus)
+        spice_gtk_session_sync_keyboard_modifiers(self);
+
+
+printf("setting focus %d\n", focus);
+    self->priv->has_focus = focus;
+}
+
+G_GNUC_INTERNAL
+void spice_gtk_session_capslock_changed(SpiceGtkSession *self)
+{
+    g_return_if_fail(SPICE_IS_GTK_SESSION(self));
+
+printf("caps lock\n");
+    self->priv->last_capslock_change = g_get_monotonic_time();
+}
+
+G_GNUC_INTERNAL
 gboolean spice_gtk_session_get_pointer_grabbed(SpiceGtkSession *self)
 {
     g_return_val_if_fail(SPICE_IS_GTK_SESSION(self), FALSE);
diff --git a/src/spice-widget.c b/src/spice-widget.c
index ade3ae4..05aa97d 100644
--- a/src/spice-widget.c
+++ b/src/spice-widget.c
@@ -1465,6 +1465,10 @@ static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
         break;
     }
 
+    /* tell session a capslock was pressed/released */
+    if (scancode == 58)
+        spice_gtk_session_capslock_changed(d->gtk_session);
+
     return true;
 }
 
@@ -1568,7 +1572,7 @@ 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);
+        spice_gtk_session_set_focus(d->gtk_session, TRUE);
     if (d->keyboard_grab_released)
         memset(d->activeseq, 0, sizeof(gboolean) * d->grabseq->nkeysyms);
     update_keyboard_focus(display, true);
@@ -1595,6 +1599,9 @@ static gboolean focus_out_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_U
     if (d->keyboard_grab_active)
         return true;
 
+    if (!d->disable_inputs)
+        spice_gtk_session_set_focus(d->gtk_session, FALSE);
+
     release_keys(display);
     update_keyboard_focus(display, false);
 
-- 
2.5.5



More information about the Spice-devel mailing list