[gtk-vnc-devel] PATCH: fix keycode -> keysym conversions



Since the VNC protocol only concerns itself with key up & down events, there
is no way to provide modifier key state to the remote server. For this reason
when converting from keycodes to keysyms prior to transmission, the client
must *not* apply modifier state. The exception is that the shift key should
be applied. The current code just sends the  keysym that GTK converted to
the server. The result is that pressing 'Alt' results in the server getting
a Alt Down, followed by  ISO_Prev_Group Up event - and so the Alt key is
forever 'stuck' down now.

This patch re-runs the keycode -> keysym conversion, masking out all the
modifier keys except for Shift. This ensures matching key down & up events
get sent to the server, which then applies the modifier mapping itself.

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:   28:233507f10a28
user:        "Daniel P. Berrange <berrange redhat com>"
date:        Fri Jul 06 13:42:52 2007 -0400
summary:     Fix keycode -> keysym mapping wrt to modifiers

diff -r 3c2eb84af9ae -r 233507f10a28 src/vncdisplay.c
--- a/src/vncdisplay.c	Fri Jul 06 13:39:41 2007 -0400
+++ b/src/vncdisplay.c	Fri Jul 06 13:42:52 2007 -0400
@@ -310,22 +310,49 @@ static gboolean key_event(GtkWidget *wid
 			  gpointer data G_GNUC_UNUSED)
 {
 	VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
-	int down;
+	guint keyval;
+	gint group, level;
+	GdkModifierType consumed;
 
 	if (priv->gvnc == NULL || !gvnc_is_connected(priv->gvnc))
 		return TRUE;
+
+	/*
+	 * Key handling in VNC is screwy. The event.keyval from GTK is
+	 * interpreted relative to modifier state. This really messes
+	 * up with VNC which has no concept of modifiers - it just sees
+	 * key up *& down events - the remote end interprets modifiers
+	 * itself. So if we interpret at the client end you can end up
+	 * with 'Alt' key press generating Alt_L, and key release generating
+	 * ISO_Prev_Group. This really really confuses the VNC server
+	 * with 'Alt' getting stuck on.
+	 *
+	 * So we have to redo GTK's  keycode -> keyval translation
+	 * using only the SHIFT modifier which the RFB explicitly
+	 * requires to be interpreted at client end.
+	 *
+	 * Arggggh.
+	 */
+	gdk_keymap_translate_keyboard_state(gdk_keymap_get_default(),
+					    key->hardware_keycode,
+					    key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK),
+					    key->group,
+					    &keyval,
+					    &group,
+					    &level,
+					    &consumed);
 
 	/* Sticky key down processing */
 	if (key->type == GDK_KEY_PRESS && priv->sticky_modifiers) {
-		if (key->keyval == GDK_Control_L ||
-		    key->keyval == GDK_Control_R ||
-		    key->keyval == GDK_Alt_L ||
-		    key->keyval == GDK_Alt_R) {
+		if (keyval == GDK_Control_L ||
+		    keyval == GDK_Control_R ||
+		    keyval == GDK_Alt_L ||
+		    keyval == GDK_Alt_R) {
 			/* Got a modifier, start counting presses */
-			if (priv->last_keyval == key->keyval) {
+			if (priv->last_keyval == keyval) {
 				priv->last_repeat++;
 			} else {
-				priv->last_keyval = key->keyval;
+				priv->last_keyval = keyval;
 				priv->last_repeat = 1;
 			}
 		} else if (priv->last_keyval != 0 && priv->last_repeat >= 3) {
@@ -336,21 +363,16 @@ static gboolean key_event(GtkWidget *wid
 	}
 
 
-	if (key->type == GDK_KEY_PRESS)
-		down = 1;
-	else
-		down = 0;
-
 	/* Send this real event */
-	gvnc_key_event(priv->gvnc, down, key->keyval);
+	gvnc_key_event(priv->gvnc, key->type == GDK_KEY_PRESS ? 1 : 0, keyval);
 
 	/* Sticky key up processing */
 	if (key->type == GDK_KEY_RELEASE && priv->sticky_modifiers) {
 		/* If non-modifier need some sticky key processing */
-		if (key->keyval != GDK_Control_L &&
-		    key->keyval != GDK_Control_R &&
-		    key->keyval != GDK_Alt_L &&
-		    key->keyval != GDK_Alt_R) {
+		if (keyval != GDK_Control_L &&
+		    keyval != GDK_Control_R &&
+		    keyval != GDK_Alt_L &&
+		    keyval != GDK_Alt_R) {
 			/* Have a sticky key, might need a fake key up */
 			if (priv->last_keyval != 0 &&
 			    priv->last_repeat >= 3)
@@ -363,8 +385,8 @@ static gboolean key_event(GtkWidget *wid
 
 	/* Release pointer grab with Ctrl+Alt */
 	if (key->type == GDK_KEY_PRESS &&
-	    ((key->keyval == GDK_Control_L && (key->state & GDK_MOD1_MASK)) ||
-	     (key->keyval == GDK_Alt_L && (key->state & GDK_CONTROL_MASK)))) {
+	    ((keyval == GDK_Control_L && (key->state & GDK_MOD1_MASK)) ||
+	     (keyval == GDK_Alt_L && (key->state & GDK_CONTROL_MASK)))) {
 		if (priv->in_pointer_grab)
 			do_pointer_ungrab(VNC_DISPLAY(widget));
 		else



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