Rewrote handling of key press on X11 client; it should be much

more robust now.  Also added handling of Super modifier key and
changed windows keys to map to Super instead of Meta, which is
the default on my keyboard.
This commit is contained in:
crs 2002-09-14 12:07:02 +00:00
parent 10bbf6f824
commit 4586f88188
7 changed files with 488 additions and 227 deletions

View File

@ -478,8 +478,9 @@ CMSWindowsSecondaryScreen::updateKeys()
if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) {
m_mask |= KeyModifierAlt;
}
// note -- no keys for KeyModifierMeta
if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) {
m_mask |= KeyModifierMeta;
m_mask |= KeyModifierSuper;
}
if ((m_keys[VK_CAPITAL] & 0x01) != 0) {
m_mask |= KeyModifierCapsLock;
@ -583,8 +584,9 @@ static const UINT g_mapEF00[] =
/* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL,
/* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, VK_LWIN|0x100,
/* 0xe8 */ VK_RWIN|0x100, VK_LMENU, VK_RMENU|0x100, 0, 0, 0, 0, 0,
/* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, 0,
/* 0xe8 */ 0, VK_LMENU, VK_RMENU|0x100, VK_LWIN|0x100,
/* 0xec */ VK_RWIN|0x100, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100
};
@ -655,7 +657,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey,
// get output mask. default output mask carries over the current
// toggle modifier states and includes desired shift, control, alt,
// and meta states.
// meta, and super states.
KeyModifierMask outMask = (m_mask &
(KeyModifierCapsLock |
KeyModifierNumLock |
@ -664,7 +666,8 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey,
(KeyModifierShift |
KeyModifierControl |
KeyModifierAlt |
KeyModifierMeta));
KeyModifierMeta |
KeyModifierSuper));
// set control and alt if mode shift (AltGr) is requested
if ((mask & KeyModifierModeSwitch) != 0) {
@ -760,7 +763,8 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey,
{ KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false },
{ KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false },
{ KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false },
{ KeyModifierMeta, VK_LWIN | 0x100, VK_RWIN | 0x100, false },
// note -- no keys for KeyModifierMeta
{ KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false },
{ KeyModifierCapsLock, VK_CAPITAL, 0, true },
{ KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true },
{ KeyModifierScrollLock,VK_SCROLL, 0, true }

View File

@ -36,6 +36,47 @@
# endif
#endif
//
// utility functions
//
inline
static
unsigned int getBits(unsigned int src, unsigned int mask)
{
return src & mask;
}
inline
static
unsigned int setBits(unsigned int src, unsigned int mask)
{
return src | mask;
}
inline
static
unsigned int clearBits(unsigned int src, unsigned int mask)
{
return src & ~mask;
}
inline
static
unsigned int flipBits(unsigned int src, unsigned int mask)
{
return src ^ mask;
}
inline
static
unsigned int assignBits(unsigned int src,
unsigned int mask, unsigned int value)
{
return setBits(clearBits(src, mask), clearBits(value, ~mask));
}
//
// CXWindowsSecondaryScreen
//
@ -406,27 +447,154 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
return m_mask;
}
// lookup the a keycode for this key id. also return the
// key modifier mask required.
unsigned int outMask;
if (!findKeyCode(keycode, outMask, id, maskToX(mask))) {
// we cannot generate the desired keysym because no key
// maps to that keysym. just return the current mask.
log((CLOG_DEBUG2 "no keycode for KeyID %d modifiers 0x%04x", id, mask));
// convert the id to a keysym and adjust the mask if necessary
unsigned int outMask = m_mask;
KeyCodeIndex keyIndex = findKey(id, outMask);
if (keyIndex == noKey()) {
// cannot convert id to keysym
return m_mask;
}
log((CLOG_DEBUG2 "keysym %d -> KeyID %d modifiers 0x%04x", id, keycode, outMask));
// if we cannot match the modifier mask then don't return any
// keys and just return the current mask.
if ((outMask & m_modifierMask) != outMask) {
log((CLOG_DEBUG2 "cannot match modifiers to mask 0x%04x", m_modifierMask));
// get the keysym we're trying to generate and possible keycodes
KeySym keysym = keyIndex->first;
const KeyCodeMask& entry = keyIndex->second;
// we can choose any of the available keycode/modifier states to
// generate our keysym. the most desireable is the one most
// closely matching the input mask. determine the order we
// should try modifier states, from best match to worst. this
// doesn't concern itself with whether or not a given modifier
// state has an associated keycode. we'll just skip those later
// if necessary.
// default is none, shift, mode switch, shift + mode switch
unsigned int desired = maskToX(mask);
unsigned int index[4];
index[0] = 0;
index[1] = 1;
index[2] = 2;
index[3] = 3;
// if mode switch is active then 2 and 3 are better than 0 and 1
if (getBits(desired, m_modeSwitchMask) != 0) {
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 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
// shift for those keysyms and shift cancels NumLock). similarly for
// keys affected by CapsLock.
bool desireShift = (getBits(desired, ShiftMask) != 0);
bool invertShift = false;
log((CLOG_DEBUG1 "desire shift 1: %s", desireShift?"yes":"no"));
if (adjustForNumLock(keysym)) {
log((CLOG_DEBUG1 "num lock sensitive"));
if (m_numLockMask != 0) {
log((CLOG_DEBUG1 "we have num lock"));
if (getBits(desired, m_numLockMask) != 0) {
log((CLOG_DEBUG1 "num lock desired, invert shift"));
invertShift = true;
}
}
}
else if (adjustForCapsLock(keysym)) {
log((CLOG_DEBUG1 "caps lock sensitive"));
if (m_capsLockMask != 0) {
log((CLOG_DEBUG1 "we have caps lock"));
if (getBits(desired, m_capsLockMask) != 0) {
log((CLOG_DEBUG1 "caps lock desired, invert shift"));
invertShift = true;
}
}
}
log((CLOG_DEBUG1 "desire shift 2: %s", desireShift?"yes":"no"));
if (desireShift != invertShift) {
index[0] ^= 1;
index[1] ^= 1;
index[2] ^= 1;
index[3] ^= 1;
}
// find the first modifier state with a keycode we can generate.
// note that if m_modeSwitchMask is 0 then we can't generate
// m_keycode[2] and m_keycode[3].
unsigned int bestIndex;
for (bestIndex = 0; bestIndex < 4; ++bestIndex) {
if (entry.m_keycode[index[bestIndex]] != 0) {
if (index[bestIndex] < 2 || m_modeSwitchMask != 0) {
bestIndex = index[bestIndex];
break;
}
}
}
if (bestIndex == 4) {
// no keycode/modifiers to generate the keysym
return m_mask;
}
// get the keycode
keycode = entry.m_keycode[bestIndex];
log((CLOG_DEBUG1 "bestIndex = %d, keycode = %d", bestIndex, keycode));
// note if the key is a modifier
ModifierMap::const_iterator index = m_keycodeToModifier.find(keycode);
const bool isModifier = (index != m_keycodeToModifier.end());
ModifierMap::const_iterator modIndex = m_keycodeToModifier.find(keycode);
unsigned int modifierBit = 0;
if (modIndex != m_keycodeToModifier.end()) {
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_DEBUG1 "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_DEBUG1 "modifier in proper state: 0x%04x", m_mask));
return m_mask;
}
}
}
// 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
// of bestIndex's lowest bit.
// we must match both.
unsigned int required = ShiftMask | m_modeSwitchMask;
if (((bestIndex & 1) == 0) != invertShift) {
desired = clearBits(desired, ShiftMask);
}
else {
desired = setBits(desired, ShiftMask);
}
if ((bestIndex & 2) == 0) {
desired = clearBits(desired, m_modeSwitchMask);
}
else {
desired = setBits(desired, m_modeSwitchMask);
}
// if the key is a modifier then remove it from the desired mask.
// we'll be matching the modifiers in the desired mask then adding
// 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_DEBUG1 "desired = 0x%04x, current = 0x%04x", desired, m_mask));
// add the key events required to get to the modifier state
// necessary to generate an event yielding id. also save the
@ -435,33 +603,35 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
// modify modifiers.
Keystrokes undo;
Keystroke keystroke;
if (outMask != m_mask && !isModifier) {
if (desired != m_mask) {
for (unsigned int i = 0; i < 8; ++i) {
unsigned int bit = (1 << i);
if ((outMask & bit) != (m_mask & bit)) {
// get list of keycodes for the modifier. if there isn't
// one then there's no key mapped to this modifier and we
// can't generate the desired key so bail.
const KeyCode* modifierKeys =
&m_modifierToKeycode[i * m_keysPerModifier];
KeyCode modifierKey = modifierKeys[0];
if (modifierKey == 0) {
modifierKey = modifierKeys[1];
}
if (getBits(desired, bit) != getBits(m_mask, bit)) {
log((CLOG_DEBUG1 "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_DEBUG1 "no key mapped to modifier 0x%04x", bit));
if (getBits(required, bit) != 0) {
keys.clear();
return m_mask;
}
else {
continue;
}
}
keystroke.m_keycode = modifierKey;
keystroke.m_repeat = false;
if ((outMask & bit) != 0) {
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", bit));
if ((bit & m_toggleModifierMask) != 0) {
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)) {
@ -495,7 +665,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
// release. we must check each keycode for the
// modifier if not a toggle.
log((CLOG_DEBUG2 "modifier 0x%04x is active", bit));
if ((bit & m_toggleModifierMask) != 0) {
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)) {
@ -516,7 +686,8 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
}
else {
for (unsigned int j = 0; j < m_keysPerModifier; ++j) {
const KeyCode key = modifierKeys[j];
const KeyCode key =
m_modifierToKeycodes[i * m_keysPerModifier + j];
if (key != 0 && m_keys[key]) {
keystroke.m_keycode = key;
keystroke.m_press = False;
@ -532,7 +703,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
}
// note if the press of a half-duplex key should be treated as a release
if (isHalfDuplex && (m_mask & (1 << index->second)) != 0) {
if (isHalfDuplex && getBits(m_mask, modifierBit) != 0) {
action = kRelease;
}
@ -568,197 +739,44 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode,
}
// if the key is a modifier key then compute the modifier map after
// this key is pressed or released. if repeating then ignore.
// this key is pressed or released.
mask = m_mask;
if (isModifier && action != kRepeat) {
// get modifier
const unsigned int modifierBit = (1 << index->second);
if (modifierBit != 0) {
// can't be repeating if we've gotten here
assert(action != kRepeat);
// toggle keys modify the state on release. other keys set the
// bit on press and clear the bit on release. if half-duplex
// then toggle each time we get here.
if ((modifierBit & m_toggleModifierMask) != 0) {
if (getBits(m_toggleModifierMask, modifierBit) != 0) {
if (isHalfDuplex || action == kRelease) {
mask ^= modifierBit;
mask = flipBits(mask, modifierBit);
}
}
else if (action == kPress) {
mask |= modifierBit;
mask = setBits(mask, modifierBit);
}
else {
else if (action == kRelease) {
// can't reset bit until all keys that set it are released.
// scan those keys to see if any (except keycode) are pressed.
bool down = false;
const KeyCode* modifierKeys = &m_modifierToKeycode[
index->second * m_keysPerModifier];
for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) {
if (modifierKeys[j] != 0 && m_keys[modifierKeys[j]])
down = true;
KeyCode modKeycode = m_modifierToKeycodes[modIndex->second *
m_keysPerModifier + j];
if (modKeycode != 0 && modKeycode != keycode) {
down = m_keys[modKeycode];
}
}
if (!down) {
mask = clearBits(mask, modifierBit);
}
if (!down)
mask &= ~modifierBit;
}
}
log((CLOG_DEBUG1 "final mask: 0x%04x", mask));
return mask;
}
bool
CXWindowsSecondaryScreen::findKeyCode(KeyCode& keycode,
unsigned int& maskOut, KeyID id, unsigned int maskIn) const
{
// convert id to keysym
KeySym keysym = 0;
switch (id & 0xffffff00) {
case 0x0000:
// Latin-1
keysym = static_cast<KeySym>(id);
break;
case 0xee00:
// ISO 9995 Function and Modifier Keys
if (id == kKeyLeftTab) {
keysym = XK_ISO_Left_Tab;
}
break;
case 0xef00:
// MISCELLANY
keysym = static_cast<KeySym>(id - 0xef00 + 0xff00);
break;
}
// fail if unknown key
if (keysym == 0) {
return false;
}
// if kKeyTab is requested with shift active then try XK_ISO_Left_Tab
// instead. if that doesn't work, we'll fall back to XK_Tab with
// shift active. this is to handle primary screens that don't map
// XK_ISO_Left_Tab sending events to secondary screens that do.
if (keysym == XK_Tab && (maskIn & ShiftMask) != 0) {
keysym = XK_ISO_Left_Tab;
maskIn &= ~ShiftMask;
}
// find a keycode to generate id. XKeysymToKeycode() almost does
// what we need but won't tell us which index to use with the
// keycode. return false if there's no keycode to generate id.
KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym);
if (index == m_keycodeMap.end()) {
// try backup keysym for certain keys (particularly the numpad
// keys since most laptops don't have a separate numpad and the
// numpad overlaying the main keyboard may not have movement
// key bindings).
switch (keysym) {
case XK_KP_Home:
keysym = XK_Home;
break;
case XK_KP_Left:
keysym = XK_Left;
break;
case XK_KP_Up:
keysym = XK_Up;
break;
case XK_KP_Right:
keysym = XK_Right;
break;
case XK_KP_Down:
keysym = XK_Down;
break;
case XK_KP_Prior:
keysym = XK_Prior;
break;
case XK_KP_Next:
keysym = XK_Next;
break;
case XK_KP_End:
keysym = XK_End;
break;
case XK_KP_Insert:
keysym = XK_Insert;
break;
case XK_KP_Delete:
keysym = XK_Delete;
break;
case XK_ISO_Left_Tab:
keysym = XK_Tab;
maskIn |= ShiftMask;
break;
default:
return false;
}
index = m_keycodeMap.find(keysym);
if (index == m_keycodeMap.end()) {
return false;
}
}
// save the keycode
keycode = index->second.m_keycode;
// compute output mask. that's the set of modifiers that need to
// be enabled when the keycode event is encountered in order to
// generate the keysym and match maskIn. it's possible that
// maskIn wants, say, a shift key to be down but that would make
// it impossible to generate the keysym. in that case we must
// override maskIn. this is complicated by caps/shift-lock and
// num-lock.
maskOut = (maskIn & ~index->second.m_keyMaskMask);
log((CLOG_DEBUG2 "maskIn(0x%04x) & ~maskMask(0x%04x) -> 0x%04x", maskIn, index->second.m_keyMaskMask, maskOut));
if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)) {
if ((m_mask & m_numLockMask) != 0) {
maskOut &= ~index->second.m_keyMask;
maskOut |= m_numLockMask;
log((CLOG_DEBUG2 "keypad key: & ~mask(0x%04x) | numLockMask(0x%04x) -> 0x%04x", index->second.m_keyMask, m_numLockMask, maskOut));
}
else {
maskOut |= index->second.m_keyMask;
maskOut &= ~m_numLockMask;
log((CLOG_DEBUG2 "keypad key: | mask(0x%04x) & ~numLockMask(0x%04x) -> 0x%04x", index->second.m_keyMask, m_numLockMask, maskOut));
}
}
else {
unsigned int maskShift = (index->second.m_keyMask & ShiftMask);
log((CLOG_DEBUG2 "maskShift = 0x%04x", maskShift));
if (maskShift != 0 && (m_mask & m_capsLockMask) != 0) {
// shift and capsLock cancel out for keysyms subject to
// case conversion but not for keys with shifted
// characters that are not case conversions. see if
// case conversion is necessary.
KeySym lKey, uKey;
XConvertCase(keysym, &lKey, &uKey);
if (lKey != uKey) {
log((CLOG_DEBUG2 "case convertable, shift && capsLock -> caps lock"));
maskShift = m_capsLockMask;
}
else {
log((CLOG_DEBUG2 "case unconvertable, shift && capsLock -> shift, caps lock"));
maskShift |= m_capsLockMask;
}
}
log((CLOG_DEBUG2 "maskShift = 0x%04x", maskShift));
maskOut |= maskShift;
maskOut |= (index->second.m_keyMask & ~(ShiftMask | LockMask));
log((CLOG_DEBUG2 "| maskShift(0x%04x) | other (0x%04x) -> 0x%04x", maskShift, (index->second.m_keyMask & ~(ShiftMask | LockMask)), maskOut));
}
return true;
}
void
CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count)
{
@ -817,6 +835,9 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const
if (inMask & KeyModifierMeta) {
outMask |= m_metaMask;
}
if (inMask & KeyModifierSuper) {
outMask |= m_superMask;
}
if (inMask & KeyModifierModeSwitch) {
outMask |= m_modeSwitchMask;
}
@ -902,7 +923,7 @@ CXWindowsSecondaryScreen::updateModifiers(Display* display)
const unsigned int bit = (1 << i);
if ((bit & m_toggleModifierMask) == 0) {
for (unsigned int j = 0; j < m_keysPerModifier; ++j) {
if (m_keys[m_modifierToKeycode[i * m_keysPerModifier + j]])
if (m_keys[m_modifierToKeycodes[i * m_keysPerModifier + j]])
m_mask |= bit;
}
}
@ -917,7 +938,10 @@ void
CXWindowsSecondaryScreen::updateKeycodeMap(Display* display)
{
// there are up to 4 keysyms per keycode
static const int maxKeysyms = 4;
static const unsigned int maxKeysyms = 4;
// table for counting 1 bits
static const int s_numBits[maxKeysyms] = { 0, 1, 1, 2 };
// get the number of keycodes
int minKeycode, maxKeycode;
@ -931,24 +955,71 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display)
&keysymsPerKeycode);
// we only understand up to maxKeysyms keysyms per keycodes
int numKeysyms = keysymsPerKeycode;
unsigned int numKeysyms = keysymsPerKeycode;
if (numKeysyms > maxKeysyms) {
numKeysyms = maxKeysyms;
}
// initialize
KeyCodeMask entry;
// KeyCodeMask entry;
m_keycodeMap.clear();
// insert keys
for (int i = 0; i < numKeycodes; ++i) {
// compute mask over all mapped keysyms. if a keycode has, say,
// no shifted keysym then we can ignore the shift state when
// synthesizing an event to generate it.
unsigned int globalMask = 0;
for (unsigned int j = 0; j < numKeysyms; ++j) {
const KeySym keysym = keysyms[i * keysymsPerKeycode + j];
if (keysym != NoSymbol) {
globalMask |= j;
}
}
// map each keysym to it's keycode/modifier mask
for (unsigned int j = 0; j < numKeysyms; ++j) {
// get keysym
KeySym keysym = keysyms[i * keysymsPerKeycode + j];
// get modifier mask required for this keysym. note that
// a keysym of NoSymbol means that a keysym using fewer
// modifiers would be generated using these modifiers.
// for example, given
// keycode 86 = KP_Add
// then we'll generate KP_Add regardless of the modifiers.
// we add an entry for that keysym for these modifiers.
unsigned int index = j;
if (keysym == NoSymbol && (index == 1 || index == 3)) {
// shift doesn't matter
index = index - 1;
keysym = keysyms[i * keysymsPerKeycode + index];
}
if (keysym == NoSymbol && index == 2) {
// mode switch doesn't matter
index = 0;
keysym = keysyms[i * keysymsPerKeycode + index];
}
if (keysym == NoSymbol && index == 0) {
// no symbols at all for this keycode
continue;
}
// look it up, creating a new entry if necessary
KeyCodeMask& entry = m_keycodeMap[keysym];
// save keycode for keysym and modifiers
entry.m_keycode[j] = static_cast<KeyCode>(minKeycode + i);
}
/*
// compute mask over all mapped keysyms. if a keycode has, say,
// no shifted keysym then we can ignore the shift state when
// synthesizing an event to generate it.
entry.m_keyMaskMask = 0;
for (int j = 0; j < numKeysyms; ++j) {
const KeySym keySym = keysyms[i * keysymsPerKeycode + j];
if (keySym != NoSymbol) {
const KeySym keysym = keysyms[i * keysymsPerKeycode + j];
if (keysym != NoSymbol) {
entry.m_keyMaskMask |= indexToModifierMask(j);
}
}
@ -956,12 +1027,15 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display)
// add entries for this keycode
entry.m_keycode = static_cast<KeyCode>(minKeycode + i);
for (int j = 0; j < numKeysyms; ++j) {
const KeySym keySym = keysyms[i * keysymsPerKeycode + j];
if (keySym != NoSymbol) {
entry.m_keyMask = indexToModifierMask(j) & ~LockMask;
m_keycodeMap.insert(std::make_pair(keySym, entry));
const KeySym keysym = keysyms[i * keysymsPerKeycode + j];
if (keysym != NoSymbol) {
// FIXME
// entry.m_keyMask = indexToModifierMask(j) & ~LockMask;
entry.m_keyMask = 0;
m_keycodeMap[i].insert(std::make_pair(keysym, entry));
}
}
*/
}
// clean up
@ -999,13 +1073,16 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display)
m_toggleModifierMask = 0;
m_altMask = 0;
m_metaMask = 0;
m_superMask = 0;
m_modeSwitchMask = 0;
m_numLockMask = 0;
m_capsLockMask = 0;
m_scrollLockMask = 0;
m_keysPerModifier = keymap->max_keypermod;
m_modifierToKeycode.clear();
m_modifierToKeycode.resize(8 * m_keysPerModifier);
m_modifierToKeycode.resize(8);
m_modifierToKeycodes.clear();
m_modifierToKeycodes.resize(8 * m_keysPerModifier);
// set keycodes and masks
for (unsigned int i = 0; i < 8; ++i) {
@ -1014,13 +1091,18 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display)
KeyCode keycode = keymap->modifiermap[i * m_keysPerModifier + j];
// save in modifier to keycode
m_modifierToKeycode[i * m_keysPerModifier + j] = keycode;
m_modifierToKeycodes[i * m_keysPerModifier + j] = keycode;
// no further interest in unmapped modifier
if (keycode == 0) {
continue;
}
// save keycode for modifier if we don't have one yet
if (m_modifierToKeycode[i] == 0) {
m_modifierToKeycode[i] = keycode;
}
// save in keycode to modifier
m_keycodeToModifier.insert(std::make_pair(keycode, i));
@ -1045,6 +1127,11 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display)
m_metaMask |= bit;
break;
case XK_Super_L:
case XK_Super_R:
m_superMask |= bit;
break;
case XK_Mode_switch:
m_modeSwitchMask |= bit;
break;
@ -1075,7 +1162,8 @@ CXWindowsSecondaryScreen::toggleKey(Display* display,
if (index == m_keycodeMap.end()) {
return;
}
KeyCode keycode = index->second.m_keycode;
// FIXME -- which keycode?
KeyCode keycode = index->second.m_keycode[0];
// toggle the key
if ((keysym == XK_Caps_Lock && m_capsLockHalfDuplex) ||
@ -1107,3 +1195,147 @@ CXWindowsSecondaryScreen::isToggleKeysym(KeySym key)
return false;
}
}
CXWindowsSecondaryScreen::KeyCodeIndex
CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const
{
// convert id to keysym
KeySym keysym = NoSymbol;
switch (id & 0xffffff00) {
case 0x0000:
// Latin-1
keysym = static_cast<KeySym>(id);
break;
case 0xee00:
// ISO 9995 Function and Modifier Keys
if (id == kKeyLeftTab) {
keysym = XK_ISO_Left_Tab;
}
break;
case 0xef00:
// MISCELLANY
keysym = static_cast<KeySym>(id - 0xef00 + 0xff00);
break;
}
// fail if unknown key
if (keysym == NoSymbol) {
return m_keycodeMap.end();
}
// if kKeyTab is requested with shift active then try XK_ISO_Left_Tab
// instead. if that doesn't work, we'll fall back to XK_Tab with
// shift active. this is to handle primary screens that don't map
// XK_ISO_Left_Tab sending events to secondary screens that do.
if (keysym == XK_Tab && (mask & ShiftMask) != 0) {
keysym = XK_ISO_Left_Tab;
mask &= ~ShiftMask;
}
// find the keycodes that generate the keysym
KeyCodeIndex index = m_keycodeMap.find(keysym);
if (index == noKey()) {
// try backup keysym for certain keys (particularly the numpad
// keys since most laptops don't have a separate numpad and the
// numpad overlaying the main keyboard may not have movement
// key bindings).
switch (keysym) {
case XK_KP_Home:
keysym = XK_Home;
break;
case XK_KP_Left:
keysym = XK_Left;
break;
case XK_KP_Up:
keysym = XK_Up;
break;
case XK_KP_Right:
keysym = XK_Right;
break;
case XK_KP_Down:
keysym = XK_Down;
break;
case XK_KP_Prior:
keysym = XK_Prior;
break;
case XK_KP_Next:
keysym = XK_Next;
break;
case XK_KP_End:
keysym = XK_End;
break;
case XK_KP_Insert:
keysym = XK_Insert;
break;
case XK_KP_Delete:
keysym = XK_Delete;
break;
case XK_ISO_Left_Tab:
keysym = XK_Tab;
mask |= ShiftMask;
break;
default:
return index;
}
index = m_keycodeMap.find(keysym);
}
return index;
}
CXWindowsSecondaryScreen::KeyCodeIndex
CXWindowsSecondaryScreen::noKey() const
{
return m_keycodeMap.end();
}
bool
CXWindowsSecondaryScreen::adjustForNumLock(KeySym keysym) const
{
if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)) {
// it's NumLock sensitive
log((CLOG_DEBUG2 "keypad key: NumLock %s", ((m_mask & m_numLockMask) != 0) ? "active" : "inactive"));
return true;
}
return false;
}
bool
CXWindowsSecondaryScreen::adjustForCapsLock(KeySym keysym) const
{
KeySym lKey, uKey;
XConvertCase(keysym, &lKey, &uKey);
if (lKey != uKey) {
// it's CapsLock sensitive
log((CLOG_DEBUG2 "case convertible: CapsLock %s", ((m_mask & m_capsLockMask) != 0) ? "active" : "inactive"));
return true;
}
return false;
}
//
// CXWindowsSecondaryScreen::KeyCodeMask
//
CXWindowsSecondaryScreen::KeyCodeMask::KeyCodeMask()
{
m_keycode[0] = 0;
m_keycode[1] = 0;
m_keycode[2] = 0;
m_keycode[3] = 0;
}

View File

@ -70,9 +70,12 @@ private:
enum EKeyAction { kPress, kRelease, kRepeat };
class KeyCodeMask {
public:
KeyCode m_keycode;
unsigned int m_keyMask;
unsigned int m_keyMaskMask;
KeyCodeMask();
public:
KeyCode m_keycode[4];
// FIXME -- don't need masks
unsigned int m_keyMask[4];
unsigned int m_keyMaskMask[4];
};
class Keystroke {
public:
@ -82,15 +85,18 @@ private:
};
typedef std::vector<Keystroke> Keystrokes;
typedef std::vector<KeyCode> KeyCodes;
typedef std::map<KeyID, KeyCodeMask> KeyCodeMap;
typedef std::map<KeySym, KeyCodeMask> KeyCodeMap;
typedef KeyCodeMap::const_iterator KeyCodeIndex;
typedef std::map<KeyCode, unsigned int> ModifierMap;
unsigned int mapButton(ButtonID button) const;
unsigned int mapKey(Keystrokes&, KeyCode&, KeyID,
KeyModifierMask, EKeyAction) const;
/*
bool findKeyCode(KeyCode&, unsigned int&,
KeyID id, unsigned int) const;
*/
void doKeystrokes(const Keystrokes&, SInt32 count);
unsigned int maskToX(KeyModifierMask) const;
@ -102,7 +108,14 @@ private:
void toggleKey(Display*, KeySym, unsigned int mask);
static bool isToggleKeysym(KeySym);
KeyCodeIndex findKey(KeyID keysym, KeyModifierMask& mask) const;
KeyCodeIndex noKey() const;
bool adjustForNumLock(KeySym) const;
bool adjustForCapsLock(KeySym) const;
private:
enum { kNONE, kSHIFT, kALTGR, kSHIFT_ALTGR };
CXWindowsScreen* m_screen;
Window m_window;
@ -134,6 +147,7 @@ private:
// modifier masks
unsigned int m_altMask;
unsigned int m_metaMask;
unsigned int m_superMask;
unsigned int m_modeSwitchMask;
unsigned int m_numLockMask;
unsigned int m_capsLockMask;
@ -142,6 +156,7 @@ private:
// map X modifier key indices to the key codes bound to them
unsigned int m_keysPerModifier;
KeyCodes m_modifierToKeycode;
KeyCodes m_modifierToKeycodes;
// maps keycodes to modifier indices
ModifierMap m_keycodeToModifier;

View File

@ -709,8 +709,8 @@ static const KeyID g_virtualKey[][2] =
/* 0x58 */ kKeyNone, kKeyNone, // VK_X
/* 0x59 */ kKeyNone, kKeyNone, // VK_Y
/* 0x5a */ kKeyNone, kKeyNone, // VK_Z
/* 0x5b */ kKeyNone, kKeyMeta_L, // VK_LWIN
/* 0x5c */ kKeyNone, kKeyMeta_R, // VK_RWIN
/* 0x5b */ kKeyNone, kKeySuper_L, // VK_LWIN
/* 0x5c */ kKeyNone, kKeySuper_R, // VK_RWIN
/* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS
/* 0x5e */ kKeyNone, kKeyNone, // undefined
/* 0x5f */ kKeyNone, kKeyNone, // undefined

View File

@ -625,6 +625,8 @@ CXWindowsPrimaryScreen::mapModifier(unsigned int state) const
mask |= KeyModifierAlt;
if (state & m_metaMask)
mask |= KeyModifierMeta;
if (state & m_superMask)
mask |= KeyModifierSuper;
if (state & m_modeSwitchMask)
mask |= KeyModifierModeSwitch;
if (state & m_numLockMask)
@ -691,6 +693,7 @@ CXWindowsPrimaryScreen::updateKeys()
// initialize
m_altMask = 0;
m_metaMask = 0;
m_superMask = 0;
m_modeSwitchMask = 0;
m_numLockMask = 0;
m_capsLockMask = 0;
@ -721,6 +724,11 @@ CXWindowsPrimaryScreen::updateKeys()
m_metaMask |= bit;
break;
case XK_Super_L:
case XK_Super_R:
m_superMask |= bit;
break;
case XK_Mode_switch:
m_modeSwitchMask |= bit;
break;

View File

@ -100,6 +100,7 @@ private:
// modifier masks
unsigned int m_altMask;
unsigned int m_metaMask;
unsigned int m_superMask;
unsigned int m_modeSwitchMask;
unsigned int m_numLockMask;
unsigned int m_capsLockMask;

View File

@ -37,7 +37,8 @@ static const KeyModifierMask KeyModifierShift = 0x0001;
static const KeyModifierMask KeyModifierControl = 0x0002;
static const KeyModifierMask KeyModifierAlt = 0x0004;
static const KeyModifierMask KeyModifierMeta = 0x0008;
static const KeyModifierMask KeyModifierModeSwitch = 0x0010;
static const KeyModifierMask KeyModifierSuper = 0x0010;
static const KeyModifierMask KeyModifierModeSwitch = 0x0020;
static const KeyModifierMask KeyModifierCapsLock = 0x1000;
static const KeyModifierMask KeyModifierNumLock = 0x2000;
static const KeyModifierMask KeyModifierScrollLock = 0x4000;