Inconsistent modifiers patch v2

Roland Kay roland.kay "at" ox.compsoc.net
Wed, 29 Nov 2000 10:30:01 +0000


Here's a much better version of my patch which insures that the VNC
server's description of the modifier keys is always consistent with the
client's. (Thus preventing all that beeping if you run the other client
under X and and switch VTs with Ctrl+Alt+Fx).

This is still a bit of a hack, but it is now, hopefully, fairly portable
(I have clients running on Solaris and Linux).
The modifications are all to the client so there is no need to
rebuild Xvnc.

R.

----------------->8 Snip 8<-------------------

diff -uNr vnc_unixsrc.orig/vncviewer/desktop.c vnc_unixsrc/vncviewer/desktop.c
--- vnc_unixsrc.orig/vncviewer/desktop.c	Tue Aug 31 14:15:33 1999
+++ vnc_unixsrc/vncviewer/desktop.c	Sat Nov 25 11:03:24 2000
@@ -28,15 +28,26 @@
 #include <X11/extensions/XShm.h>
 #endif

+#define MAXMODS 30  /* The maximum number of modifier keys supported. */
+
 GC gc;
 GC srcGC, dstGC; /* used for debugging copyrect */
 Window desktopWin;
 Widget form, viewport, desktop;

-static Bool modifierPressed[256];
+/* Keyboard description vector. */
+static char keydescvec[32];
+/* Store pointers, masks and keysyms necessary to query the modifier states. */
+static struct {
+  char  *bytes[MAXMODS];
+  char   masks[MAXMODS];
+  KeySym syms[MAXMODS];
+  int    n;                      /* No. of entries on these arrays.          */
+} modinfo;

 static XImage *image = NULL;

+static void SyncModifiers();
 static Cursor CreateDotCursor();
 static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width,int height);
 static void HandleBasicDesktopEvent(Widget w, XtPointer ptr, XEvent *ev,
@@ -58,7 +69,9 @@
 void
 DesktopInitBeforeRealization()
 {
-  int i;
+  int i,j;
+  KeyCode kc;
+  KeySym  ks;

   form = XtVaCreateManagedWidget("form", formWidgetClass, toplevel,
 				 XtNborderWidth, 0,
@@ -75,11 +88,32 @@
   XtVaSetValues(desktop, XtNwidth, si.framebufferWidth,
 		XtNheight, si.framebufferHeight, NULL);

-  XtAddEventHandler(desktop, LeaveWindowMask|ExposureMask,
+  XtAddEventHandler(desktop, EnterWindowMask|LeaveWindowMask|ExposureMask,
 		    True, HandleBasicDesktopEvent, NULL);

-  for (i = 0; i < 256; i++)
-    modifierPressed[i] = False;
+  /* Initialise the keyboard description vector. */
+  XQueryKeymap(dpy, keydescvec);
+
+  /* Set up pointers to the modifiers. */
+  modinfo.n = 0;
+  for (i=0; i<32; i++) {
+    for (j=0; j<8; j++) {
+      /* Convert keycode to keysym. */
+      kc = 8*i + j;
+      ks = XKeycodeToKeysym(dpy, kc, 0);
+      if (IsModifierKey(ks)) {
+        modinfo.bytes[modinfo.n]  = keydescvec+i;
+	modinfo.masks[modinfo.n]  = (1<<j);
+	modinfo.syms[modinfo.n++] = ks;
+	/* Buffer overrun protection. */
+	if (modinfo.n==MAXMODS) {
+	  fprintf(stderr, "Maximum number of supported modifiers exceeded.\n");
+	  modinfo.n--;
+	}
+      }
+    }
+  }
+

   image = NULL;

@@ -145,7 +179,7 @@
 static void
 HandleBasicDesktopEvent(Widget w, XtPointer ptr, XEvent *ev, Boolean *cont)
 {
-  int i;
+  int i,j;

   switch (ev->type) {

@@ -170,13 +204,19 @@
     break;

   case LeaveNotify:
-    for (i = 0; i < 256; i++) {
-      if (modifierPressed[i]) {
-	SendKeyEvent(XKeycodeToKeysym(dpy, i, 0), False);
-	modifierPressed[i] = False;
+    /* If we the mouse leaves the window, send release events for all
+       pressed modifiers.                                             */
+    for (i=0; i<modinfo.n; i++) {
+      if (*(modinfo.bytes[i]) & modinfo.masks[i]) {
+        SendKeyEvent(modinfo.syms[i], False);
+	*(modinfo.bytes[i]) &= ~modinfo.masks[i];
       }
     }
     break;
+  case EnterNotify:
+    /* Update our description of the keyboard (including modifiers). */
+    XQueryKeymap(dpy, keydescvec);
+    break;
   }
 }

@@ -221,10 +261,12 @@
 	return;
       }
       if (strcasecmp(params[0],"keydown") == 0) {
+        SyncModifiers();
 	SendKeyEvent(ks, 1);
       } else if (strcasecmp(params[0],"keyup") == 0) {
 	SendKeyEvent(ks, 0);
       } else if (strcasecmp(params[0],"key") == 0) {
+        SyncModifiers();
 	SendKeyEvent(ks, 1);
 	SendKeyEvent(ks, 0);
       } else {
@@ -298,7 +340,13 @@

     if (IsModifierKey(ks)) {
       ks = XKeycodeToKeysym(dpy, ev->xkey.keycode, 0);
-      modifierPressed[ev->xkey.keycode] = (ev->type == KeyPress);
+      if (ev->type == KeyPress)
+        keydescvec[ev->xkey.keycode/8] |= (1<<(ev->xkey.keycode%8));
+      else
+        keydescvec[ev->xkey.keycode/8] &= ~(1<<(ev->xkey.keycode%8));
+    }
+    else if(ev->type == KeyPress) {
+     SyncModifiers();
     }

     SendKeyEvent(ks, (ev->type == KeyPress));
@@ -309,6 +357,46 @@
   }
 }

+/*
+ * SyncModifiers.
+ *
+ * Send fake key events to the server in order to ensure that its' description
+ * of the modifiers agrees with ours.
+ *
+ * Press events must only be sent once all release events have been dispatched.
+ * This is because there are two control keys (for instance) and if one is
+ * held down this must have priority over the other.
+ *
+ * If two or more clients are connected to the same display then problems
+ * can arise if one sends key press events for modifier keys but never sends
+ * the corresponding release events. This can happen, for instance, when
+ * switching virtual terminals (CTRL+ALT+Fx) under Linux. This routine is
+ * designed to prevent that happening.
+ *
+ * A better solution would be to extended the protocol to include a bitfield
+ * describing the state of the modifier keys client side. The server could then
+ * ensure that its' description of the keyboard was consistent with this.
+ *
+ *                                                   -- R.Kay (24-11-2000)
+ */
+static void SyncModifiers() {
+  int i,j;
+  KeyCode kc;
+  KeySym ks;
+
+  /* Send key release events. */
+  for (i=0; i<modinfo.n; i++) {
+    /* If this modifier was not pressed. */
+    if (!(*(modinfo.bytes[i]) & modinfo.masks[i]))
+      SendKeyEvent(modinfo.syms[i], 0);
+  }
+  /* Send key press events. */
+  for (i=0; i<modinfo.n; i++) {
+    /* If this modifier was pressed. */
+    if (*(modinfo.bytes[i]) & modinfo.masks[i])
+      SendKeyEvent(modinfo.syms[i], 1);
+  }
+}

 /*
  * CreateDotCursor.
---------------------------------------------------------------------
To unsubscribe, send a message with the line: unsubscribe vnc-list
to majordomo "at" uk.research.att.com
See also: http://www.uk.research.att.com/vnc/intouch.html
---------------------------------------------------------------------