summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathon Jongsma <jjongsma@redhat.com>2014-03-31 10:52:49 -0500
committerJonathon Jongsma <jjongsma@redhat.com>2014-04-03 16:07:25 -0500
commit9c75c7ee33d8567fef88594ae7844b9121c6291f (patch)
treec7b77e0643b428e8a9fd8bf8f30d44212d0fe128
parent31c099c931c3717543b6c6db2d1af4d68f49063f (diff)
Ensure keyboard modifiers are synchronized properly
In certain circumstances, the keyboard modifiers get out-of-sync between the guest and the client. This is easy to reproduce with the following steps: - launch virt-viewer with a guest that is not running - start the guest - while guest is booting, enable CAPS LOCK on the client - after guest finishes booting, it will set its modifiers to a default value (e.g. numlock on, capslock off) - now capslock is OFF in the guest, but ON in the client - toggle caps lock - now capslock is ON in the guest, but OFF in the client This moves the responsibility for synchronizing client and guest modifiers into SpiceGtkSession. It can't be handled easily within the SpiceDisplay widget since there can be multiple display widgets for each inputs channel. A new function (spice_gtk_session_sync_keyboard_modifiers()) was added so that synchronization can be triggered manually if desired. But it also registers a signal handler for the InputsChannel::inputs-modifiers signal to detect when the guest has changed its modifiers. The signal handler simply overrides the guests modifiers and sets them back to the value from the client.
-rw-r--r--gtk/spice-gtk-session.c97
-rw-r--r--gtk/spice-gtk-session.h1
-rw-r--r--gtk/spice-widget.c91
3 files changed, 100 insertions, 89 deletions
diff --git a/gtk/spice-gtk-session.c b/gtk/spice-gtk-session.c
index a9ce025..f0f7edf 100644
--- a/gtk/spice-gtk-session.c
+++ b/gtk/spice-gtk-session.c
@@ -17,6 +17,22 @@
*/
#include "config.h"
+#if HAVE_X11_XKBLIB_H
+#include <X11/XKBlib.h>
+#include <gdk/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_X11
+#include <X11/Xlib.h>
+#include <gdk/gdkx.h>
+#endif
+#ifdef WIN32
+#include <windows.h>
+#include <gdk/gdkwin32.h>
+#ifndef MAPVK_VK_TO_VSC /* may be undefined in older mingw-headers */
+#define MAPVK_VK_TO_VSC 0
+#endif
+#endif
+
#include <gtk/gtk.h>
#include <spice/vd_agent.h>
#include "desktop-integration.h"
@@ -97,6 +113,70 @@ enum {
PROP_AUTO_USBREDIR,
};
+static guint32 get_keyboard_lock_modifiers(void)
+{
+ guint32 modifiers = 0;
+#if HAVE_X11_XKBLIB_H
+ Display *x_display = NULL;
+ XKeyboardState keyboard_state;
+
+ GdkScreen *screen = gdk_screen_get_default();
+ if (!GDK_IS_X11_DISPLAY(gdk_screen_get_display(screen))) {
+ SPICE_DEBUG("FIXME: gtk backend is not X11");
+ return 0;
+ }
+
+ x_display = GDK_SCREEN_XDISPLAY(screen);
+ XGetKeyboardControl(x_display, &keyboard_state);
+
+ if (keyboard_state.led_mask & 0x01) {
+ modifiers |= SPICE_INPUTS_CAPS_LOCK;
+ }
+ if (keyboard_state.led_mask & 0x02) {
+ modifiers |= SPICE_INPUTS_NUM_LOCK;
+ }
+ if (keyboard_state.led_mask & 0x04) {
+ modifiers |= SPICE_INPUTS_SCROLL_LOCK;
+ }
+#elif defined(win32)
+ if (GetKeyState(VK_CAPITAL) & 1) {
+ modifiers |= SPICE_INPUTS_CAPS_LOCK;
+ }
+ if (GetKeyState(VK_NUMLOCK) & 1) {
+ modifiers |= SPICE_INPUTS_NUM_LOCK;
+ }
+ if (GetKeyState(VK_SCROLL) & 1) {
+ modifiers |= SPICE_INPUTS_SCROLL_LOCK;
+ }
+#else
+ g_warning("get_keyboard_lock_modifiers not implemented");
+#endif // HAVE_X11_XKBLIB_H
+ return modifiers;
+}
+
+static void spice_gtk_session_sync_keyboard_modifiers_for_channel(SpiceGtkSession *self,
+ SpiceInputsChannel* inputs)
+{
+ gint guest_modifiers = 0, client_modifiers = 0;
+
+ g_return_if_fail(SPICE_IS_INPUTS_CHANNEL(inputs));
+
+ g_object_get(inputs, "key-modifiers", &guest_modifiers, NULL);
+
+ client_modifiers = get_keyboard_lock_modifiers();
+ g_debug("%s: input:%p client_modifiers:0x%x, guest_modifiers:0x%x",
+ G_STRFUNC, inputs, client_modifiers, guest_modifiers);
+
+ if (client_modifiers != guest_modifiers)
+ spice_inputs_set_key_locks(inputs, client_modifiers);
+}
+
+static void guest_modifiers_changed(SpiceInputsChannel *inputs, gpointer data)
+{
+ SpiceGtkSession *self = data;
+ spice_gtk_session_sync_keyboard_modifiers_for_channel(self, inputs);
+}
+
static void spice_gtk_session_init(SpiceGtkSession *self)
{
SpiceGtkSessionPrivate *s;
@@ -872,6 +952,11 @@ static void channel_new(SpiceSession *session, SpiceChannel *channel,
g_signal_connect(channel, "main-clipboard-selection-release",
G_CALLBACK(clipboard_release), self);
}
+ if (SPICE_IS_INPUTS_CHANNEL(channel)) {
+ spice_g_signal_connect_object(channel, "inputs-modifiers",
+ G_CALLBACK(guest_modifiers_changed), self, 0);
+ spice_gtk_session_sync_keyboard_modifiers_for_channel(self, SPICE_INPUTS_CHANNEL(channel));
+ }
}
static void channel_destroy(SpiceSession *session, SpiceChannel *channel,
@@ -1029,3 +1114,15 @@ void spice_gtk_session_paste_from_guest(SpiceGtkSession *self)
s->clipboard_by_guest[selection] = TRUE;
s->clip_hasdata[selection] = FALSE;
}
+
+void spice_gtk_session_sync_keyboard_modifiers(SpiceGtkSession *self)
+{
+ GList *l = NULL, *channels = spice_session_get_channels(self->priv->session);
+
+ for (l = channels; l != NULL; l = l->next) {
+ if (SPICE_IS_INPUTS_CHANNEL(l->data)) {
+ SpiceInputsChannel *inputs = SPICE_INPUTS_CHANNEL(l->data);
+ spice_gtk_session_sync_keyboard_modifiers_for_channel(self, inputs);
+ }
+ }
+}
diff --git a/gtk/spice-gtk-session.h b/gtk/spice-gtk-session.h
index 3b4eac6..fbcc353 100644
--- a/gtk/spice-gtk-session.h
+++ b/gtk/spice-gtk-session.h
@@ -59,6 +59,7 @@ GType spice_gtk_session_get_type(void);
SpiceGtkSession *spice_gtk_session_get(SpiceSession *session);
void spice_gtk_session_copy_to_guest(SpiceGtkSession *self);
void spice_gtk_session_paste_from_guest(SpiceGtkSession *self);
+void spice_gtk_session_sync_keyboard_modifiers(SpiceGtkSession *self);
G_END_DECLS
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 0e4a0de..2044513 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -131,7 +131,6 @@ static void try_mouse_ungrab(SpiceDisplay *display);
static void recalc_geometry(GtkWidget *widget);
static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data);
static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data);
-static void sync_keyboard_lock_modifiers(SpiceDisplay *display);
static void cursor_invalidate(SpiceDisplay *display);
static void update_area(SpiceDisplay *display, gint x, gint y, gint width, gint height);
static void release_keys(SpiceDisplay *display);
@@ -1457,7 +1456,8 @@ static gboolean focus_in_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UN
return true;
release_keys(display);
- sync_keyboard_lock_modifiers(display);
+ if (!d->disable_inputs)
+ spice_gtk_session_sync_keyboard_modifiers(d->gtk_session);
update_keyboard_focus(display, true);
try_keyboard_grab(display);
update_display(display);
@@ -2411,7 +2411,6 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
if (SPICE_IS_INPUTS_CHANNEL(channel)) {
d->inputs = SPICE_INPUTS_CHANNEL(channel);
spice_channel_connect(channel);
- sync_keyboard_lock_modifiers(display);
return;
}
@@ -2600,89 +2599,3 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
return pixbuf;
}
-#if HAVE_X11_XKBLIB_H
-static guint32 get_keyboard_lock_modifiers(Display *x_display)
-{
- XKeyboardState keyboard_state;
- guint32 modifiers = 0;
-
- XGetKeyboardControl(x_display, &keyboard_state);
-
- if (keyboard_state.led_mask & 0x01) {
- modifiers |= SPICE_INPUTS_CAPS_LOCK;
- }
- if (keyboard_state.led_mask & 0x02) {
- modifiers |= SPICE_INPUTS_NUM_LOCK;
- }
- if (keyboard_state.led_mask & 0x04) {
- modifiers |= SPICE_INPUTS_SCROLL_LOCK;
- }
- return modifiers;
-}
-
-static void sync_keyboard_lock_modifiers(SpiceDisplay *display)
-{
- Display *x_display;
- SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
- guint32 modifiers;
- GdkWindow *w;
-
- if (d->disable_inputs)
- return;
-
- w = gtk_widget_get_parent_window(GTK_WIDGET(display));
- if (w == NULL) /* it can happen if the display is not yet shown */
- return;
-
- if (!GDK_IS_X11_DISPLAY(gdk_window_get_display(w))) {
- SPICE_DEBUG("FIXME: gtk backend is not X11");
- return;
- }
-
- x_display = GDK_WINDOW_XDISPLAY(w);
- modifiers = get_keyboard_lock_modifiers(x_display);
- if (d->inputs)
- spice_inputs_set_key_locks(d->inputs, modifiers);
-}
-
-#elif defined (WIN32)
-static guint32 get_keyboard_lock_modifiers(void)
-{
- guint32 modifiers = 0;
-
- if (GetKeyState(VK_CAPITAL) & 1) {
- modifiers |= SPICE_INPUTS_CAPS_LOCK;
- }
- if (GetKeyState(VK_NUMLOCK) & 1) {
- modifiers |= SPICE_INPUTS_NUM_LOCK;
- }
- if (GetKeyState(VK_SCROLL) & 1) {
- modifiers |= SPICE_INPUTS_SCROLL_LOCK;
- }
-
- return modifiers;
-}
-
-static void sync_keyboard_lock_modifiers(SpiceDisplay *display)
-{
- SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
- guint32 modifiers;
- GdkWindow *w;
-
- if (d->disable_inputs)
- return;
-
- w = gtk_widget_get_parent_window(GTK_WIDGET(display));
- if (w == NULL) /* it can happen if the display is not yet shown */
- return;
-
- modifiers = get_keyboard_lock_modifiers();
- if (d->inputs)
- spice_inputs_set_key_locks(d->inputs, modifiers);
-}
-#else
-static void sync_keyboard_lock_modifiers(SpiceDisplay *display)
-{
- g_warning("sync_keyboard_lock_modifiers not implemented");
-}
-#endif // HAVE_X11_XKBLIB_H