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,66 +700,121 @@ 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);
if ((sensitive & m_numLockMask) != 0) {
LOG((CLOG_DEBUG2 "num lock sensitive")); LOG((CLOG_DEBUG2 "num lock sensitive"));
if (m_numLockMask != 0) { if (getBits(m_mask, m_numLockMask) != 0) {
LOG((CLOG_DEBUG2 "we have a num lock")); LOG((CLOG_DEBUG2 "num lock preferred, invert shift"));
if (getBits(desired, m_numLockMask) != 0) {
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 (m_capsLockMask != 0) { if (getBits(m_mask, m_capsLockMask) != 0) {
LOG((CLOG_DEBUG2 "we have a caps lock")); LOG((CLOG_DEBUG2 "caps lock preferred, invert shift"));
if (getBits(desired, m_capsLockMask) != 0) {
LOG((CLOG_DEBUG2 "caps lock desired, invert shift"));
invertShift = true; invertShift = true;
} }
} }
}
if (desireShift != invertShift) { if (desireShift != invertShift) {
LOG((CLOG_DEBUG2 "shift desired")); LOG((CLOG_DEBUG2 "shift preferred"));
index[0] ^= 1; index[0] ^= 1;
index[1] ^= 1; index[1] ^= 1;
index[2] ^= 1; index[2] ^= 1;
index[3] ^= 1; index[3] ^= 1;
} }
}
// if mode switch is active then 2 and 3 are better than 0 and 1,
// unless the key is itself mode switch.
if (modifierBit != m_modeSwitchMask &&
getBits(m_mask, m_modeSwitchMask) != 0) {
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.
// note that if m_modeSwitchMask is 0 then we can't generate // note that if m_modeSwitchMask is 0 then we can't 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,10 +869,18 @@ 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) {
continue;
}
// skip modifiers that are correct
if (getBits(desired, bit) == getBits(m_mask, bit)) {
continue;
}
LOG((CLOG_DEBUG2 "fix modifier %d", i)); LOG((CLOG_DEBUG2 "fix modifier %d", i));
// get the keycode we're using for this modifier. if // get the keycode we're using for this modifier. if
// there isn't one then bail if the modifier is required // there isn't one then bail if the modifier is required
@ -869,14 +888,9 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
KeyCode modifierKey = m_modifierToKeycode[i]; KeyCode modifierKey = m_modifierToKeycode[i];
if (modifierKey == 0) { if (modifierKey == 0) {
LOG((CLOG_DEBUG2 "no key mapped to modifier 0x%04x", bit)); LOG((CLOG_DEBUG2 "no key mapped to modifier 0x%04x", bit));
if (getBits(required, bit) != 0) {
keys.clear(); keys.clear();
return m_mask; return m_mask;
} }
else {
continue;
}
}
keystroke.m_keycode = modifierKey; keystroke.m_keycode = modifierKey;
keystroke.m_repeat = false; keystroke.m_repeat = false;
@ -885,7 +899,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
// modifier is a toggle then toggle it on with a // modifier is a toggle then toggle it on with a
// press/release, otherwise activate it with a // press/release, otherwise activate it with a
// press. use the first keycode for the modifier. // press. use the first keycode for the modifier.
LOG((CLOG_DEBUG2 "modifier 0x%04x is not active", bit)); LOG((CLOG_DEBUG2 "modifier 0x%04x is not active but should be", bit));
if (getBits(m_toggleModifierMask, bit) != 0) { if (getBits(m_toggleModifierMask, bit) != 0) {
LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit));
if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || if ((bit == m_capsLockMask && m_capsLockHalfDuplex) ||
@ -954,8 +968,6 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
} }
} }
} }
}
}
// note if the press of a half-duplex key should be treated as a release // note if the press of a half-duplex key should be treated as a release
if (isHalfDuplex && getBits(m_mask, modifierBit) != 0) { if (isHalfDuplex && getBits(m_mask, modifierBit) != 0) {
@ -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;