/* 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
#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
static int pointerProc(DeviceIntPtr pDevice, int onoff);
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);
/* Event queue is shared between all devices. */
#if XORG == 15
!EnableDevice(keyboardDev, TRUE))
FatalError("Failed to activate TigerVNC devices\n");
#endif /* 17 */
+
+ PrepareInputDevices();
}
static inline void pressKey(DeviceIntPtr dev, int kc, bool down, const char *msg)
#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 {
/*
* 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)
-{
- 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];
-
- if (keycode == 0)
- continue;
-
- for (j = 0; j < mapWidth; j++) {
- if (map[(keycode - minKeyCode) * mapWidth + j]
- == keysym) {
- return keycode;
- }
- }
- }
- }
-
- 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
+ int i;
+ unsigned state, new_state;
+ KeyCode keycode;
+
+ unsigned level_three_mask;
+ KeyCode shift_press, level_three_press;
+ std::list<KeyCode> shift_release, level_three_release;
/*
* Release events must match the press event, so look up what
*/
mieqProcessInputEvents();
- if (keysym == XK_Caps_Lock) {
- vlog.debug("Ignoring caps lock");
- return;
- }
-
-#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;
- }
-
- 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 = getKeyboardState();
- 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;
+ keycode = keysymToKeycode(keysym, state, &new_state);
-#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)
- continue;
-
- for (j = 0; j < mapWidth; j++) {
- if (map[(keycode - minKeyCode) * mapWidth + j]
- == XK_Mode_switch) {
- modeSwitchMapIndex = i;
- goto ModeSwitchFound;
- }
+ /* 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++) {
+ if (altKeysym[i].a == keysym) {
+ keycode = keysymToKeycode(altKeysym[i].b,
+ state, &new_state);
+ if (keycode != 0)
+ break;
+ }
+ if (altKeysym[i].b == keysym) {
+ keycode = keysymToKeycode(altKeysym[i].a,
+ 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
+ vlog.info("Added unknown keysym 0x%x to keycode %d",
+ keysym, keycode);
- if (maxKeysPerMod != 0) {
- if ((state & (1 << ShiftMapIndex)) != 0)
- col |= 1;
- if (modeSwitchMapIndex != 0 &&
- ((state & (1 << modeSwitchMapIndex))) != 0)
- col |= 2;
+ new_state = state;
}
- kc = KeysymToKeycode(keymap, keysym, &col);
-
/*
- * 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.
+ * We need a bigger state change than just shift,
+ * so we need to know what the mask is for level 3 shifts.
*/
- 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.
- */
- 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;
+ 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;
}
- }
- if (kc == 0) {
- /* Dynamically add a new key to the keyboard mapping. */
- for (kc = maxKeyCode; kc >= minKeyCode; kc--) {
- if (map[(kc - minKeyCode) * mapWidth] != 0)
- continue;
+ pressKey(keyboardDev, shift_press, true, "temp shift");
+ } else if ((state & ShiftMask) && !(new_state & ShiftMask)) {
+ std::list<KeyCode>::const_iterator iter;
- map[(kc - minKeyCode) * mapWidth] = keysym;
- col = 0;
+ shift_release = releaseShift();
+ if (shift_release.empty()) {
+ vlog.error("Unable to find the modifier key(s) for releasing Shift");
+ return;
+ }
- vlog.info("Added unknown keysym 0x%x to keycode %d",
- keysym, kc);
+ for (iter = shift_release.begin();iter != shift_release.end();++iter)
+ pressKey(keyboardDev, *iter, false, "temp shift");
+ }
-#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;
+ /* Need a fake press or release of level three shift? */
+ if (!(state & level_three_mask) && (new_state & level_three_mask)) {
+ level_three_press = pressShift();
+ if (level_three_press == 0) {
+ vlog.error("Unable to find a modifier key for ISO_Level3_Shift/Mode_Switch");
+ return;
}
- }
- if (kc < minKeyCode) {
- vlog.info("Keyboard mapping full - ignoring unknown keysym "
- "0x%x",keysym);
- FREE_MAPS;
- return;
- }
+ 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;
-#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;
- }
+ 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;
}
+
+ for (iter = level_three_release.begin();iter != level_three_release.end();++iter)
+ pressKey(keyboardDev, *iter, false, "temp level 3 shift");
}
-#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.
- */
- if (IS_PRESSED(keyc, kc) && down) {
- vlog.debug("KeyRelease for %d wasn't sent, releasing", kc);
- pressKey(keyboardDev, kc, false, "fixing keycode");
- }
-#endif
- 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");
+ /* Now press the actual key */
+ pressKey(keyboardDev, keycode, true, "keycode");
- /* Store the mapping so that we can do a proper release later */
- for (i = 0;i < 256;i++) {
- if (i == kc)
- continue;
- if (pressedKeys[i] == keysym) {
- vlog.error("Keysym 0x%x generated by both keys %d and %d", keysym, i, kc);
- pressedKeys[i] = NoSymbol;
- }
+ /* 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;
}
-
- pressedKeys[kc] = keysym;
}
+ pressedKeys[keycode] = keysym;
+
+ /* 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");
+ }
- FREE_MAPS;
+ /* 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");
+ }
/*
* When faking a modifier we are putting a keycode (which can
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];
-}
-
-/*
- * 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;
-
- j = *col;
- for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
- if (KeyCodetoKeySym(keymap, i, j) == ks)
- return i;
- }
-
- 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;
- }
- }
- }
-
- 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,
-};
-
-static Bool GetMappings(KeySymsPtr pKeySyms, CARD8 *pModMap)
-{
- int i;
-
- for (i = 0; i < MAP_LENGTH; i++)
- pModMap[i] = NoSymbol;
-
- 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;
- }
- }
-
- pKeySyms->minKeyCode = MIN_KEY;
- pKeySyms->maxKeyCode = MAX_KEY;
- pKeySyms->mapWidth = KEYSYMS_PER_KEY;
- pKeySyms->map = keyboardMap;
-
- return TRUE;
-}
-#endif
-
static void keyboardBell(int percent, DeviceIntPtr device, pointer ctrl,
int class_)
{
/* 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
#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:
* 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. Instead it is called from
- * XserverDesktop at an appropriate time.
+ * InitInput() calls in dix/main.c. Instead it is called from
+ * XserverDesktop at an appropriate time.
*/
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);
+
+ 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;
--- /dev/null
+/* 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/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;
+
+ /*
+ * 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 (keysym == XK_Tab && (state & ShiftMask))
+ *new_state |= ShiftMask;
+
+ 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;
+}
+
+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
+
--- /dev/null
+/* 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/Xutil.h>
+#undef public
+#undef class
+}
+
+static DevPrivateKeyRec vncXkbPrivateKeyRec;
+#define vncXkbScreenPrivateKey (&vncXkbPrivateKeyRec)
+#define vncXkbScreenPrivate(pScreen) \
+ (*(InputDevice**) dixLookupPrivate(&(pScreen)->devPrivates, \
+ vncXkbScreenPrivateKey))
+
+/* 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];
+}
+
+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)
+{
+ DeviceIntPtr master;
+
+ master = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT);
+ return XkbStateFieldFromRec(&master->key->xkbInfo->state);
+}
+
+unsigned InputDevice::getLevelThreeMask(void)
+{
+ KeyCode keycode;
+ XkbDescPtr xkb;
+ XkbAction *act;
+
+ keycode = keysymToKeycode(XK_ISO_Level3_Shift, 0, NULL);
+ if (keycode == 0) {
+ keycode = keysymToKeycode(XK_Mode_switch, 0, NULL);
+ if (keycode == 0)
+ return 0;
+ }
+
+ xkb = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+
+ act = XkbKeyActionPtr(xkb, keycode, 0);
+ 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 = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->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 = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT);
+ 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 = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+
+ act = XkbKeyActionPtr(xkb, keycode, 0);
+ 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 = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT);
+ 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 = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+ for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+ unsigned int state_out;
+
+ XkbTranslateKeyCode(xkb, key, state, &state_out, &ks);
+ if (ks == NoSymbol)
+ continue;
+
+ if (state_out & state & LockMask)
+ XkbConvertCase(ks, NULL, &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 = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+
+ act = XkbKeyActionPtr(xkb, keycode, state);
+ if (act == NULL)
+ return false;
+
+ if (act->type != XkbSA_LockMods)
+ 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;
+
+ master = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT);
+ 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));
+
+ XkbSetCauseCoreReq(&cause, X_ChangeKeyboardMapping, NULL);
+
+ /* FIXME: Verify that ONE_LEVEL isn't screwed up */
+
+ types[XkbGroup1Index] = XkbOneLevelIndex;
+ XkbChangeTypesOfKey(xkb, key, 1, XkbGroup1Mask, types, &changes.map);
+
+ syms = XkbKeySymsPtr(xkb,key);
+ syms[0] = keysym;
+
+ 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 */
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 \