From d07476360e8ae89572c1164dc7d5c605ad40b963 Mon Sep 17 00:00:00 2001 From: Adam Tkac Date: Fri, 28 Aug 2009 12:05:24 +0000 Subject: [PATCH] Move keyboard input related code to Input.h and Input.cc. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@3887 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- unix/xserver/hw/vnc/Input.cc | 606 +++++++++++++++++++++++++- unix/xserver/hw/vnc/Input.h | 13 + unix/xserver/hw/vnc/XserverDesktop.cc | 589 +------------------------ unix/xserver/hw/vnc/XserverDesktop.h | 1 + 4 files changed, 622 insertions(+), 587 deletions(-) diff --git a/unix/xserver/hw/vnc/Input.cc b/unix/xserver/hw/vnc/Input.cc index ca279f0e..478240c4 100644 --- a/unix/xserver/hw/vnc/Input.cc +++ b/unix/xserver/hw/vnc/Input.cc @@ -21,13 +21,49 @@ #include #endif +#include #include "Input.h" #include "xorg-version.h" +#include "vncExtInit.h" extern "C" { +#define public c_public +#define class c_class +#include "inputstr.h" #include "mi.h" +#ifndef XKB_IN_SERVER +#define XKB_IN_SERVER +#endif +#ifdef XKB +/* + * This include is needed to use XkbConvertCase instead of XConvertCase even if + * we don't use XKB extension. + */ +#include +#endif +#if XORG >= 16 +#include "exevents.h" +extern void +CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master); +#endif +#include +#include +#undef public +#undef class } +using namespace rdr; +using namespace rfb; + +static LogWriter vlog("Input"); + +#define BUTTONS 5 +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 static xEvent *eventq = NULL; @@ -70,10 +106,6 @@ static void enqueueEvents(DeviceIntPtr dev, int n) } } -/* Pointer device pre-declarations */ -#define BUTTONS 5 -static int pointerProc(DeviceIntPtr pDevice, int onoff); - /* Pointer device methods */ PointerDevice::PointerDevice(rfb::VNCServerST *_server) @@ -165,3 +197,569 @@ static int pointerProc(DeviceIntPtr pDevice, int onoff) return Success; } +/* KeyboardDevice methods */ + +KeyboardDevice::KeyboardDevice(void) +{ + dev = AddInputDevice( +#if XORG >= 16 + serverClient, +#endif + keyboardProc, TRUE); + RegisterKeyboardDevice(dev); + initEventq(); +} + +#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++) + generateXKeyEvent(keys[i], !pressed); + delete [] keys; + } + + void press() + { + KeyClassPtr keyc = dev->key; + if (!(keyc->state & (1 << modIndex))) { + int index = modIndex * keyc->maxKeysPerModifier; + tempKeyEvent(keyc->modifierKeyMap[index], true); + pressed = true; + } + } + + void release() + { + KeyClassPtr keyc = dev->key; + if ((keyc->state & (1 << modIndex)) == 0) + return; + + for (int k = 0; k < keyc->maxKeysPerModifier; k++) { + int index = modIndex * keyc->maxKeysPerModifier + k; + int keycode = keyc->modifierKeyMap[index]; + if (keycode && IS_PRESSED(keyc, keycode)) + tempKeyEvent(keycode, false); + } + } + +private: + void tempKeyEvent(int keycode, bool down) + { + if (keycode) { + if (!keys) keys = new int[dev->key->maxKeysPerModifier]; + keys[nKeys++] = keycode; + generateXKeyEvent(keycode, down); + } + } + + void generateXKeyEvent(int keycode, bool down) + { + int n, action; + + action = down ? KeyPress : KeyRelease; + n = GetKeyboardEvents(eventq, dev, action, keycode); + enqueueEvents(dev, n); + + vlog.debug("fake keycode %d %s", keycode, + down ? "down" : "up"); + } + + 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 { + KeySym a, b; +} altKeysym[] = { + { XK_Shift_L, XK_Shift_R }, + { XK_Control_L, XK_Control_R }, + { XK_Meta_L, XK_Meta_R }, + { XK_Alt_L, XK_Alt_R }, + { XK_Super_L, XK_Super_R }, + { XK_Hyper_L, XK_Hyper_R }, + { XK_KP_Space, XK_space }, + { XK_KP_Tab, XK_Tab }, + { XK_KP_Enter, XK_Return }, + { XK_KP_F1, XK_F1 }, + { XK_KP_F2, XK_F2 }, + { XK_KP_F3, XK_F3 }, + { XK_KP_F4, XK_F4 }, + { XK_KP_Home, XK_Home }, + { XK_KP_Left, XK_Left }, + { XK_KP_Up, XK_Up }, + { XK_KP_Right, XK_Right }, + { XK_KP_Down, XK_Down }, + { XK_KP_Page_Up, XK_Page_Up }, + { XK_KP_Page_Down, XK_Page_Down }, + { XK_KP_End, XK_End }, + { XK_KP_Begin, XK_Begin }, + { XK_KP_Insert, XK_Insert }, + { XK_KP_Delete, XK_Delete }, + { XK_KP_Equal, XK_equal }, + { XK_KP_Multiply, XK_asterisk }, + { XK_KP_Add, XK_plus }, + { XK_KP_Separator, XK_comma }, + { XK_KP_Subtract, XK_minus }, + { XK_KP_Decimal, XK_period }, + { XK_KP_Divide, XK_slash }, + { XK_KP_0, XK_0 }, + { XK_KP_1, XK_1 }, + { XK_KP_2, XK_2 }, + { XK_KP_3, XK_3 }, + { XK_KP_4, XK_4 }, + { XK_KP_5, XK_5 }, + { XK_KP_6, XK_6 }, + { XK_KP_7, XK_7 }, + { XK_KP_8, XK_8 }, + { XK_KP_9, XK_9 }, +}; + +/* + * 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 + */ + +void KeyboardDevice::keyEvent(rdr::U32 keysym, bool down) +{ + DeviceIntPtr master; + KeyClassPtr keyc = dev->key; + KeySymsPtr keymap = &keyc->curKeySyms; + KeySym *map = keymap->map; + KeyCode minKeyCode = keymap->minKeyCode; + KeyCode maxKeyCode = keymap->maxKeyCode; + int mapWidth = keymap->mapWidth; + unsigned int i, n; + int j, k, action; + + if (keysym == XK_Caps_Lock) { + vlog.debug("Ignoring caps lock"); + return; + } + + /* find which modifier Mode_switch is on. */ + int modeSwitchMapIndex = 0; + for (i = 3; i < 8; i++) { + for (k = 0; k < keyc->maxKeysPerModifier; k++) { + int index = i * keyc->maxKeysPerModifier + k; + int keycode = keyc->modifierKeyMap[index]; + + if (keycode == 0) + continue; + + for (j = 0; j < mapWidth; j++) { + if (map[(keycode - minKeyCode) * mapWidth + j] + == XK_Mode_switch) { + modeSwitchMapIndex = i; + goto ModeSwitchFound; + } + } + } + } +ModeSwitchFound: + + int col = 0; + if ((keyc->state & (1 << ShiftMapIndex)) != 0) + col |= 1; + if (modeSwitchMapIndex != 0 && + ((keyc->state & (1 << modeSwitchMapIndex))) != 0) + col |= 2; + + int 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. + */ + if (keysym == XK_Tab && ((keyc->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 (kc == 0) { + /* Dynamically add a new key to the keyboard mapping. */ + for (kc = maxKeyCode; kc >= minKeyCode; kc--) { + if (map[(kc - minKeyCode) * mapWidth] != 0) + continue; + + map[(kc - minKeyCode) * mapWidth] = keysym; + col = 0; + + vlog.info("Added unknown keysym 0x%x to keycode %d", + keysym, kc); +#if XORG == 15 + master = inputInfo.keyboard; +#else + master = dev->u.master; +#endif + void *slave = dixLookupPrivate(&master->devPrivates, + CoreDevicePrivateKey); + if (dev == slave) { + dixSetPrivate(&master->devPrivates, + CoreDevicePrivateKey, NULL); +#if XORG == 15 + SwitchCoreKeyboard(dev); +#else + CopyKeyClass(dev, master); +#endif + } + break; + } + } + + if (kc < minKeyCode) { + vlog.info("Keyboard mapping full - ignoring unknown keysym " + "0x%x",keysym); + return; + } + + /* + * 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 < keyc->maxKeysPerModifier; k++) { + int index = i * keyc->maxKeysPerModifier + k; + if (kc == keyc->modifierKeyMap[index] && + IS_PRESSED(keyc,kc) && down) + return; + } + } + + ModifierState shift(dev, ShiftMapIndex); + ModifierState modeSwitch(dev, modeSwitchMapIndex); + if (down) { + if (col & 1) + shift.press(); + else + shift.release(); + if (modeSwitchMapIndex) { + if (col & 2) + modeSwitch.press(); + else + modeSwitch.release(); + } + } + + vlog.debug("keycode %d %s", kc, down ? "down" : "up"); + action = down ? KeyPress : KeyRelease; + n = GetKeyboardEvents(eventq, dev, action, kc); + enqueueEvents(dev, n); +} + +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)) { + XConvertCase(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; +} + +/* 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, 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, +}; + +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++) { + if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Caps_Lock) + pModMap[i + MIN_KEY] = LockMask; + else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_L || + keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_R) + pModMap[i + MIN_KEY] = ShiftMask; + else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_L || + keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_R) + pModMap[i + MIN_KEY] = ControlMask; + else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_L || + keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_R) + pModMap[i + MIN_KEY] = Mod1Mask; + } + + pKeySyms->minKeyCode = MIN_KEY; + pKeySyms->maxKeyCode = MAX_KEY; + pKeySyms->mapWidth = KEYSYMS_PER_KEY; + pKeySyms->map = keyboardMap; + + return TRUE; +} + +static void keyboardBell(int percent, DeviceIntPtr device, pointer ctrl, + int class_) +{ + if (percent > 0) + vncBell(); +} + +static int keyboardProc(DeviceIntPtr pDevice, int onoff) +{ + KeySymsRec keySyms; + CARD8 modMap[MAP_LENGTH]; + DevicePtr pDev = (DevicePtr)pDevice; + + switch (onoff) { + case DEVICE_INIT: + GetMappings(&keySyms, modMap); + InitKeyboardDeviceStruct(pDev, &keySyms, modMap, keyboardBell, + (KbdCtrlProcPtr)NoopDDA); + break; + case DEVICE_ON: + pDev->on = TRUE; + break; + case DEVICE_OFF: + pDev->on = FALSE; + break; +#if 0 + case DEVICE_CLOSE: + break; +#endif + } + + return Success; +} + diff --git a/unix/xserver/hw/vnc/Input.h b/unix/xserver/hw/vnc/Input.h index 49cedf29..1bfec922 100644 --- a/unix/xserver/hw/vnc/Input.h +++ b/unix/xserver/hw/vnc/Input.h @@ -58,4 +58,17 @@ private: rfb::Point cursorPos, oldCursorPos; }; +/* Represents keyboard device. */ +class KeyboardDevice { +public: + /* Create new Keyboard device instance. */ + KeyboardDevice(void); + + void Press(rdr::U32 keysym) { keyEvent(keysym, true); } + void Release(rdr::U32 keysym) { keyEvent(keysym, false); } +private: + void keyEvent(rdr::U32 keysym, bool down); + DeviceIntPtr dev; +}; + #endif diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 58f70d97..66c92d29 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -50,26 +50,7 @@ extern "C" { extern char *display; -#include "inputstr.h" -#include "servermd.h" #include "colormapst.h" -#include "resource.h" -#include "cursorstr.h" -#include "windowstr.h" -#include "mi.h" -#define XK_CYRILLIC -#include -#ifndef XKB_IN_SERVER -#define XKB_IN_SERVER -#endif -#ifdef XKB -#include -#endif -#if XORG >= 16 -#include "exevents.h" -extern void -CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master); -#endif #ifdef RANDR #include "randrstr.h" #endif @@ -77,15 +58,6 @@ CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master); #undef class } -static DeviceIntPtr vncKeyboardDevice = NULL; -#if XORG == 15 -static xEvent *eventq = NULL; -#else -static EventList *eventq = NULL; -#endif - -static int vfbKeybdProc(DeviceIntPtr pDevice, int onoff); - using namespace rfb; using namespace network; @@ -102,8 +74,6 @@ IntParameter queryConnectTimeout("QueryConnectTimeout", "rejecting the connection", 10); -static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col); - static rdr::U8 reverseBits[] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, @@ -193,33 +163,8 @@ XserverDesktop::XserverDesktop(ScreenPtr pScreen_, if (httpListener) httpServer = new FileHTTPServer(this); -#if XORG == 15 - /* - * XXX eventq is never free()-ed because it has to exist during server life - * */ - if (!eventq) - eventq = (xEvent *) xcalloc(sizeof(xEvent), GetMaximumEventsNum()); - if (!eventq) - FatalError("Couldn't allocate eventq\n"); -#else - GetEventList(&eventq); -#endif - - /* - * NOTE: - * We _might_ have to call ActivateDevice function for both keyboard and - * mouse. For Xvnc it's not needed but I have to check libvnc.so module. - */ - if (vncKeyboardDevice == NULL) { - vncKeyboardDevice = AddInputDevice( -#if XORG >= 16 - serverClient, -#endif - vfbKeybdProc, TRUE); - RegisterKeyboardDevice(vncKeyboardDevice); - } - pointerDevice = new PointerDevice(server); + keyboardDevice = new KeyboardDevice(); } XserverDesktop::~XserverDesktop() @@ -229,6 +174,7 @@ XserverDesktop::~XserverDesktop() TimerFree(deferredUpdateTimer); TimerFree(dummyTimer); delete pointerDevice; + delete keyboardDevice; delete httpServer; delete server; } @@ -854,533 +800,10 @@ void XserverDesktop::lookup(int index, int* r, int* g, int* b) } } -// -// Keyboard handling -// - -#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(int modIndex_) - : modIndex(modIndex_), nKeys(0), keys(0), pressed(false) - { - } - ~ModifierState() { - for (int i = 0; i < nKeys; i++) - generateXKeyEvent(keys[i], !pressed); - delete [] keys; - } - void press() { - KeyClassPtr keyc = vncKeyboardDevice->key; - if (!(keyc->state & (1<modifierKeyMap[modIndex * keyc->maxKeysPerModifier], - true); - pressed = true; - } - } - void release() { - KeyClassPtr keyc = vncKeyboardDevice->key; - if (keyc->state & (1<maxKeysPerModifier; k++) { - int keycode - = keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier + k]; - if (keycode && IS_PRESSED(keyc, keycode)) - tempKeyEvent(keycode, false); - } - } - } -private: - void tempKeyEvent(int keycode, bool down) { - if (keycode) { - if (!keys) keys = new int[vncKeyboardDevice->key->maxKeysPerModifier]; - keys[nKeys++] = keycode; - generateXKeyEvent(keycode, down); - } - } - void generateXKeyEvent(int keycode, bool down) { - int i, n; - n = GetKeyboardEvents (eventq, vncKeyboardDevice, - down ? KeyPress : KeyRelease, keycode); - for (i = 0; i < n; i++) { - mieqEnqueue (vncKeyboardDevice, -#if XORG == 15 - eventq + i -#else - (eventq + i)->event -#endif - ); - } - vlog.debug("fake keycode %d %s", keycode, down ? "down" : "up"); - } - int modIndex; - int nKeys; - int* keys; - bool pressed; -}; - - -// altKeysym is a table of alternative keysyms which have the same meaning. - -struct altKeysym_t { - KeySym a, b; -}; - -altKeysym_t altKeysym[] = { - { XK_Shift_L, XK_Shift_R }, - { XK_Control_L, XK_Control_R }, - { XK_Meta_L, XK_Meta_R }, - { XK_Alt_L, XK_Alt_R }, - { XK_Super_L, XK_Super_R }, - { XK_Hyper_L, XK_Hyper_R }, - { XK_KP_Space, XK_space }, - { XK_KP_Tab, XK_Tab }, - { XK_KP_Enter, XK_Return }, - { XK_KP_F1, XK_F1 }, - { XK_KP_F2, XK_F2 }, - { XK_KP_F3, XK_F3 }, - { XK_KP_F4, XK_F4 }, - { XK_KP_Home, XK_Home }, - { XK_KP_Left, XK_Left }, - { XK_KP_Up, XK_Up }, - { XK_KP_Right, XK_Right }, - { XK_KP_Down, XK_Down }, - { XK_KP_Page_Up, XK_Page_Up }, - { XK_KP_Page_Down, XK_Page_Down }, - { XK_KP_End, XK_End }, - { XK_KP_Begin, XK_Begin }, - { XK_KP_Insert, XK_Insert }, - { XK_KP_Delete, XK_Delete }, - { XK_KP_Equal, XK_equal }, - { XK_KP_Multiply, XK_asterisk }, - { XK_KP_Add, XK_plus }, - { XK_KP_Separator, XK_comma }, - { XK_KP_Subtract, XK_minus }, - { XK_KP_Decimal, XK_period }, - { XK_KP_Divide, XK_slash }, - { XK_KP_0, XK_0 }, - { XK_KP_1, XK_1 }, - { XK_KP_2, XK_2 }, - { XK_KP_3, XK_3 }, - { XK_KP_4, XK_4 }, - { XK_KP_5, XK_5 }, - { XK_KP_6, XK_6 }, - { XK_KP_7, XK_7 }, - { XK_KP_8, XK_8 }, - { XK_KP_9, XK_9 }, -}; - -/* - * 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 - */ - void XserverDesktop::keyEvent(rdr::U32 keysym, bool down) { - DeviceIntPtr master; - KeyClassPtr keyc = vncKeyboardDevice->key; - KeySymsPtr keymap = &keyc->curKeySyms; - unsigned int i, n; - int j, k; - - if (keysym == XK_Caps_Lock) { - vlog.debug("Ignoring caps lock"); - return; - } - - // find which modifier Mode_switch is on. - int modeSwitchMapIndex = 0; - for (i = 3; i < 8; i++) { - for (k = 0; k < keyc->maxKeysPerModifier; k++) { - int keycode = keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k]; - for (j = 0; j < keymap->mapWidth; j++) { - if (keycode != 0 && - keymap->map[(keycode - keymap->minKeyCode) - * keymap->mapWidth + j] == XK_Mode_switch) - { - modeSwitchMapIndex = i; - break; - } - } - } - } - - int col = 0; - if (keyc->state & (1<state & (1<state & (1<maxKeyCode; kc >= keymap->minKeyCode; kc--) { - if (!keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth]) { - keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth] = keysym; - col = 0; - - vlog.info("Added unknown keysym 0x%x to keycode %d",keysym,kc); - -#if XORG == 15 - master = inputInfo.keyboard; -#else - master = vncKeyboardDevice->u.master; -#endif - if (vncKeyboardDevice == - dixLookupPrivate(&master->devPrivates, CoreDevicePrivateKey)) { - dixSetPrivate(&master->devPrivates, CoreDevicePrivateKey, NULL); -#if XORG == 15 - SwitchCoreKeyboard(vncKeyboardDevice); -#else - CopyKeyClass(vncKeyboardDevice, master); -#endif - } - break; - } - } - if (kc < keymap->minKeyCode) { - vlog.info("Keyboard mapping full - ignoring unknown keysym 0x%x",keysym); - return; - } - } - - // 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 < keyc->maxKeysPerModifier; k++) { - if (kc == keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k] && - IS_PRESSED(keyc,kc) && down) - return; - } - } - - ModifierState shift(ShiftMapIndex); - ModifierState modeSwitch(modeSwitchMapIndex); - if (down) { - if (col & 1) - shift.press(); - else - shift.release(); - if (modeSwitchMapIndex) { - if (col & 2) - modeSwitch.press(); - else - modeSwitch.release(); - } - } - vlog.debug("keycode %d %s", kc, down ? "down" : "up"); - n = GetKeyboardEvents (eventq, vncKeyboardDevice, down ? - KeyPress : KeyRelease, kc); - for (i = 0; i < n; i++) { - mieqEnqueue (vncKeyboardDevice, -#if XORG == 15 - eventq + i -#else - (eventq + i)->event -#endif - ); - } + if (down) + keyboardDevice->Press(keysym); + else + keyboardDevice->Release(keysym); } - -static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col) -{ - register int per = keymap->mapWidth; - register 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) { - if (col > 1) { - while ((per > 2) && (syms[per - 1] == NoSymbol)) - per--; - if (per < 3) - col -= 2; - } - if ((per <= (col|1)) || (syms[col|1] == NoSymbol)) { - XConvertCase(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). - // else if (usym == lsym) - // return NoSymbol; - 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) -{ - register 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; -} - -/* Fairly standard US PC Keyboard */ - -#define VFB_MIN_KEY 8 -#define VFB_MAX_KEY 255 -#define VFB_MAP_LEN (VFB_MAX_KEY - VFB_MIN_KEY + 1) -#define KEYSYMS_PER_KEY 2 -KeySym keyboardMap[VFB_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, 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, -}; - -static Bool GetMappings(KeySymsPtr pKeySyms, CARD8 *pModMap) -{ - int i; - - for (i = 0; i < MAP_LENGTH; i++) - pModMap[i] = NoSymbol; - - for (i = 0; i < VFB_MAP_LEN; i++) { - if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Caps_Lock) - pModMap[i + VFB_MIN_KEY] = LockMask; - else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_L || - keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_R) - pModMap[i + VFB_MIN_KEY] = ShiftMask; - else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_L || - keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_R) { - pModMap[i + VFB_MIN_KEY] = ControlMask; - } - else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_L || - keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_R) - pModMap[i + VFB_MIN_KEY] = Mod1Mask; - } - - pKeySyms->minKeyCode = VFB_MIN_KEY; - pKeySyms->maxKeyCode = VFB_MAX_KEY; - pKeySyms->mapWidth = KEYSYMS_PER_KEY; - pKeySyms->map = keyboardMap; - - return TRUE; -} - -static void vfbBell(int percent, DeviceIntPtr device, pointer ctrl, int class_) -{ - if (percent > 0) - vncBell(); -} - -static int vfbKeybdProc(DeviceIntPtr pDevice, int onoff) -{ - KeySymsRec keySyms; - CARD8 modMap[MAP_LENGTH]; - DevicePtr pDev = (DevicePtr)pDevice; -#ifdef XKB - XkbComponentNamesRec names; -#endif - - switch (onoff) - { - case DEVICE_INIT: - GetMappings(&keySyms, modMap); -#ifdef XKB - if (!noXkbExtension) { - memset(&names, 0, sizeof (names)); - XkbSetRulesDflts("base", "pc105", "us", NULL, NULL); - XkbInitKeyboardDeviceStruct(pDevice, &names, &keySyms, modMap, - (BellProcPtr)vfbBell, - (KbdCtrlProcPtr)NoopDDA); - } else -#endif - { - InitKeyboardDeviceStruct(pDev, &keySyms, modMap, - (BellProcPtr)vfbBell, (KbdCtrlProcPtr)NoopDDA); - } - break; - case DEVICE_ON: - pDev->on = TRUE; - break; - case DEVICE_OFF: - pDev->on = FALSE; - break; - case DEVICE_CLOSE: - break; - } - return Success; -} - diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index 9f300f53..6e4f609c 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -123,6 +123,7 @@ private: void deferUpdate(); ScreenPtr pScreen; PointerDevice *pointerDevice; + KeyboardDevice *keyboardDevice; OsTimerPtr deferredUpdateTimer, dummyTimer; rfb::VNCServerST* server; rfb::HTTPServer* httpServer; -- 2.39.5