Browse Source

Major rewrite of the keyboard code. The old code was too focused on

the old core X11 keyboards. The new code has an abstract common section,
and two different backends for core and XKB. Core keyboards should be
unaffected, but XKB support should be much better with this approach.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@5075 3789f03b-4d11-0410-bbf8-ca57d06f2519
tags/v1.2.90
Pierre Ossman 11 years ago
parent
commit
0b52ecc0cd

+ 113
- 703
unix/xserver/hw/vnc/Input.cc View File

@@ -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_)
{

+ 33
- 2
unix/xserver/hw/vnc/Input.h View File

@@ -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;

+ 589
- 0
unix/xserver/hw/vnc/InputCore.cc View File

@@ -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


+ 538
- 0
unix/xserver/hw/vnc/InputXKB.cc View File

@@ -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 */

+ 1
- 1
unix/xserver/hw/vnc/Makefile.am View File

@@ -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 \

Loading…
Cancel
Save