#include <dix-config.h>
#endif
+#include <rfb/LogWriter.h>
#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 <xkbsrv.h>
+#endif
+#if XORG >= 16
+#include "exevents.h"
+extern void
+CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master);
+#endif
+#include <X11/keysym.h>
+#include <X11/Xutil.h>
+#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;
}
}
-/* Pointer device pre-declarations */
-#define BUTTONS 5
-static int pointerProc(DeviceIntPtr pDevice, int onoff);
-
/* Pointer device methods */
PointerDevice::PointerDevice(rfb::VNCServerST *_server)
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;
+}
+
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 <X11/keysym.h>
-#ifndef XKB_IN_SERVER
-#define XKB_IN_SERVER
-#endif
-#ifdef XKB
-#include <xkbsrv.h>
-#endif
-#if XORG >= 16
-#include "exevents.h"
-extern void
-CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master);
-#endif
#ifdef RANDR
#include "randrstr.h"
#endif
#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;
"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,
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()
TimerFree(deferredUpdateTimer);
TimerFree(dummyTimer);
delete pointerDevice;
+ delete keyboardDevice;
delete httpServer;
delete server;
}
}
}
-//
-// 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<<modIndex))) {
- tempKeyEvent(keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier],
- true);
- pressed = true;
- }
- }
- void release() {
- KeyClassPtr keyc = vncKeyboardDevice->key;
- if (keyc->state & (1<<modIndex)) {
- for (int k = 0; k < keyc->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<<ShiftMapIndex)) col |= 1;
- if (modeSwitchMapIndex && (keyc->state & (1<<modeSwitchMapIndex))) 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)))
- 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) {
- // Last resort - dynamically add a new key to the keyboard mapping.
- for (kc = keymap->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;
-}
-