Checkpoint for improving X11 client key handling. Should prevent

unintentional Pointer_EnableKeys (i.e. generating NumLock press
and release around a shift press).
This commit is contained in:
crs 2003-06-22 15:01:44 +00:00
parent 5ca0e026ab
commit 92539f2ccc
2 changed files with 253 additions and 204 deletions

View File

@ -700,65 +700,120 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
const KeyCodeMask& entry = keyIndex->second; const KeyCodeMask& entry = keyIndex->second;
LOG((CLOG_DEBUG2 "keysym is 0x%08x", keysym)); LOG((CLOG_DEBUG2 "keysym is 0x%08x", keysym));
// note if the key is a modifier
unsigned int modifierBit;
unsigned int modifierIndex = keySymToModifierIndex(keysym);
if (modifierIndex != static_cast<unsigned int>(-1)) {
LOG((CLOG_DEBUG2 "keysym is modifier %d", modifierIndex));
modifierBit = (1 << modifierIndex);
}
else {
modifierBit = 0;
}
// if the key is a modifier and that modifier is already in the
// desired state then ignore the request since there's nothing
// to do. never ignore a toggle modifier on press or release,
// though.
if (modifierBit != 0) {
if (action == kRepeat) {
LOG((CLOG_DEBUG2 "ignore repeating modifier"));
return m_mask;
}
if (getBits(m_toggleModifierMask, modifierBit) == 0) {
if ((action == kPress && (m_mask & modifierBit) != 0) ||
(action == kRelease && (m_mask & modifierBit) == 0)) {
LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask));
return m_mask;
}
}
}
// sensitive notes the modifier keys that affect the synthesized
// key event. these modifiers must be in the expected state to
// get the correct keysym and we'll only try to match these
// modifiers.
//
// the shift and mode switch keys can modify any keycode. num
// lock and caps lock only affect certain keysyms and if a
// keysym is affected by num lock it is not affected by caps
// lock. no other modifiers have any effect.
//
// requested notes the modifiers requested by the server and
// desired notes the modifier state we ultimately want to match.
// only the bits in desired indicated by sensitive are relevant.
// we assign the num lock and caps lock bits here if relevant.
// we'll assign shift and mode switch later.
unsigned int sensitive = ShiftMask | m_modeSwitchMask;
unsigned int requested = maskToX(mask);
unsigned int desired = 0;
if (adjustForNumLock(keysym)) {
sensitive |= m_numLockMask;
desired = assignBits(desired, m_numLockMask, requested);
}
else if (adjustForCapsLock(keysym)) {
sensitive |= m_capsLockMask;
desired = assignBits(desired, m_capsLockMask, requested);
}
// we cannot be sensitive to the modifier we're pressing/releasing
sensitive = clearBits(sensitive, modifierBit);
// we can choose any of the available keycode/modifier states to // we can choose any of the available keycode/modifier states to
// generate our keysym. the most desireable is the one most // generate our keysym. the most desireable is the one most
// closely matching the input mask. determine the order we // closely matching the current mask. determine the order we
// should try modifier states, from best match to worst. this // should try modifier states, from best match to worst. this
// doesn't concern itself with whether or not a given modifier // doesn't concern itself with whether or not a given modifier
// state has an associated keycode. we'll just skip those later // state has an associated keycode. we'll just skip those later
// if necessary. // if necessary. default is none, shift, mode switch, shift +
// mode switch.
// default is none, shift, mode switch, shift + mode switch
unsigned int desired = maskToX(mask);
unsigned int index[4]; unsigned int index[4];
index[0] = 0; index[0] = 0;
index[1] = 1; index[1] = 1;
index[2] = 2; index[2] = 2;
index[3] = 3; index[3] = 3;
// if mode switch is active then 2 and 3 are better than 0 and 1
if (getBits(desired, m_modeSwitchMask) != 0) {
LOG((CLOG_DEBUG2 "mode switch desired"));
index[0] ^= 2;
index[1] ^= 2;
index[2] ^= 2;
index[3] ^= 2;
}
// if shift is active then 1 and 3 are better than 0 and 2. however, // if shift is active then 1 and 3 are better than 0 and 2. however,
// if the key is affected by NumLock and NumLock is active then 1 and // if the key is affected by NumLock and NumLock is active then 1 and
// 3 are better if shift is *not* down (because NumLock acts like // 3 are better if shift is *not* down (because NumLock acts like
// shift for those keysyms and shift cancels NumLock). similarly for // shift for those keysyms and shift cancels NumLock). similarly for
// keys affected by CapsLock. // keys affected by CapsLock. none of this is necessary if the key
bool desireShift = (getBits(desired, ShiftMask) != 0); // is itself shift.
bool invertShift = false; bool invertShift = false;
LOG((CLOG_DEBUG2 "desire shift: %s", desireShift ? "yes" : "no")); if (modifierBit != ShiftMask) {
if (adjustForNumLock(keysym)) { bool desireShift = (getBits(m_mask, ShiftMask) != 0);
LOG((CLOG_DEBUG2 "num lock sensitive")); if ((sensitive & m_numLockMask) != 0) {
if (m_numLockMask != 0) { LOG((CLOG_DEBUG2 "num lock sensitive"));
LOG((CLOG_DEBUG2 "we have a num lock")); if (getBits(m_mask, m_numLockMask) != 0) {
if (getBits(desired, m_numLockMask) != 0) { LOG((CLOG_DEBUG2 "num lock preferred, invert shift"));
LOG((CLOG_DEBUG2 "num lock desired, invert shift"));
invertShift = true; invertShift = true;
} }
} }
} else if ((sensitive & m_capsLockMask) != 0) {
else if (adjustForCapsLock(keysym)) { LOG((CLOG_DEBUG2 "caps lock sensitive"));
LOG((CLOG_DEBUG2 "caps lock sensitive")); if (getBits(m_mask, m_capsLockMask) != 0) {
if (m_capsLockMask != 0) { LOG((CLOG_DEBUG2 "caps lock preferred, invert shift"));
LOG((CLOG_DEBUG2 "we have a caps lock"));
if (getBits(desired, m_capsLockMask) != 0) {
LOG((CLOG_DEBUG2 "caps lock desired, invert shift"));
invertShift = true; invertShift = true;
} }
} }
if (desireShift != invertShift) {
LOG((CLOG_DEBUG2 "shift preferred"));
index[0] ^= 1;
index[1] ^= 1;
index[2] ^= 1;
index[3] ^= 1;
}
} }
if (desireShift != invertShift) {
LOG((CLOG_DEBUG2 "shift desired")); // if mode switch is active then 2 and 3 are better than 0 and 1,
index[0] ^= 1; // unless the key is itself mode switch.
index[1] ^= 1; if (modifierBit != m_modeSwitchMask &&
index[2] ^= 1; getBits(m_mask, m_modeSwitchMask) != 0) {
index[3] ^= 1; LOG((CLOG_DEBUG2 "mode switch preferred"));
index[0] ^= 2;
index[1] ^= 2;
index[2] ^= 2;
index[3] ^= 2;
} }
// find the first modifier state with a keycode we can generate. // find the first modifier state with a keycode we can generate.
@ -784,37 +839,13 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
keycode = entry.m_keycode[bestIndex]; keycode = entry.m_keycode[bestIndex];
LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode));
// note if the key is a modifier // FIXME -- can remove bits from sensitive if keycode doesn't have
ModifierMap::const_iterator modIndex = m_keycodeToModifier.find(keycode); // keysyms mapped to shift and/or mode switch.
unsigned int modifierBit = 0;
if (modIndex != m_keycodeToModifier.end()) {
LOG((CLOG_DEBUG2 "keysym is modifier %d", modIndex->second));
modifierBit = (1 << modIndex->second);
}
// if the key is a modifier and that modifier is already in the
// desired state then ignore the request since there's nothing
// to do. never ignore a toggle modifier on press or release,
// though.
if (modifierBit != 0) {
if (action == kRepeat) {
LOG((CLOG_DEBUG2 "ignore repeating modifier"));
return m_mask;
}
if (getBits(m_toggleModifierMask, modifierBit) == 0) {
if ((action == kPress && (m_mask & modifierBit) != 0) ||
(action == kRelease && (m_mask & modifierBit) == 0)) {
LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask));
return m_mask;
}
}
}
// bestIndex tells us if shift and mode switch should be on or off, // bestIndex tells us if shift and mode switch should be on or off,
// except if caps lock or num lock was down then we invert the sense // except if caps lock or num lock was down then we invert the sense
// of bestIndex's lowest bit. // of bestIndex's lowest bit.
// we must match both. // we must match both.
unsigned int required = ShiftMask | m_modeSwitchMask;
if (((bestIndex & 1) == 0) != invertShift) { if (((bestIndex & 1) == 0) != invertShift) {
desired = clearBits(desired, ShiftMask); desired = clearBits(desired, ShiftMask);
} }
@ -828,28 +859,8 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
desired = setBits(desired, m_modeSwitchMask); desired = setBits(desired, m_modeSwitchMask);
} }
// if the key is a modifier then remove it from the desired mask. // we now know what modifiers we want
// we'll be matching the modifiers in the desired mask then adding LOG((CLOG_DEBUG2 "modifiers: sensitive = 0x%04x, desired = 0x%04x, current = 0x%04x", sensitive, desired, m_mask));
// a key press or release for the keysym. if we don't clear the
// modifier bit from the desired mask we'll end up dealing with
// that key twice, once while matching modifiers and once while
// handling the keysym.
//
// note that instead of clearing the bit, we make it identical to
// the same bit in m_mask, meaning it's already in the right state.
desired = assignBits(desired, modifierBit, m_mask);
required = clearBits(required, modifierBit);
LOG((CLOG_DEBUG2 "desired = 0x%04x, current = 0x%04x", desired, m_mask));
// some modifiers never have an effect on keysym lookup. leave
// those modifiers alone by copying their state from m_mask to
// desired.
desired = assignBits(desired,
ControlMask |
m_altMask |
m_metaMask |
m_superMask |
m_scrollLockMask, m_mask);
// add the key events required to get to the modifier state // add the key events required to get to the modifier state
// necessary to generate an event yielding id. also save the // necessary to generate an event yielding id. also save the
@ -858,99 +869,100 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
// modify modifiers. // modify modifiers.
Keystrokes undo; Keystrokes undo;
Keystroke keystroke; Keystroke keystroke;
if (desired != m_mask) { for (unsigned int i = 0; i < 8; ++i) {
for (unsigned int i = 0; i < 8; ++i) { // skip modifiers we don't care about
unsigned int bit = (1 << i); unsigned int bit = (1 << i);
if (getBits(desired, bit) != getBits(m_mask, bit)) { if ((bit & sensitive) == 0) {
LOG((CLOG_DEBUG2 "fix modifier %d", i)); continue;
// get the keycode we're using for this modifier. if }
// there isn't one then bail if the modifier is required
// or ignore it if not required.
KeyCode modifierKey = m_modifierToKeycode[i];
if (modifierKey == 0) {
LOG((CLOG_DEBUG2 "no key mapped to modifier 0x%04x", bit));
if (getBits(required, bit) != 0) {
keys.clear();
return m_mask;
}
else {
continue;
}
}
keystroke.m_keycode = modifierKey; // skip modifiers that are correct
keystroke.m_repeat = false; if (getBits(desired, bit) == getBits(m_mask, bit)) {
if (getBits(desired, bit)) { continue;
// modifier is not active but should be. if the }
// modifier is a toggle then toggle it on with a
// press/release, otherwise activate it with a
// press. use the first keycode for the modifier.
LOG((CLOG_DEBUG2 "modifier 0x%04x is not active", bit));
if (getBits(m_toggleModifierMask, bit) != 0) {
LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit));
if ((bit == m_capsLockMask && m_capsLockHalfDuplex) ||
(bit == m_numLockMask && m_numLockHalfDuplex)) {
keystroke.m_press = True;
keys.push_back(keystroke);
keystroke.m_press = False;
undo.push_back(keystroke);
}
else {
keystroke.m_press = True;
keys.push_back(keystroke);
keystroke.m_press = False;
keys.push_back(keystroke);
undo.push_back(keystroke);
keystroke.m_press = True;
undo.push_back(keystroke);
}
}
else {
keystroke.m_press = True;
keys.push_back(keystroke);
keystroke.m_press = False;
undo.push_back(keystroke);
}
}
LOG((CLOG_DEBUG2 "fix modifier %d", i));
// get the keycode we're using for this modifier. if
// there isn't one then bail if the modifier is required
// or ignore it if not required.
KeyCode modifierKey = m_modifierToKeycode[i];
if (modifierKey == 0) {
LOG((CLOG_DEBUG2 "no key mapped to modifier 0x%04x", bit));
keys.clear();
return m_mask;
}
keystroke.m_keycode = modifierKey;
keystroke.m_repeat = false;
if (getBits(desired, bit)) {
// modifier is not active but should be. if the
// modifier is a toggle then toggle it on with a
// press/release, otherwise activate it with a
// press. use the first keycode for the modifier.
LOG((CLOG_DEBUG2 "modifier 0x%04x is not active but should be", bit));
if (getBits(m_toggleModifierMask, bit) != 0) {
LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit));
if ((bit == m_capsLockMask && m_capsLockHalfDuplex) ||
(bit == m_numLockMask && m_numLockHalfDuplex)) {
keystroke.m_press = True;
keys.push_back(keystroke);
keystroke.m_press = False;
undo.push_back(keystroke);
}
else { else {
// modifier is active but should not be. if the keystroke.m_press = True;
// modifier is a toggle then toggle it off with a keys.push_back(keystroke);
// press/release, otherwise deactivate it with a keystroke.m_press = False;
// release. we must check each keycode for the keys.push_back(keystroke);
// modifier if not a toggle. undo.push_back(keystroke);
LOG((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); keystroke.m_press = True;
if (getBits(m_toggleModifierMask, bit) != 0) { undo.push_back(keystroke);
LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); }
if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || }
(bit == m_numLockMask && m_numLockHalfDuplex)) { else {
keystroke.m_press = False; keystroke.m_press = True;
keys.push_back(keystroke); keys.push_back(keystroke);
keystroke.m_press = True; keystroke.m_press = False;
undo.push_back(keystroke); undo.push_back(keystroke);
} }
else { }
keystroke.m_press = True;
keys.push_back(keystroke); else {
keystroke.m_press = False; // modifier is active but should not be. if the
keys.push_back(keystroke); // modifier is a toggle then toggle it off with a
undo.push_back(keystroke); // press/release, otherwise deactivate it with a
keystroke.m_press = True; // release. we must check each keycode for the
undo.push_back(keystroke); // modifier if not a toggle.
} LOG((CLOG_DEBUG2 "modifier 0x%04x is active", bit));
} if (getBits(m_toggleModifierMask, bit) != 0) {
else { LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit));
for (unsigned int j = 0; j < m_keysPerModifier; ++j) { if ((bit == m_capsLockMask && m_capsLockHalfDuplex) ||
const KeyCode key = (bit == m_numLockMask && m_numLockHalfDuplex)) {
m_modifierToKeycodes[i * m_keysPerModifier + j]; keystroke.m_press = False;
if (key != 0 && m_keys[key]) { keys.push_back(keystroke);
keystroke.m_keycode = key; keystroke.m_press = True;
keystroke.m_press = False; undo.push_back(keystroke);
keys.push_back(keystroke); }
keystroke.m_press = True; else {
undo.push_back(keystroke); keystroke.m_press = True;
} keys.push_back(keystroke);
} keystroke.m_press = False;
keys.push_back(keystroke);
undo.push_back(keystroke);
keystroke.m_press = True;
undo.push_back(keystroke);
}
}
else {
for (unsigned int j = 0; j < m_keysPerModifier; ++j) {
const KeyCode key =
m_modifierToKeycodes[i * m_keysPerModifier + j];
if (key != 0 && m_keys[key]) {
keystroke.m_keycode = key;
keystroke.m_press = False;
keys.push_back(keystroke);
keystroke.m_press = True;
undo.push_back(keystroke);
} }
} }
} }
@ -1016,7 +1028,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
// scan those keys to see if any (except keycode) are pressed. // scan those keys to see if any (except keycode) are pressed.
bool down = false; bool down = false;
for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) { for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) {
KeyCode modKeycode = m_modifierToKeycodes[modIndex->second * KeyCode modKeycode = m_modifierToKeycodes[modifierIndex *
m_keysPerModifier + j]; m_keysPerModifier + j];
if (modKeycode != 0 && modKeycode != keycode) { if (modKeycode != 0 && modKeycode != keycode) {
down = m_keys[modKeycode]; down = m_keys[modKeycode];
@ -1026,9 +1038,9 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
mask = clearBits(mask, modifierBit); mask = clearBits(mask, modifierBit);
} }
} }
LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask));
} }
LOG((CLOG_DEBUG2 "final mask: 0x%04x", mask));
return mask; return mask;
} }
@ -1307,26 +1319,6 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display)
XFree(keysyms); XFree(keysyms);
} }
unsigned int
CXWindowsSecondaryScreen::indexToModifierMask(int index) const
{
assert(index >= 0 && index <= 3);
switch (index) {
case 0:
return 0;
case 1:
return ShiftMask | LockMask;
case 2:
return m_modeSwitchMask;
case 3:
return ShiftMask | LockMask | m_modeSwitchMask;
}
}
void void
CXWindowsSecondaryScreen::updateModifierMap(Display* display) CXWindowsSecondaryScreen::updateModifierMap(Display* display)
{ {
@ -1343,6 +1335,13 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display)
m_numLockMask = 0; m_numLockMask = 0;
m_capsLockMask = 0; m_capsLockMask = 0;
m_scrollLockMask = 0; m_scrollLockMask = 0;
m_altIndex = static_cast<unsigned int>(-1);
m_metaIndex = static_cast<unsigned int>(-1);
m_superIndex = static_cast<unsigned int>(-1);
m_modeSwitchIndex = static_cast<unsigned int>(-1);
m_numLockIndex = static_cast<unsigned int>(-1);
m_capsLockIndex = static_cast<unsigned int>(-1);
m_scrollLockIndex = static_cast<unsigned int>(-1);
m_keysPerModifier = keymap->max_keypermod; m_keysPerModifier = keymap->max_keypermod;
m_modifierToKeycode.clear(); m_modifierToKeycode.clear();
m_modifierToKeycode.resize(8); m_modifierToKeycode.resize(8);
@ -1368,9 +1367,6 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display)
m_modifierToKeycode[i] = keycode; m_modifierToKeycode[i] = keycode;
} }
// save in keycode to modifier
m_keycodeToModifier.insert(std::make_pair(keycode, i));
// save bit in all-modifiers mask // save bit in all-modifiers mask
m_modifierMask |= bit; m_modifierMask |= bit;
@ -1384,33 +1380,41 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display)
switch (keysym) { switch (keysym) {
case XK_Alt_L: case XK_Alt_L:
case XK_Alt_R: case XK_Alt_R:
m_altIndex = i;
m_altMask |= bit; m_altMask |= bit;
break; break;
case XK_Meta_L: case XK_Meta_L:
case XK_Meta_R: case XK_Meta_R:
m_metaIndex = i;
m_metaMask |= bit; m_metaMask |= bit;
break; break;
case XK_Super_L: case XK_Super_L:
case XK_Super_R: case XK_Super_R:
m_superIndex = i;
m_superMask |= bit; m_superMask |= bit;
break; break;
case XK_Mode_switch: case XK_Mode_switch:
m_modeSwitchIndex = i;
m_modeSwitchMask |= bit; m_modeSwitchMask |= bit;
break; break;
case XK_Num_Lock: case XK_Num_Lock:
m_numLockIndex = i;
m_numLockMask |= bit; m_numLockMask |= bit;
break; break;
case XK_Caps_Lock: case XK_Caps_Lock:
m_capsLockIndex = i;
m_capsLockMask |= bit; m_capsLockMask |= bit;
break; break;
case XK_Scroll_Lock: case XK_Scroll_Lock:
m_scrollLockIndex = i;
m_scrollLockMask |= bit; m_scrollLockMask |= bit;
break;
} }
} }
} }
@ -1418,6 +1422,46 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display)
XFreeModifiermap(keymap); XFreeModifiermap(keymap);
} }
unsigned int
CXWindowsSecondaryScreen::keySymToModifierIndex(KeySym keysym) const
{
switch (keysym) {
case XK_Shift_L:
case XK_Shift_R:
return 0;
case XK_Control_L:
case XK_Control_R:
return 2;
case XK_Alt_L:
case XK_Alt_R:
return m_altIndex;
case XK_Meta_L:
case XK_Meta_R:
return m_metaIndex;
case XK_Super_L:
case XK_Super_R:
return m_superIndex;
case XK_Mode_switch:
return m_modeSwitchIndex;
case XK_Num_Lock:
return m_numLockIndex;
case XK_Caps_Lock:
return m_capsLockIndex;
case XK_Scroll_Lock:
return m_scrollLockIndex;
}
return static_cast<unsigned int>(-1);
}
void void
CXWindowsSecondaryScreen::toggleKey(Display* display, CXWindowsSecondaryScreen::toggleKey(Display* display,
KeySym keysym, unsigned int mask) KeySym keysym, unsigned int mask)

View File

@ -93,7 +93,6 @@ private:
typedef std::vector<KeyCode> KeyCodes; typedef std::vector<KeyCode> KeyCodes;
typedef std::map<KeySym, KeyCodeMask> KeyCodeMap; typedef std::map<KeySym, KeyCodeMask> KeyCodeMap;
typedef KeyCodeMap::const_iterator KeyCodeIndex; typedef KeyCodeMap::const_iterator KeyCodeIndex;
typedef std::map<KeyCode, unsigned int> ModifierMap;
typedef std::map<KeyButton, KeyCode> ServerKeyMap; typedef std::map<KeyButton, KeyCode> ServerKeyMap;
unsigned int mapButton(ButtonID button) const; unsigned int mapButton(ButtonID button) const;
@ -108,7 +107,7 @@ private:
void updateKeycodeMap(Display* display); void updateKeycodeMap(Display* display);
void updateModifiers(Display* display); void updateModifiers(Display* display);
void updateModifierMap(Display* display); void updateModifierMap(Display* display);
unsigned int indexToModifierMask(int index) const; unsigned int keySymToModifierIndex(KeySym) const;
void toggleKey(Display*, KeySym, unsigned int mask); void toggleKey(Display*, KeySym, unsigned int mask);
static bool isToggleKeysym(KeySym); static bool isToggleKeysym(KeySym);
@ -162,14 +161,20 @@ private:
unsigned int m_capsLockMask; unsigned int m_capsLockMask;
unsigned int m_scrollLockMask; unsigned int m_scrollLockMask;
// modifier indices
unsigned int m_altIndex;
unsigned int m_metaIndex;
unsigned int m_superIndex;
unsigned int m_modeSwitchIndex;
unsigned int m_numLockIndex;
unsigned int m_capsLockIndex;
unsigned int m_scrollLockIndex;
// map X modifier key indices to the key codes bound to them // map X modifier key indices to the key codes bound to them
unsigned int m_keysPerModifier; unsigned int m_keysPerModifier;
KeyCodes m_modifierToKeycode; KeyCodes m_modifierToKeycode;
KeyCodes m_modifierToKeycodes; KeyCodes m_modifierToKeycodes;
// maps keycodes to modifier indices
ModifierMap m_keycodeToModifier;
// map server key buttons to local keycodes // map server key buttons to local keycodes
ServerKeyMap m_serverKeyMap; ServerKeyMap m_serverKeyMap;