diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index 974a189a..a42f3763 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -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 } diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index 3f6c2ee3..4cb71fba 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -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)); - return m_mask; + 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(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(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(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(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(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(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; +} diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h index 5dc08c0b..d055016b 100644 --- a/lib/client/CXWindowsSecondaryScreen.h +++ b/lib/client/CXWindowsSecondaryScreen.h @@ -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 Keystrokes; typedef std::vector KeyCodes; - typedef std::map KeyCodeMap; + typedef std::map KeyCodeMap; + typedef KeyCodeMap::const_iterator KeyCodeIndex; typedef std::map 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; diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 0bd15164..912cc219 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -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 diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index d9cce9bd..756dcb03 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -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; diff --git a/lib/server/CXWindowsPrimaryScreen.h b/lib/server/CXWindowsPrimaryScreen.h index 8486ab5e..b67f3009 100644 --- a/lib/server/CXWindowsPrimaryScreen.h +++ b/lib/server/CXWindowsPrimaryScreen.h @@ -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; diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 954b5305..49b10c63 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -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;