From 4586f8818805a7aa4ca019323f92d6333bb1a57e Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 12:07:02 +0000 Subject: [PATCH] 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. --- lib/client/CMSWindowsSecondaryScreen.cpp | 16 +- lib/client/CXWindowsSecondaryScreen.cpp | 660 +++++++++++++++-------- lib/client/CXWindowsSecondaryScreen.h | 23 +- lib/server/CMSWindowsPrimaryScreen.cpp | 4 +- lib/server/CXWindowsPrimaryScreen.cpp | 8 + lib/server/CXWindowsPrimaryScreen.h | 1 + lib/synergy/KeyTypes.h | 3 +- 7 files changed, 488 insertions(+), 227 deletions(-) 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;