[gtk-vnc-devel] PATCH: Track key presses & fake up events when loosing focus



The VNC protocol has no concept of focus in/out, nor modifier key states.
So if the VNC widget looses key board focus while certain keys are held
down, and then re-gains it when a different set of keys are held down it
can get very confusing state - missing key up events for the original set
of keys, and unexpected key up events for the new set of keys. This is
particularly bad if the keys are modifier keys. This is really really 
painful if one uses 'Alt+Tab' for cycling keyboard focus through local
windows because Alt+Tab get stuck on in the remote server.

This code keeps track of key up & down events, and only sends a key up event
if there was a matching key down event sent. It will also send fake up events
when loosing widget focus. I only keep track of upto 16 pressed keys, because
most of us only have 10 fingers & I can't imagine people ever pressing 16
keys all at once...

The traditional vncviewer also tracks key presses & fakes up events on leave
but does so with a 255 byte arrays of flags - one per keysym. This can keep
track of an arbitrary number of pressed keys, but 255 isn't actaully big
enough, since there's actually upto 2^16 possible keys to track. 

Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
changeset:   29:b853ff6b4c12
tag:         tip
user:        "Daniel P. Berrange <berrange redhat com>"
date:        Fri Jul 06 13:56:47 2007 -0400
summary:     Track key presses & send fake key ups when loosing focus

diff -r 233507f10a28 -r b853ff6b4c12 src/vncdisplay.c
--- a/src/vncdisplay.c	Fri Jul 06 13:42:52 2007 -0400
+++ b/src/vncdisplay.c	Fri Jul 06 13:56:47 2007 -0400
@@ -41,6 +41,8 @@ struct _VncDisplayPrivate
 
 	gboolean in_pointer_grab;
 	gboolean in_keyboard_grab;
+
+	guint down_keyval[16];
 
 	int button_mask;
 	int last_x;
@@ -363,8 +365,43 @@ static gboolean key_event(GtkWidget *wid
 	}
 
 
-	/* Send this real event */
-	gvnc_key_event(priv->gvnc, key->type == GDK_KEY_PRESS ? 1 : 0, keyval);
+	/*
+	 * More VNC suckiness with key state & modifiers in particular
+	 *
+	 * Because VNC has no concept of modifiers, we have to track what keys are
+	 * pressed and when the widget looses focus send fake key up events for all
+	 * keys current held down. This is because upon gaining focus any keys held
+	 * down are no longer likely to be down. This would thus result in keys
+	 * being 'stuck on' in the remote server. eg upon Alt-Tab to switch window
+	 * focus you'd never see key up for the Alt or Tab keys without this :-(
+	 *
+	 * This is mostly a problem with modifier heys, but its best to just track
+	 * all key presses regardless. There's a limit to how many keys a user can
+	 * press at once, so down_key_vals is only storing upto 16 for now.
+	 *
+	 * Arggggh.
+	 */
+	if (key->type == GDK_KEY_PRESS) {
+		int i;
+		for (i = 0 ; i < (int)(sizeof(priv->down_keyval)/sizeof(priv->down_keyval[0])) ; i++) {
+			if (priv->down_keyval[i] == 0) {
+				priv->down_keyval[i] = keyval;
+				/* Send the actual key event we're dealing with */
+				gvnc_key_event(priv->gvnc, 1, keyval);
+				break;
+			}
+		}
+	} else {
+		int i;
+		for (i = 0 ; i < (int)(sizeof(priv->down_keyval)/sizeof(priv->down_keyval[0])) ; i++) {
+			if (priv->down_keyval[i] == keyval) {
+				priv->down_keyval[i] = 0;
+				/* Send the actual key event we're dealing with */
+				gvnc_key_event(priv->gvnc, 0, keyval);
+				break;
+			}
+		}
+	}
 
 	/* Sticky key up processing */
 	if (key->type == GDK_KEY_RELEASE && priv->sticky_modifiers) {
@@ -399,35 +436,49 @@ static gboolean enter_event(GtkWidget *w
 static gboolean enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
                             gpointer data G_GNUC_UNUSED)
 {
-        VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
-
-        if (priv->gvnc == NULL || !gvnc_is_connected(priv->gvnc))
-                return TRUE;
-
-        if (crossing->mode != GDK_CROSSING_NORMAL)
-                return TRUE;
-
-        if (priv->grab_keyboard)
-                do_keyboard_grab(VNC_DISPLAY(widget));
-
-        return TRUE;
+	VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
+
+	if (priv->gvnc == NULL || !gvnc_is_connected(priv->gvnc))
+		return TRUE;
+
+	if (crossing->mode != GDK_CROSSING_NORMAL)
+		return TRUE;
+
+	if (priv->grab_keyboard)
+		do_keyboard_grab(VNC_DISPLAY(widget));
+
+	return TRUE;
 }
 
 static gboolean leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
                             gpointer data G_GNUC_UNUSED)
 {
-        VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
-
-        if (priv->gvnc == NULL || !gvnc_is_connected(priv->gvnc))
-                return TRUE;
-
-        if (crossing->mode != GDK_CROSSING_NORMAL)
-                return TRUE;
-
-        if (priv->grab_keyboard)
-                do_keyboard_ungrab(VNC_DISPLAY(widget));
-
-        return TRUE;
+	VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
+	int i;
+
+	if (priv->gvnc == NULL || !gvnc_is_connected(priv->gvnc))
+		return TRUE;
+
+	if (crossing->mode != GDK_CROSSING_NORMAL)
+		return TRUE;
+
+	if (priv->grab_keyboard)
+		do_keyboard_ungrab(VNC_DISPLAY(widget));
+
+	/*
+	 * Forceably release any keys currently held down, so state is 'sane'
+	 * when we re-enter the widget
+	 */
+	for (i = 0 ; i < (int)(sizeof(priv->down_keyval)/sizeof(priv->down_keyval[0])) ; i++) {
+		if (priv->down_keyval[i] != 0) {
+			/* Send the actual key event we're dealing with */
+			gvnc_key_event(priv->gvnc, 0, priv->down_keyval[i]);
+			priv->down_keyval[i] = 0;
+			break;
+		}
+	}
+
+	return TRUE;
 }
 
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]