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 -#endif #if XORG >= 16 #include "exevents.h" #endif @@ -58,6 +49,7 @@ CopyKeyClass(DeviceIntPtr device, Device extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey; #endif #include +#include #include #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< 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 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::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::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::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::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 +#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 +#endif +/* These defines give us access to all keysyms we need */ +#define XK_PUBLISHING +#define XK_TECHNICAL +#include +#include +#include +#include +#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 InputDevice::releaseShift(void) +{ + std::list 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 InputDevice::releaseLevelThree(void) +{ + std::list 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 #endif +#include + #include 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 releaseShift(void); + + KeyCode pressLevelThree(void); + std::list 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 +#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 +#include +#include +#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;imap_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;imap_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 InputDevice::releaseShift(void) +{ + unsigned state; + std::list 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 InputDevice::releaseLevelThree(void) +{ + unsigned state, mask; + std::list 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;