diff options
-rw-r--r-- | unix/xserver/hw/vnc/Input.cc | 816 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/Input.h | 35 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/InputCore.cc | 589 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/InputXKB.cc | 538 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/Makefile.am | 2 |
5 files changed, 1274 insertions, 706 deletions
diff --git a/unix/xserver/hw/vnc/Input.cc b/unix/xserver/hw/vnc/Input.cc index 878122c3..83a9e885 100644 --- a/unix/xserver/hw/vnc/Input.cc +++ b/unix/xserver/hw/vnc/Input.cc @@ -1,5 +1,6 @@ /* Copyright (C) 2009 TightVNC Team * Copyright (C) 2009 Red Hat, Inc. + * Copyright 2013 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,16 +35,6 @@ extern "C" { #include "inpututils.h" #endif #include "mi.h" -#ifndef XKB_IN_SERVER -#define XKB_IN_SERVER -#endif -#ifdef XKB -/* - * This include is needed to use XkbConvertCase instead of XConvertCase even if - * we don't use XKB extension. - */ -#include <xkbsrv.h> -#endif #if XORG >= 16 #include "exevents.h" #endif @@ -76,8 +67,6 @@ static LogWriter vlog("Input"); 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 @@ -320,6 +309,8 @@ void InputDevice::InitInputDevice(void) !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) @@ -341,143 +332,6 @@ 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 { @@ -529,96 +383,21 @@ 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 @@ -650,256 +429,135 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) */ 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 @@ -911,254 +569,6 @@ press: 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_) { diff --git a/unix/xserver/hw/vnc/Input.h b/unix/xserver/hw/vnc/Input.h index fafefded..3a0e9039 100644 --- a/unix/xserver/hw/vnc/Input.h +++ b/unix/xserver/hw/vnc/Input.h @@ -1,6 +1,7 @@ /* Copyright (C) 2009 TightVNC Team * Copyright (C) 2009, 2010 Red Hat, Inc. * Copyright (C) 2009, 2010 TigerVNC Team + * Copyright 2013 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,12 +27,16 @@ #include <dix-config.h> #endif +#include <list> + #include <rfb/VNCServerST.h> extern "C" { #include "input.h" }; +#include "xorg-version.h" + /* Represents input device (keyboard + pointer) */ class InputDevice { public: @@ -62,14 +67,40 @@ 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; diff --git a/unix/xserver/hw/vnc/InputCore.cc b/unix/xserver/hw/vnc/InputCore.cc new file mode 100644 index 00000000..63d94125 --- /dev/null +++ b/unix/xserver/hw/vnc/InputCore.cc @@ -0,0 +1,589 @@ +/* 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 + diff --git a/unix/xserver/hw/vnc/InputXKB.cc b/unix/xserver/hw/vnc/InputXKB.cc new file mode 100644 index 00000000..4ac0d917 --- /dev/null +++ b/unix/xserver/hw/vnc/InputXKB.cc @@ -0,0 +1,538 @@ +/* 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 */ diff --git a/unix/xserver/hw/vnc/Makefile.am b/unix/xserver/hw/vnc/Makefile.am index 5166ef29..9885b8a3 100644 --- a/unix/xserver/hw/vnc/Makefile.am +++ b/unix/xserver/hw/vnc/Makefile.am @@ -13,7 +13,7 @@ HDRS = RegionHelper.h vncExtInit.h vncHooks.h XserverDesktop.h xorg-version.h \ 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 \ |