Blob Blame History Raw
diff -up tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Input.cc.input tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Input.cc
--- tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Input.cc.input	2012-08-29 09:56:37.000000000 +0100
+++ tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Input.cc	2014-01-17 16:01:13.140109450 +0000
@@ -1,5 +1,6 @@
 /* Copyright (C) 2009 TightVNC Team
  * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright 2013 Pierre Ossman for Cendio AB
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -34,16 +35,6 @@ extern "C" {
 #include "inpututils.h"
 #endif
 #include "mi.h"
-#ifndef XKB_IN_SERVER
-#define XKB_IN_SERVER
-#endif
-#ifdef XKB
-/*
- * This include is needed to use XkbConvertCase instead of XConvertCase even if
- * we don't use XKB extension.
- */
-#include <xkbsrv.h>
-#endif
 #if XORG >= 16
 #include "exevents.h"
 #endif
@@ -58,6 +49,7 @@ CopyKeyClass(DeviceIntPtr device, Device
 extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey;
 #endif
 #include <X11/keysym.h>
+#include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #undef public
 #undef class
@@ -72,12 +64,9 @@ using namespace rfb;
 
 static LogWriter vlog("Input");
 
-#define BUTTONS 7
-static int pointerProc(DeviceIntPtr pDevice, int onoff);
+rfb::BoolParameter avoidShiftNumLock("AvoidShiftNumLock", "Avoid fake Shift presses for keys affected by NumLock.", true);
 
-static int keyboardProc(DeviceIntPtr pDevice, int onoff);
-static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col);
-static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col);
+#define BUTTONS 7
 
 /* Event queue is shared between all devices. */
 #if XORG == 15
@@ -128,26 +117,16 @@ static void enqueueEvents(DeviceIntPtr d
 #endif /* XORG < 111 */
 
 InputDevice::InputDevice(rfb::VNCServerST *_server)
-	: server(_server), oldButtonMask(0)
+	: server(_server), initialized(false), oldButtonMask(0)
 {
-#if XORG < 17
-	pointerDev = AddInputDevice(
-#if XORG >= 16
-				    serverClient,
-#endif
-				    pointerProc, TRUE);
-	RegisterPointerDevice(pointerDev);
+	int i;
 
-	keyboardDev = AddInputDevice(
-#if XORG >= 16
-				     serverClient,
-#endif
-				     keyboardProc, TRUE);
-	RegisterKeyboardDevice(keyboardDev);
-#endif
 #if XORG < 111
 	initEventq();
 #endif
+
+	for (i = 0;i < 256;i++)
+		pressedKeys[i] = NoSymbol;
 }
 
 void InputDevice::PointerButtonAction(int buttonMask)
@@ -160,8 +139,6 @@ void InputDevice::PointerButtonAction(in
 	ValuatorMask mask;
 #endif
 
-	initInputDevice();
-
 	for (i = 0; i < BUTTONS; i++) {
 		if ((buttonMask ^ oldButtonMask) & (1 << i)) {
 			int action = (buttonMask & (1<<i)) ?
@@ -199,8 +176,6 @@ void InputDevice::PointerMove(const rfb:
 	if (pos.equals(cursorPos))
 		return;
 
-	initInputDevice();
-
 	valuators[0] = pos.x;
 	valuators[1] = pos.y;
 #if XORG < 110
@@ -287,16 +262,78 @@ static int pointerProc(DeviceIntPtr pDev
 	return Success;
 }
 
-void InputDevice::initInputDevice(void)
+static void keyboardBell(int percent, DeviceIntPtr device, pointer ctrl,
+			 int class_)
 {
+	if (percent > 0)
+		vncBell();
+}
+
+extern void GetInitKeyboardMap(KeySymsPtr keysyms, CARD8 *modmap);
+
+static int keyboardProc(DeviceIntPtr pDevice, int onoff)
+{
+#if XORG < 17
+	KeySymsRec keySyms;
+	CARD8 modMap[MAP_LENGTH];
+#endif
+	DevicePtr pDev = (DevicePtr)pDevice;
+
+	switch (onoff) {
+	case DEVICE_INIT:
+#if XORG < 17
+		GetInitKeyboardMap(&keySyms, modMap);
+#endif
+		InitKeyboardDeviceStruct(
 #if XORG >= 17
-	int ret;
-	static int initialized = 0;
+					 pDevice, NULL,
+#else
+					 pDev, &keySyms, modMap,
+#endif
+					 keyboardBell, (KbdCtrlProcPtr)NoopDDA);
+		break;
+	case DEVICE_ON:
+		pDev->on = TRUE;
+		break;
+	case DEVICE_OFF:
+		pDev->on = FALSE;
+		break;
+	}
 
-	if (initialized != 0)
+	return Success;
+}
+
+void InputDevice::InitInputDevice(void)
+{
+	if (initialized)
 		return;
 
-	initialized = 1;
+	initialized = true;
+
+#if XORG < 17
+	pointerDev = AddInputDevice(
+#if XORG >= 16
+				    serverClient,
+#endif
+				    pointerProc, TRUE);
+	RegisterPointerDevice(pointerDev);
+
+	keyboardDev = AddInputDevice(
+#if XORG >= 16
+				     serverClient,
+#endif
+				     keyboardProc, TRUE);
+	RegisterKeyboardDevice(keyboardDev);
+
+	if (ActivateDevice(pointerDev) != Success ||
+	    ActivateDevice(keyboardDev) != Success)
+		FatalError("Failed to activate TigerVNC devices\n");
+
+	if (!EnableDevice(pointerDev) ||
+	    !EnableDevice(keyboardDev))
+		FatalError("Failed to enable TigerVNC devices\n");
+#else /* < 17 */
+	int ret;
 
 	ret = AllocDevicePair(serverClient, "TigerVNC", &pointerDev,
 			      &keyboardDev, pointerProc, keyboardProc,
@@ -312,7 +349,9 @@ void InputDevice::initInputDevice(void)
 	if (!EnableDevice(pointerDev, TRUE) ||
 	    !EnableDevice(keyboardDev, TRUE))
 		FatalError("Failed to activate TigerVNC devices\n");
-#endif
+#endif /* 17 */
+
+	PrepareInputDevices();
 }
 
 static inline void pressKey(DeviceIntPtr dev, int kc, bool down, const char *msg)
@@ -334,143 +373,6 @@ static inline void pressKey(DeviceIntPtr
 #endif
 }
 
-#define IS_PRESSED(keyc, keycode) \
-	((keyc)->down[(keycode) >> 3] & (1 << ((keycode) & 7)))
-
-/*
- * ModifierState is a class which helps simplify generating a "fake" press or
- * release of shift, ctrl, alt, etc.  An instance of the class is created for
- * every modifier which may need to be pressed or released.  Then either
- * press() or release() may be called to make sure that the corresponding keys
- * are in the right state.  The destructor of the class automatically reverts
- * to the previous state.  Each modifier may have multiple keys associated with
- * it, so in the case of a fake release, this may involve releasing more than
- * one key.
- */
-
-class ModifierState {
-public:
-	ModifierState(DeviceIntPtr _dev, int _modIndex)
-		: modIndex(_modIndex), nKeys(0), keys(0), pressed(false),
-		  dev(_dev) {}
-
-	~ModifierState()
-	{
-		for (int i = 0; i < nKeys; i++)
-			pressKey(dev, keys[i], !pressed, "fake keycode");
-		delete [] keys;
-	}
-
-	void press()
-	{
-		int state, maxKeysPerMod, keycode;
-#if XORG >= 17
-		KeyCode *modmap = NULL;
-#if XORG >= 111
-		state = XkbStateFieldFromRec(&dev->master->key->xkbInfo->state);
-#else /* XORG >= 111 */
-		state = XkbStateFieldFromRec(&dev->u.master->key->xkbInfo->state);
-#endif /* XORG >= 111 */
-#else
-		KeyClassPtr keyc = dev->key;
-		state = keyc->state;
-#endif
-		if ((state & (1 << modIndex)) != 0)
-			return;
-
-#if XORG >= 17
-		if (generate_modkeymap(serverClient, dev, &modmap,
-				       &maxKeysPerMod) != Success) {
-			vlog.error("generate_modkeymap failed");
-			return;
-		}
-
-		if (maxKeysPerMod == 0) {
-			vlog.debug("Keyboard has no modifiers");
-			xfree(modmap);
-			return;
-		}
-
-		keycode = modmap[modIndex * maxKeysPerMod];
-		xfree(modmap);
-#else
-		maxKeysPerMod = keyc->maxKeysPerModifier;
-		keycode = keyc->modifierKeyMap[modIndex * maxKeysPerMod];
-#endif
-		tempKeyEvent(keycode, true, maxKeysPerMod);
-		pressed = true;
-	}
-
-	void release()
-	{
-		int state, maxKeysPerMod;
-		KeyClassPtr keyc;
-#if XORG >= 17
-		KeyCode *modmap = NULL;
-
-#if XORG >= 111
-		keyc = dev->master->key;
-#else /* XORG >= 111 */
-		keyc = dev->u.master->key;
-#endif /* XORG >= 111 */
-		state = XkbStateFieldFromRec(&keyc->xkbInfo->state);
-#else
-		keyc = dev->key;
-		state = keyc->state;
-#endif
-		if ((state & (1 << modIndex)) == 0)
-			return;
-
-#if XORG >= 17
-		if (generate_modkeymap(serverClient, dev, &modmap,
-				       &maxKeysPerMod) != Success) {
-			vlog.error("generate_modkeymap failed");
-			return;
-		}
-
-		if (maxKeysPerMod == 0) {
-			vlog.debug("Keyboard has no modifiers");
-			xfree(modmap);
-			return;
-		}
-#else
-		maxKeysPerMod = keyc->maxKeysPerModifier;
-#endif
-
-		for (int k = 0; k < maxKeysPerMod; k++) {
-			int keycode;
-			int index = modIndex * maxKeysPerMod + k;
-#if XORG >= 17
-			keycode = modmap[index];
-#else
-			keycode = keyc->modifierKeyMap[index];
-#endif
-			if (keycode && IS_PRESSED(keyc, keycode))
-				tempKeyEvent(keycode, false, maxKeysPerMod);
-		}
-#if XORG >= 17
-		xfree(modmap);
-#endif
-	}
-
-private:
-	void tempKeyEvent(int keycode, bool down, int maxKeysPerMod)
-	{
-		if (keycode) {
-			if (!keys) keys = new int[maxKeysPerMod];
-			keys[nKeys++] = keycode;
-			pressKey(dev, keycode, down, "fake keycode");
-		}
-	}
-
-	int modIndex;
-	int nKeys;
-	int *keys;
-	bool pressed;
-	DeviceIntPtr dev;
-};
-
-
 /* altKeysym is a table of alternative keysyms which have the same meaning. */
 
 static struct altKeysym_t {
@@ -517,102 +419,48 @@ static struct altKeysym_t {
 	{ XK_KP_7,		XK_7 },
 	{ XK_KP_8,		XK_8 },
 	{ XK_KP_9,		XK_9 },
+	{ XK_ISO_Level3_Shift,	XK_Mode_switch },
 };
 
 /*
  * keyEvent() - work out the best keycode corresponding to the keysym sent by
- * the viewer.  This is non-trivial because we can't assume much about the
- * local keyboard layout.  We must also find out which column of the keyboard
- * mapping the keysym is in, and alter the shift state appropriately.  Column 0
- * means both shift and "mode_switch" (AltGr) must be released, column 1 means
- * shift must be pressed and mode_switch released, column 2 means shift must be
- * released and mode_switch pressed, and column 3 means both shift and
- * mode_switch must be pressed.
- *
- * Magic, which dynamically adds keysym<->keycode mapping depends on X.Org
- * version. Quick explanation of that "magic":
- * 
- * 1.5
- * - has only one core keyboard so we have to keep core keyboard mapping
- *   synchronized with vncKeyboardDevice. Do it via SwitchCoreKeyboard()
- *
- * 1.6 (aka MPX - Multi pointer X)
- * - multiple master devices (= core devices) exists, keep vncKeyboardDevice
- *   synchronized with proper master device
- */
-
-#if XORG >= 17
-#define FREE_MAPS \
-	do { \
-	        xfree(modmap); \
-	        xfree(keymap->map); \
-	        xfree(keymap); \
-	} while (0);
-#else
-#define FREE_MAPS
-#endif
-
-#if XORG >= 17
-/*
- * Modifier keysyms must be handled differently. Instead of finding
- * the right row and collumn in the keymap, directly press/release
- * the keycode which is mapped as modifier with the same keysym.
- *
- * This will avoid issues when there are multiple modifier keysyms
- * in the keymap but only some of them are mapped as modifiers in
- * the modmap.
- *
- * Returns keycode of the modifier key.
+ * the viewer. This is basically impossible in the general case, but we make
+ * a best effort by assuming that all useful keysyms can be reached using
+ * just the Shift and Level 3 (AltGr) modifiers. For core keyboards this is
+ * basically always true, and should be true for most sane, western XKB
+ * layouts.
  */
-
-static inline int isModifier(KeySymsPtr keymap, KeyCode *modmap,
-			      int maxKeysPerMod, rdr::U32 keysym)
+void InputDevice::keyEvent(rdr::U32 keysym, bool down)
 {
-	KeySym *map = keymap->map;
-	KeyCode minKeyCode = keymap->minKeyCode;
-	int mapWidth = keymap->mapWidth;
-	int i, j, k;
-
-	/* Find modifier index in the modmap */
-	for (i = 0; i < 8; i++) {
-		for (k = 0; k < maxKeysPerMod; k++) {
-			int index = i * maxKeysPerMod + k;
-			int keycode = modmap[index];
+	int i;
+	unsigned state, new_state;
+	KeyCode keycode;
 
-			if (keycode == 0)
-				continue;
+	unsigned level_three_mask;
+	KeyCode shift_press, level_three_press;
+	std::list<KeyCode> shift_release, level_three_release;
 
-			for (j = 0; j < mapWidth; j++) {
-				if (map[(keycode - minKeyCode) * mapWidth + j]
-				    == keysym) {
-					return keycode;
-				}
+	/*
+	 * Release events must match the press event, so look up what
+	 * keycode we sent for the press.
+	 */
+	if (!down) {
+		for (i = 0;i < 256;i++) {
+			if (pressedKeys[i] == keysym) {
+				pressedKeys[i] = NoSymbol;
+				pressKey(keyboardDev, i, false, "keycode");
+				mieqProcessInputEvents();
+				return;
 			}
 		}
-	}
-
-	return -1; /* Not a modifier */
-}
-#endif
 
-void InputDevice::keyEvent(rdr::U32 keysym, bool down)
-{
-#if XORG < 17
-	DeviceIntPtr master;
-#endif
-	KeyClassPtr keyc;
-	KeySymsPtr keymap = NULL;
-	KeySym *map = NULL;
-	KeyCode minKeyCode, maxKeyCode;
-	KeyCode *modmap = NULL;
-	int mapWidth;
-	unsigned int i;
-	int j, k, state, maxKeysPerMod;
-#if XORG >= 17
-	KeybdCtrl ctrl;
-#endif
-
-	initInputDevice();
+		/*
+		 * This can happen quite often as we ignore some
+		 * key presses.
+		 */
+		vlog.debug("Unexpected release of keysym 0x%x", keysym);
+		return;
+	}
 
 	/* 
 	 * Since we are checking the current state to determine if we need
@@ -622,543 +470,214 @@ void InputDevice::keyEvent(rdr::U32 keys
 	 */ 
 	mieqProcessInputEvents();
 
-	if (keysym == XK_Caps_Lock) {
-		vlog.debug("Ignoring caps lock");
-		return;
-	}
+	state = getKeyboardState();
 
-#if XORG >= 17
-#if XORG >= 111
-	keyc = keyboardDev->master->key;
-#else /* XORG >= 111 */
-	keyc = keyboardDev->u.master->key;
-#endif /* XORG >= 111 */
-
-	keymap = XkbGetCoreMap(keyboardDev);
-	if (!keymap) {
-		vlog.error("VNC keyboard device has no map");
-		return;
-	}
+	keycode = keysymToKeycode(keysym, state, &new_state);
 
-	if (generate_modkeymap(serverClient, keyboardDev, &modmap,
-	    		       &maxKeysPerMod) != Success) {
-		vlog.error("generate_modkeymap failed");
-		xfree(keymap->map);
-		xfree(keymap);
-		return;
-	}
-
-	if (maxKeysPerMod == 0)
-		vlog.debug("Keyboard has no modifiers");
-
-	state = XkbStateFieldFromRec(&keyc->xkbInfo->state);
-#else
-	keyc = keyboardDev->key;
-	state = keyc->state;
-	maxKeysPerMod = keyc->maxKeysPerModifier;
-	keymap = &keyc->curKeySyms;
-	modmap = keyc->modifierKeyMap;
-#endif
-	map = keymap->map;
-	minKeyCode = keymap->minKeyCode;
-	maxKeyCode = keymap->maxKeyCode;
-	mapWidth = keymap->mapWidth;
-
-#if XORG >= 17
-	/*
-	 * No server-side key repeating, please. Some clients won't work well,
-	 * check https://bugzilla.redhat.com/show_bug.cgi?id=607866.
-	 */
-	ctrl = keyboardDev->kbdfeed->ctrl;
-	if (ctrl.autoRepeat != FALSE) {
-		ctrl.autoRepeat = FALSE;
-		XkbSetRepeatKeys(keyboardDev, -1, ctrl.autoRepeat);
-	}
-#endif
-
-	/* find which modifier Mode_switch is on. */
-	int modeSwitchMapIndex = 0;
-	for (i = 3; i < 8; i++) {
-		for (k = 0; k < maxKeysPerMod; k++) {
-			int index = i * maxKeysPerMod + k;
-			int keycode = modmap[index];
-
-			if (keycode == 0)
+	/* Try some equivalent keysyms if we couldn't find a perfect match */
+	if (keycode == 0) {
+		for (i = 0;i < sizeof(altKeysym)/sizeof(altKeysym[0]);i++) {
+			KeySym altsym;
+
+			if (altKeysym[i].a == keysym)
+				altsym = altKeysym[i].b;
+			else if (altKeysym[i].b == keysym)
+				altsym = altKeysym[i].a;
+			else
 				continue;
 
-			for (j = 0; j < mapWidth; j++) {
-				if (map[(keycode - minKeyCode) * mapWidth + j]
-				    == XK_Mode_switch) {
-					modeSwitchMapIndex = i;
-					goto ModeSwitchFound;
-				}
-			}
+			keycode = keysymToKeycode(altsym, state, &new_state);
+			if (keycode != 0)
+				break;
 		}
 	}
-ModeSwitchFound:
 
-	int kc;
-	int col = 0;
+	/* We don't have lock synchronisation... */
+	if (isLockModifier(keycode, new_state)) {
+		vlog.debug("Ignoring lock key (e.g. caps lock)");
+		return;
+	}
 
-#if XORG >= 17
-	if ((kc = isModifier(keymap, modmap, maxKeysPerMod, keysym)) != -1) {
-		/*
-		 * It is a modifier key event.
-		 *
-		 * Don't do any auto-repeat because the X server will translate
-		 * each press into a release followed by a press.
-		 */
-		if (IS_PRESSED(keyc, kc) && down) {
-			FREE_MAPS;
+	/* No matches. Will have to add a new entry... */
+	if (keycode == 0) {
+		keycode = addKeysym(keysym, state);
+		if (keycode == 0) {
+			vlog.error("Failure adding new keysym 0x%x", keysym);
 			return;
 		}
 
-		goto press;
-	}
-#endif
-
-	if (maxKeysPerMod != 0) {
-		if ((state & (1 << ShiftMapIndex)) != 0)
-			col |= 1;
-		if (modeSwitchMapIndex != 0 &&
-		    ((state & (1 << modeSwitchMapIndex))) != 0)
-			col |= 2;
-	}
-
-	kc = KeysymToKeycode(keymap, keysym, &col);
+		vlog.info("Added unknown keysym 0x%x to keycode %d",
+			  keysym, keycode);
 
-	/*
-	 * Sort out the "shifted Tab" mess.  If we are sent a shifted Tab,
-	 * generate a local shifted Tab regardless of what the "shifted Tab"
-	 * keysym is on the local keyboard (it might be Tab, ISO_Left_Tab or
-	 * HP's private BackTab keysym, and quite possibly some others too).
-	 * We never get ISO_Left_Tab here because it's already been translated
-	 * in VNCSConnectionST.
-	 */
-	if (maxKeysPerMod != 0 && keysym == XK_Tab &&
-	    ((state & (1 << ShiftMapIndex))) != 0)
-		col |= 1;
-
-	if (kc == 0) {
 		/*
-		 * Not a direct match in the local keyboard mapping.  Check for
-		 * alternative keysyms with the same meaning.
+		 * The state given to addKeysym() is just a hint and
+		 * the actual result might still require some state
+		 * changes.
 		 */
-		for (i = 0; i < sizeof(altKeysym) / sizeof(altKeysym_t); i++) {
-			if (keysym == altKeysym[i].a)
-				kc = KeysymToKeycode(keymap, altKeysym[i].b,
-						     &col);
-			else if (keysym == altKeysym[i].b)
-				kc = KeysymToKeycode(keymap, altKeysym[i].a,
-						     &col);
-			if (kc)
-				break;
+		keycode = keysymToKeycode(keysym, state, &new_state);
+		if (keycode == 0) {
+			vlog.error("Newly added keysym 0x%x cannot be generated", keysym);
+			return;
 		}
 	}
 
-	if (kc == 0) {
-		/* Dynamically add a new key to the keyboard mapping. */
-		for (kc = maxKeyCode; kc >= minKeyCode; kc--) {
-			if (map[(kc - minKeyCode) * mapWidth] != 0)
+	/*
+	 * X11 generally lets shift toggle the keys on the numeric pad
+	 * the same way NumLock does. This is however not the case on
+	 * other systems like Windows. As a result, some applications
+	 * get confused when we do a fake shift to get the same effect
+	 * that having NumLock active would produce.
+	 *
+	 * Until we have proper NumLock synchronisation (so we can
+	 * avoid faking shift), we try to avoid the fake shifts if we
+	 * can use an alternative keysym.
+	 */
+	if (((state & ShiftMask) != (new_state & ShiftMask)) &&
+	    avoidShiftNumLock && isAffectedByNumLock(keycode)) {
+	    	KeyCode keycode2;
+	    	unsigned new_state2;
+
+		vlog.debug("Finding alternative to keysym 0x%x to avoid fake shift for numpad", keysym);
+
+		for (i = 0;i < sizeof(altKeysym)/sizeof(altKeysym[0]);i++) {
+			KeySym altsym;
+
+			if (altKeysym[i].a == keysym)
+				altsym = altKeysym[i].b;
+			else if (altKeysym[i].b == keysym)
+				altsym = altKeysym[i].a;
+			else
 				continue;
 
-			map[(kc - minKeyCode) * mapWidth] = keysym;
-			col = 0;
+			keycode2 = keysymToKeycode(altsym, state, &new_state2);
+			if (keycode2 == 0)
+				continue;
 
-			vlog.info("Added unknown keysym 0x%x to keycode %d",
-				  keysym, kc);
+			if (((state & ShiftMask) != (new_state2 & ShiftMask)) &&
+			    isAffectedByNumLock(keycode2))
+				continue;
 
-#if XORG < 17
-#if XORG == 15
-			master = inputInfo.keyboard;
-#else
-			master = keyboardDev->u.master;
-#endif
-			void *slave = dixLookupPrivate(&master->devPrivates,
-						       CoreDevicePrivateKey);
-			if (keyboardDev == slave) {
-				dixSetPrivate(&master->devPrivates,
-					      CoreDevicePrivateKey, NULL);
-#if XORG == 15
-				SwitchCoreKeyboard(keyboardDev);
-#else
-				CopyKeyClass(keyboardDev, master);
-#endif
-			}
-#else /* XORG < 17 */
-			XkbApplyMappingChange(keyboardDev, keymap, minKeyCode,
-					      maxKeyCode - minKeyCode + 1,
-					      NULL, serverClient);
-#if XORG >= 111
-			XkbCopyDeviceKeymap(keyboardDev->master, keyboardDev);
-#else
-			XkbCopyDeviceKeymap(keyboardDev->u.master, keyboardDev);
-#endif
-#endif /* XORG < 17 */
 			break;
 		}
-	}
 
-	if (kc < minKeyCode) {
-		vlog.info("Keyboard mapping full - ignoring unknown keysym "
-			  "0x%x",keysym);
-		FREE_MAPS;
-		return;
-	}
-
-#if XORG < 17
-	/*
-	 * See if it's a modifier key.  If so, then don't do any auto-repeat,
-	 * because the X server will translate each press into a release
-	 * followed by a press.
-	 */
-	for (i = 0; i < 8; i++) {
-		for (k = 0; k < maxKeysPerMod; k++) {
-			int index = i * maxKeysPerMod + k;
-			if (kc == modmap[index] && IS_PRESSED(keyc,kc) && down) {
-				FREE_MAPS;
-				return;
-			}	
+		if (i == sizeof(altKeysym)/sizeof(altKeysym[0]))
+			vlog.debug("No alternative keysym found");
+		else {
+			keycode = keycode2;
+			new_state = new_state2;
 		}
 	}
-#else
+
 	/*
-	 * If you would like to press a key which is already pressed then
-	 * viewer didn't send the "release" event. In this case release it
-	 * before the press.
+	 * "Shifted Tab" is a bit of a mess. Some systems have varying,
+	 * special keysyms for this symbol. VNC mandates that clients
+	 * should always send the plain XK_Tab keysym and the server
+	 * should deduce the meaning based on current Shift state.
+	 * To comply with this, we will find the keycode that sends
+	 * XK_Tab, and make sure that Shift isn't cleared. This can
+	 * possibly result in a different keysym than XK_Tab, but that
+	 * is the desired behaviour.
+	 *
+	 * Note: We never get ISO_Left_Tab here because it's already
+	 *       been translated in VNCSConnectionST.
 	 */
-	if (IS_PRESSED(keyc, kc) && down) {
-		vlog.debug("KeyRelease for %d wasn't sent, releasing", kc);
-		pressKey(keyboardDev, kc, false, "fixing keycode");
-	}
-#endif
+	if (keysym == XK_Tab && (state & ShiftMask))
+		new_state |= ShiftMask;
 
-	if (maxKeysPerMod != 0) {
-		ModifierState shift(keyboardDev, ShiftMapIndex);
-		ModifierState modeSwitch(keyboardDev, modeSwitchMapIndex);
-		if (down) {
-			if (col & 1)
-				shift.press();
-			else
-				shift.release();
-			if (modeSwitchMapIndex) {
-				if (col & 2)
-					modeSwitch.press();
-				else
-					modeSwitch.release();
-			}
-		}
-		/*
-		 * Ensure ModifierState objects are not destroyed before
-		 * pressKey call, otherwise fake modifier keypress can be lost.
-		 */
-		pressKey(keyboardDev, kc, down, "keycode");
-	} else {
-press:
-		pressKey(keyboardDev, kc, down, "keycode");
-	}
-
-
-        FREE_MAPS;
-	
 	/*
-	 * When faking a modifier we are putting a keycode (which can
-	 * currently activate the desired modifier) on the input
-	 * queue. A future modmap change can change the mapping so
-	 * that this keycode means something else entirely. Guard
-	 * against this by processing the queue now.
+	 * We need a bigger state change than just shift,
+	 * so we need to know what the mask is for level 3 shifts.
 	 */
-	mieqProcessInputEvents();
-}
-
-static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col)
-{
-	int per = keymap->mapWidth;
-	KeySym *syms;
-	KeySym lsym, usym;
-
-	if ((col < 0) || ((col >= per) && (col > 3)) ||
-	    (keycode < keymap->minKeyCode) || (keycode > keymap->maxKeyCode))
-		return NoSymbol;
-
-	syms = &keymap->map[(keycode - keymap->minKeyCode) * per];
-	if (col >= 4)
-		return syms[col];
-
-	if (col > 1) {
-		while ((per > 2) && (syms[per - 1] == NoSymbol))
-			per--;
-		if (per < 3)
-			col -= 2;
-	}
-
-	if ((per <= (col|1)) || (syms[col|1] == NoSymbol)) {
-		XkbConvertCase
-			    (syms[col&~1], &lsym, &usym);
-		if (!(col & 1))
-			return lsym;
-		/*
-		 * I'm commenting out this logic because it's incorrect even
-		 * though it was copied from the Xlib sources.  The X protocol
-		 * book quite clearly states that where a group consists of
-		 * element 1 being a non-alphabetic keysym and element 2 being
-		 * NoSymbol that you treat the second element as being the
-		 * same as the first.  This also tallies with the behaviour
-		 * produced by the installed Xlib on my linux box (I believe
-		 * this is because it uses some XKB code rather than the
-		 * original Xlib code - compare XKBBind.c with KeyBind.c in
-		 * lib/X11).
-		 */
-#if 0
-		else if (usym == lsym)
-			return NoSymbol;
-#endif
-		else
-			return usym;
-	}
-
-	return syms[col];
-}
+	if ((new_state & ~ShiftMask) != (state & ~ShiftMask))
+		level_three_mask = getLevelThreeMask();
+	else
+		level_three_mask = 0;
+
+	shift_press = level_three_press = 0;
+
+	/* Need a fake press or release of shift? */
+	if (!(state & ShiftMask) && (new_state & ShiftMask)) {
+		shift_press = pressShift();
+		if (shift_press == 0) {
+			vlog.error("Unable to find a modifier key for Shift");
+			return;
+		}
 
-/*
- * KeysymToKeycode() - find the keycode and column corresponding to the given
- * keysym.  The value of col passed in should be the column determined from the
- * current shift state.  If the keysym can be found in that column we prefer
- * that to finding it in a different column (which would require fake events to
- * alter the shift state).
- */
-static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col)
-{
-	int i, j;
+		pressKey(keyboardDev, shift_press, true, "temp shift");
+	} else if ((state & ShiftMask) && !(new_state & ShiftMask)) {
+		std::list<KeyCode>::const_iterator iter;
+
+		shift_release = releaseShift();
+		if (shift_release.empty()) {
+			vlog.error("Unable to find the modifier key(s) for releasing Shift");
+			return;
+		}
 
-	j = *col;
-	for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
-		if (KeyCodetoKeySym(keymap, i, j) == ks)
-			return i;
+		for (iter = shift_release.begin();iter != shift_release.end();++iter)
+			pressKey(keyboardDev, *iter, false, "temp shift");
 	}
 
-	for (j = 0; j < keymap->mapWidth; j++) {
-		for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
-			if (KeyCodetoKeySym(keymap, i, j) == ks) {
-				*col = j;
-				return i;
-			}
+	/* Need a fake press or release of level three shift? */
+	if (!(state & level_three_mask) && (new_state & level_three_mask)) {
+		level_three_press = pressLevelThree();
+		if (level_three_press == 0) {
+			vlog.error("Unable to find a modifier key for ISO_Level3_Shift/Mode_Switch");
+			return;
 		}
-	}
 
-	return 0;
-}
-
-#if XORG < 17
-/* Fairly standard US PC Keyboard */
-
-#define MIN_KEY 8
-#define MAX_KEY 255
-#define MAP_LEN (MAX_KEY - MIN_KEY + 1)
-#define KEYSYMS_PER_KEY 2
-KeySym keyboardMap[MAP_LEN * KEYSYMS_PER_KEY] = {
-	NoSymbol, NoSymbol,
-	XK_Escape, NoSymbol,
-	XK_1, XK_exclam,
-	XK_2, XK_at,
-	XK_3, XK_numbersign,
-	XK_4, XK_dollar,
-	XK_5, XK_percent,
-	XK_6, XK_asciicircum,
-	XK_7, XK_ampersand,
-	XK_8, XK_asterisk,
-	XK_9, XK_parenleft,
-	XK_0, XK_parenright,
-	XK_minus, XK_underscore,
-	XK_equal, XK_plus,
-	XK_BackSpace, NoSymbol,
-	XK_Tab, NoSymbol,
-	XK_q, XK_Q,
-	XK_w, XK_W,
-	XK_e, XK_E,
-	XK_r, XK_R,
-	XK_t, XK_T,
-	XK_y, XK_Y,
-	XK_u, XK_U,
-	XK_i, XK_I,
-	XK_o, XK_O,
-	XK_p, XK_P,
-	XK_bracketleft, XK_braceleft,
-	XK_bracketright, XK_braceright,
-	XK_Return, NoSymbol,
-	XK_Control_L, NoSymbol,
-	XK_a, XK_A,
-	XK_s, XK_S,
-	XK_d, XK_D,
-	XK_f, XK_F,
-	XK_g, XK_G,
-	XK_h, XK_H,
-	XK_j, XK_J,
-	XK_k, XK_K,
-	XK_l, XK_L,
-	XK_semicolon, XK_colon,
-	XK_apostrophe, XK_quotedbl,
-	XK_grave, XK_asciitilde,
-	XK_Shift_L, NoSymbol,
-	XK_backslash, XK_bar,
-	XK_z, XK_Z,
-	XK_x, XK_X,
-	XK_c, XK_C,
-	XK_v, XK_V,
-	XK_b, XK_B,
-	XK_n, XK_N,
-	XK_m, XK_M,
-	XK_comma, XK_less,
-	XK_period, XK_greater,
-	XK_slash, XK_question,
-	XK_Shift_R, NoSymbol,
-	XK_KP_Multiply, NoSymbol,
-	XK_Alt_L, XK_Meta_L,
-	XK_space, NoSymbol,
-	XK_Caps_Lock, NoSymbol,
-	XK_F1, NoSymbol,
-	XK_F2, NoSymbol,
-	XK_F3, NoSymbol,
-	XK_F4, NoSymbol,
-	XK_F5, NoSymbol,
-	XK_F6, NoSymbol,
-	XK_F7, NoSymbol,
-	XK_F8, NoSymbol,
-	XK_F9, NoSymbol,
-	XK_F10, NoSymbol,
-	XK_Num_Lock, XK_Pointer_EnableKeys,
-	XK_Scroll_Lock, NoSymbol,
-	XK_KP_Home, XK_KP_7,
-	XK_KP_Up, XK_KP_8,
-	XK_KP_Prior, XK_KP_9,
-	XK_KP_Subtract, NoSymbol,
-	XK_KP_Left, XK_KP_4,
-	XK_KP_Begin, XK_KP_5,
-	XK_KP_Right, XK_KP_6,
-	XK_KP_Add, NoSymbol,
-	XK_KP_End, XK_KP_1,
-	XK_KP_Down, XK_KP_2,
-	XK_KP_Next, XK_KP_3,
-	XK_KP_Insert, XK_KP_0,
-	XK_KP_Delete, XK_KP_Decimal,
-	NoSymbol, NoSymbol,
-	NoSymbol, NoSymbol,
-	NoSymbol, NoSymbol,
-	XK_F11, NoSymbol,
-	XK_F12, NoSymbol,
-	XK_Home, NoSymbol,
-	XK_Up, NoSymbol,
-	XK_Prior, NoSymbol,
-	XK_Left, NoSymbol,
-	NoSymbol, NoSymbol,
-	XK_Right, NoSymbol,
-	XK_End, NoSymbol,
-	XK_Down, NoSymbol,
-	XK_Next, NoSymbol,
-	XK_Insert, NoSymbol,
-	XK_Delete, NoSymbol,
-	XK_KP_Enter, NoSymbol,
-	XK_Control_R, NoSymbol,
-	XK_Pause, XK_Break,
-	XK_Print, XK_Execute,
-	XK_KP_Divide, NoSymbol,
-	XK_Alt_R, XK_Meta_R,
-	NoSymbol, NoSymbol,
-	XK_Super_L, NoSymbol,
-	XK_Super_R, NoSymbol,
-	XK_Menu, NoSymbol,
-};
+		pressKey(keyboardDev, level_three_press, true, "temp level 3 shift");
+	} else if ((state & level_three_mask) && !(new_state & level_three_mask)) {
+		std::list<KeyCode>::const_iterator iter;
+
+		level_three_release = releaseLevelThree();
+		if (level_three_release.empty()) {
+			vlog.error("Unable to find the modifier key(s) for releasing ISO_Level3_Shift/Mode_Switch");
+			return;
+		}
 
-static Bool GetMappings(KeySymsPtr pKeySyms, CARD8 *pModMap)
-{
-	int i;
+		for (iter = level_three_release.begin();iter != level_three_release.end();++iter)
+			pressKey(keyboardDev, *iter, false, "temp level 3 shift");
+	}
 
-	for (i = 0; i < MAP_LENGTH; i++)
-		pModMap[i] = NoSymbol;
+	/* Now press the actual key */
+	pressKey(keyboardDev, keycode, true, "keycode");
 
-	for (i = 0; i < MAP_LEN; i++) {
-		switch (keyboardMap[i * KEYSYMS_PER_KEY]) {
-		case XK_Shift_L:
-		case XK_Shift_R:
-			pModMap[i + MIN_KEY] = ShiftMask;
-			break;
-		case XK_Caps_Lock:
-			pModMap[i + MIN_KEY] = LockMask;
-			break;
-		case XK_Control_L:
-		case XK_Control_R:
-			pModMap[i + MIN_KEY] = ControlMask;
-			break;
-		case XK_Alt_L:
-		case XK_Alt_R:
-			pModMap[i + MIN_KEY] = Mod1Mask;
-			break;
-		case XK_Num_Lock:
-			pModMap[i + MIN_KEY] = Mod2Mask;
-			break;
-			/* No defaults for Mod3Mask yet */
-		case XK_Super_L:
-		case XK_Super_R:
-		case XK_Hyper_L:
-		case XK_Hyper_R:
-			pModMap[i + MIN_KEY] = Mod4Mask;
-			break;
-		case XK_ISO_Level3_Shift:
-		case XK_Mode_switch:
-			pModMap[i + MIN_KEY] = Mod5Mask;
-			break;
+	/* And store the mapping so that we can do a proper release later */
+	for (i = 0;i < 256;i++) {
+		if (i == keycode)
+			continue;
+		if (pressedKeys[i] == keysym) {
+			vlog.error("Keysym 0x%x generated by both keys %d and %d", keysym, i, keycode);
+			pressedKeys[i] = NoSymbol;
 		}
 	}
 
-	pKeySyms->minKeyCode = MIN_KEY;
-	pKeySyms->maxKeyCode = MAX_KEY;
-	pKeySyms->mapWidth = KEYSYMS_PER_KEY;
-	pKeySyms->map = keyboardMap;
+	pressedKeys[keycode] = keysym;
 
-	return TRUE;
-}
-#endif
-
-static void keyboardBell(int percent, DeviceIntPtr device, pointer ctrl,
-			 int class_)
-{
-	if (percent > 0)
-		vncBell();
-}
-
-static int keyboardProc(DeviceIntPtr pDevice, int onoff)
-{
-#if XORG < 17
-	KeySymsRec keySyms;
-	CARD8 modMap[MAP_LENGTH];
-#endif
-	DevicePtr pDev = (DevicePtr)pDevice;
+	/* Undo any fake level three shift */
+	if (level_three_press != 0)
+		pressKey(keyboardDev, level_three_press, false, "temp level 3 shift");
+	else if (!level_three_release.empty()) {
+		std::list<KeyCode>::const_iterator iter;
+		for (iter = level_three_release.begin();iter != level_three_release.end();++iter)
+			pressKey(keyboardDev, *iter, true, "temp level 3 shift");
+	}
 
-	switch (onoff) {
-	case DEVICE_INIT:
-#if XORG < 17
-		GetMappings(&keySyms, modMap);
-#endif
-		InitKeyboardDeviceStruct(
-#if XORG >= 17
-					 pDevice, NULL,
-#else
-					 pDev, &keySyms, modMap,
-#endif
-					 keyboardBell, (KbdCtrlProcPtr)NoopDDA);
-		break;
-	case DEVICE_ON:
-		pDev->on = TRUE;
-		break;
-	case DEVICE_OFF:
-		pDev->on = FALSE;
-		break;
-#if 0
-	case DEVICE_CLOSE:
-		break;
-#endif
+	/* Undo any fake shift */
+	if (shift_press != 0)
+		pressKey(keyboardDev, shift_press, false, "temp shift");
+	else if (!shift_release.empty()) {
+		std::list<KeyCode>::const_iterator iter;
+		for (iter = shift_release.begin();iter != shift_release.end();++iter)
+			pressKey(keyboardDev, *iter, true, "temp shift");
 	}
 
-	return Success;
+	/*
+	 * When faking a modifier we are putting a keycode (which can
+	 * currently activate the desired modifier) on the input
+	 * queue. A future modmap change can change the mapping so
+	 * that this keycode means something else entirely. Guard
+	 * against this by processing the queue now.
+	 */
+	mieqProcessInputEvents();
 }
-
diff -up tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/InputCore.cc.input tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/InputCore.cc
--- tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/InputCore.cc.input	2014-01-17 16:01:13.141109454 +0000
+++ tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/InputCore.cc	2014-01-17 16:01:13.141109454 +0000
@@ -0,0 +1,594 @@
+/* Copyright (C) 2009 TightVNC Team
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright 2013 Pierre Ossman for Cendio AB
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include "Input.h"
+#include "xorg-version.h"
+
+extern "C" {
+#define public c_public
+#define class c_class
+#include "inputstr.h"
+#ifndef XKB_IN_SERVER
+#define XKB_IN_SERVER
+#endif
+#ifdef XKB
+/*
+ * This include is needed to use XkbConvertCase instead of XConvertCase even if
+ * we don't use XKB extension.
+ */
+#include <xkbsrv.h>
+#endif
+/* These defines give us access to all keysyms we need */
+#define XK_PUBLISHING
+#define XK_TECHNICAL
+#include <X11/keysym.h>
+#include <X11/XF86keysym.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#undef public
+#undef class
+}
+
+#if XORG < 17
+
+#define IS_PRESSED(dev, keycode) \
+	((dev)->key->down[(keycode) >> 3] & (1 << ((keycode) & 7)))
+
+/* Fairly standard US PC Keyboard */
+
+#define MIN_KEY 8
+#define MAX_KEY 255
+#define MAP_LEN (MAX_KEY - MIN_KEY + 1)
+#define KEYSYMS_PER_KEY 2
+KeySym keyboardMap[MAP_LEN * KEYSYMS_PER_KEY] = {
+	NoSymbol, NoSymbol,
+	XK_Escape, NoSymbol,
+	XK_1, XK_exclam,
+	XK_2, XK_at,
+	XK_3, XK_numbersign,
+	XK_4, XK_dollar,
+	XK_5, XK_percent,
+	XK_6, XK_asciicircum,
+	XK_7, XK_ampersand,
+	XK_8, XK_asterisk,
+	XK_9, XK_parenleft,
+	XK_0, XK_parenright,
+	XK_minus, XK_underscore,
+	XK_equal, XK_plus,
+	XK_BackSpace, NoSymbol,
+	XK_Tab, NoSymbol,
+	XK_q, XK_Q,
+	XK_w, XK_W,
+	XK_e, XK_E,
+	XK_r, XK_R,
+	XK_t, XK_T,
+	XK_y, XK_Y,
+	XK_u, XK_U,
+	XK_i, XK_I,
+	XK_o, XK_O,
+	XK_p, XK_P,
+	XK_bracketleft, XK_braceleft,
+	XK_bracketright, XK_braceright,
+	XK_Return, NoSymbol,
+	XK_Control_L, NoSymbol,
+	XK_a, XK_A,
+	XK_s, XK_S,
+	XK_d, XK_D,
+	XK_f, XK_F,
+	XK_g, XK_G,
+	XK_h, XK_H,
+	XK_j, XK_J,
+	XK_k, XK_K,
+	XK_l, XK_L,
+	XK_semicolon, XK_colon,
+	XK_apostrophe, XK_quotedbl,
+	XK_grave, XK_asciitilde,
+	XK_Shift_L, NoSymbol,
+	XK_backslash, XK_bar,
+	XK_z, XK_Z,
+	XK_x, XK_X,
+	XK_c, XK_C,
+	XK_v, XK_V,
+	XK_b, XK_B,
+	XK_n, XK_N,
+	XK_m, XK_M,
+	XK_comma, XK_less,
+	XK_period, XK_greater,
+	XK_slash, XK_question,
+	XK_Shift_R, NoSymbol,
+	XK_KP_Multiply, NoSymbol,
+	XK_Alt_L, XK_Meta_L,
+	XK_space, NoSymbol,
+	XK_Caps_Lock, NoSymbol,
+	XK_F1, NoSymbol,
+	XK_F2, NoSymbol,
+	XK_F3, NoSymbol,
+	XK_F4, NoSymbol,
+	XK_F5, NoSymbol,
+	XK_F6, NoSymbol,
+	XK_F7, NoSymbol,
+	XK_F8, NoSymbol,
+	XK_F9, NoSymbol,
+	XK_F10, NoSymbol,
+	XK_Num_Lock, XK_Pointer_EnableKeys,
+	XK_Scroll_Lock, NoSymbol,
+	XK_KP_Home, XK_KP_7,
+	XK_KP_Up, XK_KP_8,
+	XK_KP_Prior, XK_KP_9,
+	XK_KP_Subtract, NoSymbol,
+	XK_KP_Left, XK_KP_4,
+	XK_KP_Begin, XK_KP_5,
+	XK_KP_Right, XK_KP_6,
+	XK_KP_Add, NoSymbol,
+	XK_KP_End, XK_KP_1,
+	XK_KP_Down, XK_KP_2,
+	XK_KP_Next, XK_KP_3,
+	XK_KP_Insert, XK_KP_0,
+	XK_KP_Delete, XK_KP_Decimal,
+	NoSymbol, NoSymbol,
+	NoSymbol, NoSymbol,
+	NoSymbol, NoSymbol,
+	XK_F11, NoSymbol,
+	XK_F12, NoSymbol,
+	XK_Home, NoSymbol,
+	XK_Up, NoSymbol,
+	XK_Prior, NoSymbol,
+	XK_Left, NoSymbol,
+	NoSymbol, NoSymbol,
+	XK_Right, NoSymbol,
+	XK_End, NoSymbol,
+	XK_Down, NoSymbol,
+	XK_Next, NoSymbol,
+	XK_Insert, NoSymbol,
+	XK_Delete, NoSymbol,
+	XK_KP_Enter, NoSymbol,
+	XK_Control_R, NoSymbol,
+	XK_Pause, XK_Break,
+	XK_Print, XK_Execute,
+	XK_KP_Divide, NoSymbol,
+	XK_Alt_R, XK_Meta_R,
+	NoSymbol, NoSymbol,
+	XK_Super_L, NoSymbol,
+	XK_Super_R, NoSymbol,
+	XK_Menu, NoSymbol,
+};
+
+void GetInitKeyboardMap(KeySymsPtr keysyms, CARD8 *modmap)
+{
+	int i;
+
+	for (i = 0; i < MAP_LENGTH; i++)
+		modmap[i] = NoSymbol;
+
+	for (i = 0; i < MAP_LEN; i++) {
+		switch (keyboardMap[i * KEYSYMS_PER_KEY]) {
+		case XK_Shift_L:
+		case XK_Shift_R:
+			modmap[i + MIN_KEY] = ShiftMask;
+			break;
+		case XK_Caps_Lock:
+			modmap[i + MIN_KEY] = LockMask;
+			break;
+		case XK_Control_L:
+		case XK_Control_R:
+			modmap[i + MIN_KEY] = ControlMask;
+			break;
+		case XK_Alt_L:
+		case XK_Alt_R:
+			modmap[i + MIN_KEY] = Mod1Mask;
+			break;
+		case XK_Num_Lock:
+			modmap[i + MIN_KEY] = Mod2Mask;
+			break;
+			/* No defaults for Mod3Mask yet */
+		case XK_Super_L:
+		case XK_Super_R:
+		case XK_Hyper_L:
+		case XK_Hyper_R:
+			modmap[i + MIN_KEY] = Mod4Mask;
+			break;
+		case XK_ISO_Level3_Shift:
+		case XK_Mode_switch:
+			modmap[i + MIN_KEY] = Mod5Mask;
+			break;
+		}
+	}
+
+	keysyms->minKeyCode = MIN_KEY;
+	keysyms->maxKeyCode = MAX_KEY;
+	keysyms->mapWidth = KEYSYMS_PER_KEY;
+	keysyms->map = keyboardMap;
+}
+
+void InputDevice::PrepareInputDevices(void)
+{
+	/* Don't need to do anything here */
+}
+
+unsigned InputDevice::getKeyboardState(void)
+{
+	return keyboardDev->key->state;
+}
+
+unsigned InputDevice::getLevelThreeMask(void)
+{
+	int i, j, k;
+
+	int minKeyCode, mapWidth;
+	KeySym *map;
+
+	int maxKeysPerMod;
+	CARD8 *modmap;
+
+	minKeyCode = keyboardDev->key->curKeySyms.minKeyCode;
+	mapWidth = keyboardDev->key->curKeySyms.mapWidth;
+	map = keyboardDev->key->curKeySyms.map;
+
+	maxKeysPerMod = keyboardDev->key->maxKeysPerModifier;
+	modmap = keyboardDev->key->modifierKeyMap;
+
+	for (i = 3; i < 8; i++) {
+		for (k = 0; k < maxKeysPerMod; k++) {
+			int index = i * maxKeysPerMod + k;
+			int keycode = modmap[index];
+
+			if (keycode == 0)
+				continue;
+
+			for (j = 0; j < mapWidth; j++) {
+				KeySym keysym;
+				keysym = map[(keycode - minKeyCode) * mapWidth + j];
+				if (keysym == XK_Mode_switch)
+					return 1 << i;
+			}
+		}
+	}
+
+	return 0;
+}
+
+KeyCode InputDevice::pressShift(void)
+{
+	int maxKeysPerMod;
+
+	maxKeysPerMod = keyboardDev->key->maxKeysPerModifier;
+	return keyboardDev->key->modifierKeyMap[ShiftMapIndex * maxKeysPerMod];
+}
+
+std::list<KeyCode> InputDevice::releaseShift(void)
+{
+	std::list<KeyCode> keys;
+
+	int maxKeysPerMod;
+
+	maxKeysPerMod = keyboardDev->key->maxKeysPerModifier;
+	for (int k = 0; k < maxKeysPerMod; k++) {
+		int keycode;
+		int index;
+
+		index = ShiftMapIndex * maxKeysPerMod + k;
+
+		keycode = keyboardDev->key->modifierKeyMap[index];
+		if (keycode == 0)
+			continue;
+
+		if (!IS_PRESSED(keyboardDev, keycode))
+			continue;
+
+		keys.push_back(keycode);
+	}
+
+	return keys;
+}
+
+KeyCode InputDevice::pressLevelThree(void)
+{
+	unsigned mask, index;
+	int maxKeysPerMod;
+
+	mask = getLevelThreeMask();
+	if (mask == 0)
+		return 0;
+
+	index = ffs(mask) - 1;
+	maxKeysPerMod = keyboardDev->key->maxKeysPerModifier;
+	return keyboardDev->key->modifierKeyMap[index * maxKeysPerMod];
+}
+
+std::list<KeyCode> InputDevice::releaseLevelThree(void)
+{
+	std::list<KeyCode> keys;
+
+	unsigned mask, msindex;
+	int maxKeysPerMod;
+
+	mask = getLevelThreeMask();
+	if (mask == 0)
+		return keys;
+
+	msindex = ffs(mask) - 1;
+
+	maxKeysPerMod = keyboardDev->key->maxKeysPerModifier;
+	for (int k = 0; k < maxKeysPerMod; k++) {
+		int keycode;
+		int index;
+
+		index = msindex * maxKeysPerMod + k;
+
+		keycode = keyboardDev->key->modifierKeyMap[index];
+		if (keycode == 0)
+			continue;
+
+		if (!IS_PRESSED(keyboardDev, keycode))
+			continue;
+
+		keys.push_back(keycode);
+	}
+
+	return keys;
+}
+
+static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col)
+{
+	int per = keymap->mapWidth;
+	KeySym *syms;
+	KeySym lsym, usym;
+
+	if ((col < 0) || ((col >= per) && (col > 3)) ||
+	    (keycode < keymap->minKeyCode) || (keycode > keymap->maxKeyCode))
+		return NoSymbol;
+
+	syms = &keymap->map[(keycode - keymap->minKeyCode) * per];
+	if (col >= 4)
+		return syms[col];
+
+	if (col > 1) {
+		while ((per > 2) && (syms[per - 1] == NoSymbol))
+			per--;
+		if (per < 3)
+			col -= 2;
+	}
+
+	if ((per <= (col|1)) || (syms[col|1] == NoSymbol)) {
+		XkbConvertCase(syms[col&~1], &lsym, &usym);
+		if (!(col & 1))
+			return lsym;
+		/*
+		 * I'm commenting out this logic because it's incorrect even
+		 * though it was copied from the Xlib sources.  The X protocol
+		 * book quite clearly states that where a group consists of
+		 * element 1 being a non-alphabetic keysym and element 2 being
+		 * NoSymbol that you treat the second element as being the
+		 * same as the first.  This also tallies with the behaviour
+		 * produced by the installed Xlib on my linux box (I believe
+		 * this is because it uses some XKB code rather than the
+		 * original Xlib code - compare XKBBind.c with KeyBind.c in
+		 * lib/X11).
+		 */
+#if 0
+		else if (usym == lsym)
+			return NoSymbol;
+#endif
+		else
+			return usym;
+	}
+
+	return syms[col];
+}
+
+KeyCode InputDevice::keysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state)
+{
+	int i, j;
+	unsigned mask;
+
+	KeySymsPtr keymap;
+	int mapWidth;
+
+	mask = getLevelThreeMask();
+
+	keymap = &keyboardDev->key->curKeySyms;
+
+	/*
+	 * Column 0 means both shift and "mode_switch" (AltGr) must be released,
+	 * column 1 means shift must be pressed and mode_switch released,
+	 * column 2 means shift must be released and mode_switch pressed, and
+	 * column 3 means both shift and mode_switch must be pressed.
+	 */
+	j = 0;
+	if (state & ShiftMask)
+		j |= 0x1;
+	if (state & mask)
+		j |= 0x2;
+
+	*new_state = state;
+	for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
+		if (KeyCodetoKeySym(keymap, i, j) == keysym)
+			return i;
+	}
+
+	/* Only the first four columns have well-defined meaning */
+	mapWidth = keymap->mapWidth;
+	if (mapWidth > 4)
+		mapWidth = 4;
+
+	for (j = 0; j < mapWidth; j++) {
+		for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
+			if (KeyCodetoKeySym(keymap, i, j) == keysym) {
+				*new_state = state & ~(ShiftMask|mask);
+				if (j & 0x1)
+					*new_state |= ShiftMask;
+				if (j & 0x2)
+					*new_state |= mask;
+
+				return i;
+			}
+		}
+	}
+
+	return 0;
+}
+
+bool InputDevice::isLockModifier(KeyCode keycode, unsigned state)
+{
+	int i, j, k;
+
+	int minKeyCode, mapWidth;
+	KeySym *map;
+
+	int maxKeysPerMod;
+	CARD8 *modmap;
+
+	int num_lock_index;
+
+	minKeyCode = keyboardDev->key->curKeySyms.minKeyCode;
+	mapWidth = keyboardDev->key->curKeySyms.mapWidth;
+	map = keyboardDev->key->curKeySyms.map;
+
+	maxKeysPerMod = keyboardDev->key->maxKeysPerModifier;
+	modmap = keyboardDev->key->modifierKeyMap;
+
+	/* Caps Lock is fairly easy as it has a dedicated modmap entry */
+	for (k = 0; k < maxKeysPerMod; k++) {
+		int index;
+
+		index = LockMapIndex * maxKeysPerMod + k;
+		if (keycode == modmap[index])
+			return true;
+	}
+
+	/* For Num Lock we need to find the correct modmap entry */
+	num_lock_index = i;
+	for (i = 3; i < 8; i++) {
+		for (k = 0; k < maxKeysPerMod; k++) {
+			int index = i * maxKeysPerMod + k;
+			int keycode = modmap[index];
+
+			if (keycode == 0)
+				continue;
+
+			for (j = 0; j < mapWidth; j++) {
+				KeySym keysym;
+				keysym = map[(keycode - minKeyCode) * mapWidth + j];
+				if (keysym == XK_Num_Lock) {
+					num_lock_index = i;
+					goto done;
+				}
+			}
+		}
+	}
+done:
+
+	if (num_lock_index == 0)
+		return false;
+
+	/* Now we can look in the modmap */ 
+	for (k = 0; k < maxKeysPerMod; k++) {
+		int index;
+
+		index = num_lock_index * maxKeysPerMod + k;
+		if (keycode == modmap[index])
+			return true;
+	}
+
+	return false;
+}
+
+bool InputDevice::isAffectedByNumLock(KeyCode keycode)
+{
+	KeySymsPtr keymap;
+	int i, per;
+	KeySym *syms;
+
+	keymap = &keyboardDev->key->curKeySyms;
+	per = keymap->mapWidth;
+	syms = &keymap->map[(keycode - keymap->minKeyCode) * per];
+
+	for (i = 0;i < per;i++) {
+		if (IsKeypadKey(syms[i]))
+			return true;
+		if (IsPrivateKeypadKey(syms[i]))
+			return true;
+	}
+
+	return false;
+}
+
+KeyCode InputDevice::addKeysym(KeySym keysym, unsigned state)
+{
+	KeyCode kc;
+
+	int minKeyCode, maxKeyCode, mapWidth;
+	KeySym *map;
+
+	minKeyCode = keyboardDev->key->curKeySyms.minKeyCode;
+	maxKeyCode = keyboardDev->key->curKeySyms.maxKeyCode;
+	mapWidth = keyboardDev->key->curKeySyms.mapWidth;
+	map = keyboardDev->key->curKeySyms.map;
+
+	/*
+	 * Magic, which dynamically adds keysym<->keycode mapping
+	 * depends on X.Org version. Quick explanation of that "magic":
+	 * 
+	 * 1.5
+	 * - has only one core keyboard so we have to keep core
+	 *   keyboard mapping synchronized with vncKeyboardDevice. Do
+	 *   it via SwitchCoreKeyboard()
+	 *
+	 * 1.6 (aka MPX - Multi pointer X)
+	 * - multiple master devices (= core devices) exists, keep
+	 *   vncKeyboardDevice synchronized with proper master device
+	 */
+	for (kc = maxKeyCode; kc >= minKeyCode; kc--) {
+		DeviceIntPtr master;
+
+		if (map[(kc - minKeyCode) * mapWidth] != 0)
+			continue;
+
+		map[(kc - minKeyCode) * mapWidth] = keysym;
+
+#if XORG == 15
+		master = inputInfo.keyboard;
+#else
+		master = keyboardDev->u.master;
+#endif
+		void *slave = dixLookupPrivate(&master->devPrivates,
+					       CoreDevicePrivateKey);
+		if (keyboardDev == slave) {
+			dixSetPrivate(&master->devPrivates,
+				      CoreDevicePrivateKey, NULL);
+#if XORG == 15
+			SwitchCoreKeyboard(keyboardDev);
+#else
+			CopyKeyClass(keyboardDev, master);
+#endif
+		}
+
+		return kc;
+	}
+
+	return 0;
+}
+
+#endif
+
diff -up tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Input.h.input tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Input.h
--- tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Input.h.input	2010-04-14 10:24:06.000000000 +0100
+++ tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Input.h	2014-01-17 16:01:13.141109454 +0000
@@ -1,6 +1,7 @@
 /* Copyright (C) 2009 TightVNC Team
  * Copyright (C) 2009, 2010 Red Hat, Inc.
  * Copyright (C) 2009, 2010 TigerVNC Team
+ * Copyright 2013 Pierre Ossman for Cendio AB
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,12 +27,16 @@
 #include <dix-config.h>
 #endif
 
+#include <list>
+
 #include <rfb/VNCServerST.h>
 
 extern "C" {
 #include "input.h"
 };
 
+#include "xorg-version.h"
+
 /* Represents input device (keyboard + pointer) */
 class InputDevice {
 public:
@@ -55,24 +60,58 @@ public:
 
 	void KeyboardPress(rdr::U32 keysym) { keyEvent(keysym, true); }
 	void KeyboardRelease(rdr::U32 keysym) { keyEvent(keysym, false); }
-private:
+
 	/*
 	 * Init input device. This cannot be done in the constructor
 	 * because constructor is called during X server extensions
 	 * initialization. Devices must be initialized after core
 	 * pointer/keyboard initialization which is actually after extesions
 	 * initialization. Check InitExtensions(), InitCoreDevices() and
-	 * InitInput() calls in dix/main.c
+	 * InitInput() calls in dix/main.c. Instead it is called from
+	 * XserverDesktop at an appropriate time.
 	 */
-	void initInputDevice(void);
+	void InitInputDevice(void);
 
+private:
 	void keyEvent(rdr::U32 keysym, bool down);
 
+	/* Backend dependent functions below here */
+	void PrepareInputDevices(void);
+
+	unsigned getKeyboardState(void);
+	unsigned getLevelThreeMask(void);
+
+	KeyCode pressShift(void);
+	std::list<KeyCode> releaseShift(void);
+
+	KeyCode pressLevelThree(void);
+	std::list<KeyCode> releaseLevelThree(void);
+
+	KeyCode keysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state);
+
+	bool isLockModifier(KeyCode keycode, unsigned state);
+
+	bool isAffectedByNumLock(KeyCode keycode);
+
+	KeyCode addKeysym(KeySym keysym, unsigned state);
+
+private:
+#if XORG >= 17
+	static void vncXkbProcessDeviceEvent(int screenNum,
+	                                     InternalEvent *event,
+	                                     DeviceIntPtr dev);
+#endif
+
+private:
 	rfb::VNCServerST *server;
+	bool initialized;
 	DeviceIntPtr keyboardDev;
 	DeviceIntPtr pointerDev;
+
 	int oldButtonMask;
 	rfb::Point cursorPos, oldCursorPos;
+
+	KeySym pressedKeys[256];
 };
 
 #endif
diff -up tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/InputXKB.cc.input tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/InputXKB.cc
--- tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/InputXKB.cc.input	2014-01-17 16:01:13.141109454 +0000
+++ tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/InputXKB.cc	2014-01-17 16:01:13.141109454 +0000
@@ -0,0 +1,669 @@
+/* Copyright (C) 2009 TightVNC Team
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright 2013 Pierre Ossman for Cendio AB
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include "Input.h"
+#include "xorg-version.h"
+
+#if XORG >= 17
+
+extern "C" {
+#define public c_public
+#define class c_class
+#include "xkbsrv.h"
+#include "xkbstr.h"
+#include "eventstr.h"
+#include "scrnintstr.h"
+#include "mi.h"
+#include <X11/keysym.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#undef public
+#undef class
+}
+
+#if XORG < 19
+static int vncXkbScreenPrivateKeyIndex;
+static DevPrivateKey vncXkbScreenPrivateKey = &vncXkbScreenPrivateKeyIndex;
+#else
+static DevPrivateKeyRec vncXkbPrivateKeyRec;
+#define vncXkbScreenPrivateKey (&vncXkbPrivateKeyRec)
+#endif
+
+#define vncXkbScreenPrivate(pScreen) \
+	(*(InputDevice**) dixLookupPrivate(&(pScreen)->devPrivates, \
+	                                   vncXkbScreenPrivateKey))
+
+#ifndef KEYBOARD_OR_FLOAT
+#define KEYBOARD_OR_FLOAT MASTER_KEYBOARD
+#endif
+
+/* Stolen from libX11 */
+static Bool
+XkbTranslateKeyCode(register XkbDescPtr xkb, KeyCode key,
+                    register unsigned int mods, unsigned int *mods_rtrn,
+                    KeySym *keysym_rtrn)
+{
+	XkbKeyTypeRec *type;
+	int col,nKeyGroups;
+	unsigned preserve,effectiveGroup;
+	KeySym *syms;
+
+	if (mods_rtrn!=NULL)
+		*mods_rtrn = 0;
+
+	nKeyGroups= XkbKeyNumGroups(xkb,key);
+	if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0)) {
+		if (keysym_rtrn!=NULL)
+			*keysym_rtrn = NoSymbol;
+		return False;
+	}
+
+	syms = XkbKeySymsPtr(xkb,key);
+
+	/* find the offset of the effective group */
+	col = 0;
+	effectiveGroup= XkbGroupForCoreState(mods);
+	if ( effectiveGroup>=nKeyGroups ) {
+		unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
+		switch (XkbOutOfRangeGroupAction(groupInfo)) {
+		default:
+			effectiveGroup %= nKeyGroups;
+			break;
+		case XkbClampIntoRange:
+			effectiveGroup = nKeyGroups-1;
+			break;
+		case XkbRedirectIntoRange:
+			effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
+			if (effectiveGroup>=nKeyGroups)
+				effectiveGroup= 0;
+			break;
+		}
+	}
+	col= effectiveGroup*XkbKeyGroupsWidth(xkb,key);
+	type = XkbKeyKeyType(xkb,key,effectiveGroup);
+
+	preserve= 0;
+	if (type->map) { /* find the column (shift level) within the group */
+		register int i;
+		register XkbKTMapEntryPtr entry;
+		for (i=0,entry=type->map;i<type->map_count;i++,entry++) {
+			if ((entry->active)&&((mods&type->mods.mask)==entry->mods.mask)) {
+				col+= entry->level;
+				if (type->preserve)
+					preserve= type->preserve[i].mask;
+				break;
+			}
+		}
+	}
+
+	if (keysym_rtrn!=NULL)
+		*keysym_rtrn= syms[col];
+	if (mods_rtrn)
+		*mods_rtrn= type->mods.mask&(~preserve);
+
+	return (syms[col]!=NoSymbol);
+}
+
+static XkbAction *XkbKeyActionPtr(XkbDescPtr xkb, KeyCode key, unsigned int mods)
+{
+	XkbKeyTypeRec *type;
+	int col,nKeyGroups;
+	unsigned effectiveGroup;
+	XkbAction *acts;
+
+	if (!XkbKeyHasActions(xkb, key))
+		return NULL;
+
+	nKeyGroups= XkbKeyNumGroups(xkb,key);
+	if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0))
+		return NULL;
+
+	acts = XkbKeyActionsPtr(xkb,key);
+
+	/* find the offset of the effective group */
+	col = 0;
+	effectiveGroup= XkbGroupForCoreState(mods);
+	if ( effectiveGroup>=nKeyGroups ) {
+		unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
+		switch (XkbOutOfRangeGroupAction(groupInfo)) {
+		default:
+			effectiveGroup %= nKeyGroups;
+			break;
+		case XkbClampIntoRange:
+			effectiveGroup = nKeyGroups-1;
+			break;
+		case XkbRedirectIntoRange:
+			effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
+			if (effectiveGroup>=nKeyGroups)
+				effectiveGroup= 0;
+			break;
+		}
+	}
+	col= effectiveGroup*XkbKeyGroupsWidth(xkb,key);
+	type = XkbKeyKeyType(xkb,key,effectiveGroup);
+
+	if (type->map) { /* find the column (shift level) within the group */
+		register int i;
+		register XkbKTMapEntryPtr entry;
+		for (i=0,entry=type->map;i<type->map_count;i++,entry++) {
+			if ((entry->active)&&((mods&type->mods.mask)==entry->mods.mask)) {
+				col+= entry->level;
+				break;
+			}
+		}
+	}
+
+	return &acts[col];
+}
+
+static unsigned XkbKeyEffectiveGroup(XkbDescPtr xkb, KeyCode key, unsigned int mods)
+{
+	int nKeyGroups;
+	unsigned effectiveGroup;
+
+	nKeyGroups= XkbKeyNumGroups(xkb,key);
+	if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0))
+		return 0;
+
+	effectiveGroup= XkbGroupForCoreState(mods);
+	if ( effectiveGroup>=nKeyGroups ) {
+		unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
+		switch (XkbOutOfRangeGroupAction(groupInfo)) {
+		default:
+			effectiveGroup %= nKeyGroups;
+			break;
+		case XkbClampIntoRange:
+			effectiveGroup = nKeyGroups-1;
+			break;
+		case XkbRedirectIntoRange:
+			effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
+			if (effectiveGroup>=nKeyGroups)
+				effectiveGroup= 0;
+			break;
+		}
+	}
+
+	return effectiveGroup;
+}
+
+void InputDevice::PrepareInputDevices(void)
+{
+#if XORG < 19
+	if (!dixRequestPrivate(vncXkbScreenPrivateKey, sizeof(InputDevice*)))
+		FatalError("Failed to register TigerVNC XKB screen key\n");
+#else
+	if (!dixRegisterPrivateKey(vncXkbScreenPrivateKey, PRIVATE_SCREEN,
+	                           sizeof(InputDevice*)))
+		FatalError("Failed to register TigerVNC XKB screen key\n");
+#endif
+
+	for (int scr = 0; scr < screenInfo.numScreens; scr++)
+		vncXkbScreenPrivate(screenInfo.screens[scr]) = this;
+
+	/*
+	 * Not ideal since these callbacks do not stack, but it's the only
+	 * decent way we can reliably catch events for both the slave and
+	 * master device.
+	 */
+	mieqSetHandler(ET_KeyPress, vncXkbProcessDeviceEvent);
+	mieqSetHandler(ET_KeyRelease, vncXkbProcessDeviceEvent);
+}
+
+unsigned InputDevice::getKeyboardState(void)
+{
+	return XkbStateFieldFromRec(&keyboardDev->master->key->xkbInfo->state);
+}
+
+unsigned InputDevice::getLevelThreeMask(void)
+{
+	unsigned state;
+	KeyCode keycode;
+	XkbDescPtr xkb;
+	XkbAction *act;
+
+	/* Group state is still important */
+	state = getKeyboardState();
+	state &= ~0xff;
+
+	keycode = keysymToKeycode(XK_ISO_Level3_Shift, state, NULL);
+	if (keycode == 0) {
+		keycode = keysymToKeycode(XK_Mode_switch, state, NULL);
+		if (keycode == 0)
+			return 0;
+	}
+
+	xkb = keyboardDev->master->key->xkbInfo->desc;
+
+	act = XkbKeyActionPtr(xkb, keycode, state);
+	if (act == NULL)
+		return 0;
+	if (act->type != XkbSA_SetMods)
+		return 0;
+
+	if (act->mods.flags & XkbSA_UseModMapMods)
+		return xkb->map->modmap[keycode];
+	else
+		return act->mods.mask;
+}
+
+KeyCode InputDevice::pressShift(void)
+{
+	unsigned state;
+
+	XkbDescPtr xkb;
+	unsigned int key;
+
+	state = getKeyboardState();
+	if (state & ShiftMask)
+		return 0;
+
+	xkb = keyboardDev->master->key->xkbInfo->desc;
+	for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+		XkbAction *act;
+		unsigned char mask;
+
+		act = XkbKeyActionPtr(xkb, key, state);
+		if (act == NULL)
+			continue;
+
+		if (act->type != XkbSA_SetMods)
+			continue;
+
+		if (act->mods.flags & XkbSA_UseModMapMods)
+			mask = xkb->map->modmap[key];
+		else
+			mask = act->mods.mask;
+
+		if ((mask & ShiftMask) == ShiftMask)
+			return key;
+	}
+
+	return 0;
+}
+
+std::list<KeyCode> InputDevice::releaseShift(void)
+{
+	unsigned state;
+	std::list<KeyCode> keys;
+
+	DeviceIntPtr master;
+	XkbDescPtr xkb;
+	unsigned int key;
+
+	state = getKeyboardState();
+	if (!(state & ShiftMask))
+		return keys;
+
+	master = keyboardDev->master;
+	xkb = master->key->xkbInfo->desc;
+	for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+		XkbAction *act;
+		unsigned char mask;
+
+		if (!key_is_down(master, key, KEY_PROCESSED))
+			continue;
+
+		act = XkbKeyActionPtr(xkb, key, state);
+		if (act == NULL)
+			continue;
+
+		if (act->type != XkbSA_SetMods)
+			continue;
+
+		if (act->mods.flags & XkbSA_UseModMapMods)
+			mask = xkb->map->modmap[key];
+		else
+			mask = act->mods.mask;
+
+		if (!(mask & ShiftMask))
+			continue;
+
+		keys.push_back(key);
+	}
+
+	return keys;
+}
+
+KeyCode InputDevice::pressLevelThree(void)
+{
+	unsigned state, mask;
+
+	KeyCode keycode;
+	XkbDescPtr xkb;
+	XkbAction *act;
+
+	mask = getLevelThreeMask();
+	if (mask == 0)
+		return 0;
+
+	state = getKeyboardState();
+	if (state & mask)
+		return 0;
+
+	keycode = keysymToKeycode(XK_ISO_Level3_Shift, state, NULL);
+	if (keycode == 0) {
+		keycode = keysymToKeycode(XK_Mode_switch, state, NULL);
+		if (keycode == 0)
+			return 0;
+	}
+
+	xkb = keyboardDev->master->key->xkbInfo->desc;
+
+	act = XkbKeyActionPtr(xkb, keycode, state);
+	if (act == NULL)
+		return 0;
+	if (act->type != XkbSA_SetMods)
+		return 0;
+
+	return keycode;
+}
+
+std::list<KeyCode> InputDevice::releaseLevelThree(void)
+{
+	unsigned state, mask;
+	std::list<KeyCode> keys;
+
+	DeviceIntPtr master;
+	XkbDescPtr xkb;
+	unsigned int key;
+
+	mask = getLevelThreeMask();
+	if (mask == 0)
+		return keys;
+
+	state = getKeyboardState();
+	if (!(state & mask))
+		return keys;
+
+	master = keyboardDev->master;
+	xkb = master->key->xkbInfo->desc;
+	for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+		XkbAction *act;
+		unsigned char key_mask;
+
+		if (!key_is_down(master, key, KEY_PROCESSED))
+			continue;
+
+		act = XkbKeyActionPtr(xkb, key, state);
+		if (act == NULL)
+			continue;
+
+		if (act->type != XkbSA_SetMods)
+			continue;
+
+		if (act->mods.flags & XkbSA_UseModMapMods)
+			key_mask = xkb->map->modmap[key];
+		else
+			key_mask = act->mods.mask;
+
+		if (!(key_mask & mask))
+			continue;
+
+		keys.push_back(key);
+	}
+
+	return keys;
+}
+
+KeyCode InputDevice::keysymToKeycode(KeySym keysym, unsigned state,
+                                     unsigned *new_state)
+{
+	XkbDescPtr xkb;
+	unsigned int key;
+	KeySym ks;
+	unsigned level_three_mask;
+
+	if (new_state != NULL)
+		*new_state = state;
+
+	xkb = keyboardDev->master->key->xkbInfo->desc;
+	for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+		unsigned int state_out;
+		KeySym dummy;
+
+		XkbTranslateKeyCode(xkb, key, state, &state_out, &ks);
+		if (ks == NoSymbol)
+			continue;
+
+		/*
+		 * Despite every known piece of documentation on
+		 * XkbTranslateKeyCode() stating that mods_rtrn returns
+		 * the unconsumed modifiers, in reality it always
+		 * returns the _potentially consumed_ modifiers.
+		 */
+		state_out = state & ~state_out;
+		if (state_out & LockMask)
+			XkbConvertCase(ks, &dummy, &ks);
+
+		if (ks == keysym)
+			return key;
+	}
+
+	if (new_state == NULL)
+		return 0;
+
+	*new_state = (state & ~ShiftMask) |
+	             ((state & ShiftMask) ? 0 : ShiftMask);
+	key = keysymToKeycode(keysym, *new_state, NULL);
+	if (key != 0)
+		return key;
+
+	level_three_mask = getLevelThreeMask();
+	if (level_three_mask == 0)
+		return 0;
+
+	*new_state = (state & ~level_three_mask) | 
+	             ((state & level_three_mask) ? 0 : level_three_mask);
+	key = keysymToKeycode(keysym, *new_state, NULL);
+	if (key != 0)
+		return key;
+
+	*new_state = (state & ~(ShiftMask | level_three_mask)) | 
+	             ((state & ShiftMask) ? 0 : ShiftMask) |
+	             ((state & level_three_mask) ? 0 : level_three_mask);
+	key = keysymToKeycode(keysym, *new_state, NULL);
+	if (key != 0)
+		return key;
+
+	return 0;
+}
+
+bool InputDevice::isLockModifier(KeyCode keycode, unsigned state)
+{
+	XkbDescPtr xkb;
+	XkbAction *act;
+
+	xkb = keyboardDev->master->key->xkbInfo->desc;
+
+	act = XkbKeyActionPtr(xkb, keycode, state);
+	if (act == NULL)
+		return false;
+
+	if (act->type != XkbSA_LockMods)
+		return false;
+
+	return true;
+}
+
+bool InputDevice::isAffectedByNumLock(KeyCode keycode)
+{
+	unsigned state;
+
+	KeyCode numlock_keycode;
+	unsigned numlock_mask;
+
+	XkbDescPtr xkb;
+	XkbAction *act;
+
+	unsigned group;
+	XkbKeyTypeRec *type;
+
+	/* Group state is still important */
+	state = getKeyboardState();
+	state &= ~0xff;
+
+	/*
+	 * Not sure if hunting for a virtual modifier called "NumLock",
+	 * or following the keysym Num_Lock is the best approach. We
+	 * try the latter.
+	 */
+	numlock_keycode = keysymToKeycode(XK_Num_Lock, state, NULL);
+	if (numlock_keycode == 0)
+		return false;
+
+	xkb = keyboardDev->master->key->xkbInfo->desc;
+
+	act = XkbKeyActionPtr(xkb, numlock_keycode, state);
+	if (act == NULL)
+		return false;
+	if (act->type != XkbSA_LockMods)
+		return false;
+
+	if (act->mods.flags & XkbSA_UseModMapMods)
+		numlock_mask = xkb->map->modmap[keycode];
+	else
+		numlock_mask = act->mods.mask;
+
+	group = XkbKeyEffectiveGroup(xkb, keycode, state);
+	type = XkbKeyKeyType(xkb, keycode, group);
+	if ((type->mods.mask & numlock_mask) == 0)
+		return false;
+
+	return true;
+}
+
+KeyCode InputDevice::addKeysym(KeySym keysym, unsigned state)
+{
+	DeviceIntPtr master;
+	XkbDescPtr xkb;
+	unsigned int key;
+
+	XkbEventCauseRec cause;
+	XkbChangesRec changes;
+
+	int types[1];
+	KeySym *syms;
+	KeySym upper, lower;
+
+	master = keyboardDev->master;
+	xkb = master->key->xkbInfo->desc;
+	for (key = xkb->max_key_code; key >= xkb->min_key_code; key--) {
+		if (XkbKeyNumGroups(xkb, key) == 0)
+			break;
+	}
+
+	if (key < xkb->min_key_code)
+		return 0;
+
+	memset(&changes, 0, sizeof(changes));
+	memset(&cause, 0, sizeof(cause));
+
+	XkbSetCauseUnknown(&cause);
+
+	/*
+	 * Tools like xkbcomp get confused if there isn't a name
+	 * assigned to the keycode we're trying to use.
+	 */
+	if (xkb->names && xkb->names->keys &&
+	    (xkb->names->keys[key].name[0] == '\0')) {
+		xkb->names->keys[key].name[0] = 'I';
+		xkb->names->keys[key].name[1] = '0' + (key / 100) % 10;
+		xkb->names->keys[key].name[2] = '0' + (key /  10) % 10;
+		xkb->names->keys[key].name[3] = '0' + (key /   1) % 10;
+
+		changes.names.changed |= XkbKeyNamesMask;
+		changes.names.first_key = key;
+		changes.names.num_keys = 1;
+	}
+
+	/* FIXME: Verify that ONE_LEVEL/ALPHABETIC isn't screwed up */
+
+	/*
+	 * For keysyms that are affected by Lock, we are better off
+	 * using ALPHABETIC rather than ONE_LEVEL as the latter
+	 * generally cannot produce lower case when Lock is active.
+	 */
+	XkbConvertCase(keysym, &lower, &upper);
+	if (upper == lower)
+		types[XkbGroup1Index] = XkbOneLevelIndex;
+	else
+		types[XkbGroup1Index] = XkbAlphabeticIndex;
+
+	XkbChangeTypesOfKey(xkb, key, 1, XkbGroup1Mask, types, &changes.map);
+
+	syms = XkbKeySymsPtr(xkb,key);
+	if (upper == lower)
+		syms[0] = keysym;
+	else {
+		syms[0] = lower;
+		syms[1] = upper;
+	}
+
+	changes.map.changed |= XkbKeySymsMask;
+	changes.map.first_key_sym = key;
+	changes.map.num_key_syms = 1;
+
+	XkbSendNotification(master, &changes, &cause);
+
+	return key;
+}
+
+void InputDevice::vncXkbProcessDeviceEvent(int screenNum,
+                                           InternalEvent *event,
+                                           DeviceIntPtr dev)
+{
+	InputDevice *self = vncXkbScreenPrivate(screenInfo.screens[screenNum]);
+	unsigned int backupctrls;
+
+	if (event->device_event.sourceid == self->keyboardDev->id) {
+		XkbControlsPtr ctrls;
+
+		/*
+		 * We need to bypass AccessX since it is timing sensitive and
+		 * the network can cause fake event delays.
+		 */
+		ctrls = dev->key->xkbInfo->desc->ctrls;
+		backupctrls = ctrls->enabled_ctrls;
+		ctrls->enabled_ctrls &= ~XkbAllFilteredEventsMask;
+
+		/*
+		 * This flag needs to be set for key repeats to be properly
+		 * respected.
+		 */
+		if ((event->device_event.type == ET_KeyPress) &&
+		    key_is_down(dev, event->device_event.detail.key, KEY_PROCESSED))
+			event->device_event.key_repeat = TRUE;
+	}
+
+	dev->c_public.processInputProc(event, dev);
+
+	if (event->device_event.sourceid == self->keyboardDev->id) {
+		XkbControlsPtr ctrls;
+
+		ctrls = dev->key->xkbInfo->desc->ctrls;
+		ctrls->enabled_ctrls = backupctrls;
+	}
+}
+
+#endif /* XORG >= 117 */
diff -up tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Makefile.am.input tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Makefile.am
--- tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Makefile.am.input	2014-01-17 16:01:04.154064515 +0000
+++ tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/Makefile.am	2014-01-17 16:01:13.142109459 +0000
@@ -14,7 +14,7 @@ HDRS = RegionHelper.h vncExtInit.h vncHo
 	Input.h
 
 libvnccommon_la_SOURCES = $(HDRS) vncExtInit.cc vncHooks.cc XserverDesktop.cc \
-	Input.cc
+	Input.cc InputCore.cc InputXKB.cc
 
 libvnccommon_la_CPPFLAGS = -DVENDOR_RELEASE="$(VENDOR_RELEASE)" \
 	-DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(TIGERVNC_SRCDIR)/common -UHAVE_CONFIG_H \
diff -up tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/XserverDesktop.cc.input tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/XserverDesktop.cc
--- tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/XserverDesktop.cc.input	2014-01-17 16:01:32.532206349 +0000
+++ tigervnc-1.2.80-20130314svn5065/unix/xserver/hw/vnc/XserverDesktop.cc	2014-01-17 16:02:13.775412129 +0000
@@ -579,6 +579,12 @@ static struct timeval XserverDesktopTime
 
 void XserverDesktop::blockHandler(fd_set* fds, OSTimePtr timeout)
 {
+  // We don't have a good callback for when we can init input devices[1],
+  // so we abuse the fact that this routine will be called first thing
+  // once the dix is done initialising.
+  // [1] Technically Xvnc has InitInput(), but libvnc.so has nothing.
+  inputDevice->InitInputDevice();
+
   try {
     int nextTimeout;