diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index ea4184c4..fbcc53c2 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -281,60 +281,41 @@ CXWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, if (keyIndex != m_keysymMap.end()) { // the keysym is mapped to some keycode. create the keystrokes // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat); + return mapToKeystrokes(keys, keyIndex, isAutoRepeat, false); } // we can't find the keysym mapped to any keycode. this doesn't // necessarily mean we can't generate the keysym, though. if the // keysym can be created by combining keysyms then we may still // be okay. - CXWindowsUtil::KeySyms decomposition; - if (CXWindowsUtil::decomposeKeySym(keysym, decomposition)) { - LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); - - // map each decomposed keysym to keystrokes. we want the mask - // and the keycode from the last keysym (which should be the - // only non-dead key). the dead keys are not sensitive to - // anything but shift and mode switch. - KeyButton keycode = 0; - for (CXWindowsUtil::KeySyms::const_iterator i = decomposition.begin(); - i != decomposition.end(); ++i) { - // lookup the key - keysym = *i; - keyIndex = m_keysymMap.find(keysym); - if (keyIndex == m_keysymMap.end()) { - // missing a required keysym - LOG((CLOG_DEBUG2 "can't map keysym %d: 0x%04x", i - decomposition.begin(), keysym)); - return 0; - } - - // the keysym is mapped to some keycode - keycode = mapToKeystrokes(keys, keyIndex, isAutoRepeat); - if (keycode == 0) { - return 0; - } + if (!isAutoRepeat) { + KeyButton keycode = mapDecompositionToKeystrokes(keys, keysym, true); + if (keycode != 0) { + return keycode; + } + keycode = mapDecompositionToKeystrokes(keys, keysym, false); + if (keycode != 0) { + // no key is left synthetically down when using the compose key + // so return 0 even though we succeeded. + return 0; } - - return keycode; } - // if the mapping isn't found and keysym is caps lock sensitive - // then convert the case of the keysym and try again. - if (keyIndex == m_keysymMap.end()) { - KeySym lKey, uKey; - XConvertCase(keysym, &lKey, &uKey); - if (lKey != uKey) { - if (lKey == keysym) { - keyIndex = m_keysymMap.find(uKey); - } - else { - keyIndex = m_keysymMap.find(lKey); - } + // if the keysym is caps lock sensitive then convert the case of + // the keysym and try again. + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + if (lKey != uKey) { + if (lKey == keysym) { + keyIndex = m_keysymMap.find(uKey); + } + else { + keyIndex = m_keysymMap.find(lKey); } if (keyIndex != m_keysymMap.end()) { // the keysym is mapped to some keycode. create the keystrokes // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat); + return mapToKeystrokes(keys, keyIndex, isAutoRepeat, false); } } @@ -786,7 +767,8 @@ CXWindowsKeyState::keyIDToKeySym(KeyID id, KeyModifierMask mask) const KeyButton CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, - KeySymIndex keyIndex, bool isAutoRepeat) const + KeySymIndex keyIndex, bool isAutoRepeat, + bool pressAndRelease) const { // keyIndex must be valid assert(keyIndex != m_keysymMap.end()); @@ -873,7 +855,14 @@ CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, // add the key event Keystroke keystroke; keystroke.m_key = keycode; - if (!isAutoRepeat) { + if (pressAndRelease) { + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + } + else if (!isAutoRepeat) { keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); @@ -895,6 +884,60 @@ CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, return keycode; } +KeyButton +CXWindowsKeyState::mapDecompositionToKeystrokes( + Keystrokes& keys, KeySym keysym, bool usingDeadKeys) const +{ + // decompose the keysym + CXWindowsUtil::KeySyms decomposed; + if (usingDeadKeys) { + if (!CXWindowsUtil::decomposeKeySymWithDeadKeys(keysym, decomposed)) { + // no decomposition + return 0; + } + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms using dead keys", keysym, decomposed.size())); + } + else { + if (!CXWindowsUtil::decomposeKeySymWithCompose(keysym, decomposed)) { + // no decomposition + return 0; + } + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms using compose key", keysym, decomposed.size())); + } + size_t n = decomposed.size(); + if (n == 0) { + // nothing in the decomposition + return 0; + } + + // map to keystrokes + Keystrokes keystrokes; + KeyButton keycode = 0; + for (size_t i = 0; i < n; ++i) { + // lookup the key + keysym = decomposed[i]; + KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end()) { + // missing a required keysym + LOG((CLOG_DEBUG2 "can't map keysym %d: 0x%04x", i, keysym)); + return 0; + } + + // the keysym is mapped to some keycode. add press and + // release unless this is the last key and usingDeadKeys. + keycode = mapToKeystrokes(keystrokes, keyIndex, + false, (i + 1 < n || !usingDeadKeys)); + if (keycode == 0) { + return 0; + } + } + + // copy keystrokes + keys.insert(keys.end(), keystrokes.begin(), keystrokes.end()); + + return keycode; +} + unsigned int CXWindowsKeyState::findBestKeyIndex(KeySymIndex keyIndex, KeyModifierMask /*currentMask*/) const diff --git a/lib/platform/CXWindowsKeyState.h b/lib/platform/CXWindowsKeyState.h index 15d8f894..bb660d48 100644 --- a/lib/platform/CXWindowsKeyState.h +++ b/lib/platform/CXWindowsKeyState.h @@ -100,7 +100,13 @@ private: // map a KeySym into the keystrokes to produce it KeyButton mapToKeystrokes(Keystrokes& keys, KeySymIndex keyIndex, - bool isAutoRepeat) const; + bool isAutoRepeat, + bool pressAndRelease) const; + + // map a decomposition into keystrokes to produce it. returns the + // last key added to keys iff successful and 0 otherwise. + KeyButton mapDecompositionToKeystrokes(Keystrokes& keys, + KeySym keysym, bool usingDeadKeys) const; // choose the best set of modifiers to generate the KeySym unsigned int findBestKeyIndex(KeySymIndex keyIndex, diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 80b7fc73..b7a84a8a 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -334,6 +334,7 @@ CXWindowsScreen::leave() if (m_ic != NULL) { XmbResetIC(m_ic); XSetICFocus(m_ic); + m_filtered.clear(); } // now off screen @@ -876,6 +877,18 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) // now filter the event if (XFilterEvent(xevent, None)) { + if (xevent->type == KeyPress) { + // add filtered presses to the filtered list + m_filtered.insert(m_lastKeycode); + } + return; + } + + // discard matching key releases for key presses that were + // filtered and remove them from our filtered list. + else if (xevent->type == KeyRelease && + m_filtered.count(xevent->xkey.keycode) > 0) { + m_filtered.erase(xevent->xkey.keycode); return; } } @@ -1030,14 +1043,26 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) // get which button. see call to XFilterEvent() in onEvent() // for more info. + bool isFake = false; KeyButton keycode = static_cast(xkey.keycode); if (keycode == 0) { + isFake = true; keycode = static_cast(m_lastKeycode); + if (keycode == 0) { + // no keycode + return; + } } // handle key m_keyState->sendKeyEvent(getEventTarget(), true, false, key, mask, 1, keycode); + + // do fake release if this is a fake press + if (isFake) { + m_keyState->sendKeyEvent(getEventTarget(), + false, false, key, mask, 1, keycode); + } } } diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 0a93f8b2..cec6c6fa 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -16,6 +16,7 @@ #define CXWINDOWSSCREEN_H #include "CPlatformScreen.h" +#include "stdset.h" #include "stdvector.h" #if X_DISPLAY_MISSING # error X11 is required to build synergy @@ -135,6 +136,7 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: + typedef std::set CFilteredKeycodes; // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; @@ -164,6 +166,7 @@ private: XIM m_im; XIC m_ic; KeyCode m_lastKeycode; + CFilteredKeycodes m_filtered; // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 9d6641cc..b556da00 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -17,6 +17,7 @@ #include "CLog.h" #include "CStringUtil.h" #include +#define XK_MISCELLANY #define XK_XKB_KEYS #define XK_LATIN1 #define XK_LATIN2 @@ -814,7 +815,7 @@ struct codepair { { 0x20ac, 0x20ac } /* EuroSign EURO SIGN */ }; -static const KeySym s_rawDecomposeTable[] = { +static const KeySym s_rawDeadDecomposeTable[] = { // non-dead version of dead keys XK_grave, XK_dead_grave, XK_space, 0, XK_acute, XK_dead_acute, XK_space, 0, @@ -1026,6 +1027,116 @@ static const KeySym s_rawDecomposeTable[] = { 0 }; +static const KeySym s_rawComposedDecomposeTable[] = { + XK_AE, XK_Multi_key, XK_A, XK_E, 0, + XK_Aacute, XK_Multi_key, XK_A, XK_apostrophe, 0, + XK_Acircumflex, XK_Multi_key, XK_A, XK_asciicircum, 0, + XK_Adiaeresis, XK_Multi_key, XK_A, XK_quotedbl, 0, + XK_Agrave, XK_Multi_key, XK_A, XK_grave, 0, + XK_Aring, XK_Multi_key, XK_A, XK_asterisk, 0, + XK_Atilde, XK_Multi_key, XK_A, XK_asciitilde, 0, + XK_Ccedilla, XK_Multi_key, XK_C, XK_comma, 0, + XK_ETH, XK_Multi_key, XK_D, XK_minus, 0, + XK_Eacute, XK_Multi_key, XK_E, XK_apostrophe, 0, + XK_Ecircumflex, XK_Multi_key, XK_E, XK_asciicircum, 0, + XK_Ediaeresis, XK_Multi_key, XK_E, XK_quotedbl, 0, + XK_Egrave, XK_Multi_key, XK_E, XK_grave, 0, + XK_Iacute, XK_Multi_key, XK_I, XK_apostrophe, 0, + XK_Icircumflex, XK_Multi_key, XK_I, XK_asciicircum, 0, + XK_Idiaeresis, XK_Multi_key, XK_I, XK_quotedbl, 0, + XK_Igrave, XK_Multi_key, XK_I, XK_grave, 0, + XK_Ntilde, XK_Multi_key, XK_N, XK_asciitilde, 0, + XK_Oacute, XK_Multi_key, XK_O, XK_apostrophe, 0, + XK_Ocircumflex, XK_Multi_key, XK_O, XK_asciicircum, 0, + XK_Odiaeresis, XK_Multi_key, XK_O, XK_quotedbl, 0, + XK_Ograve, XK_Multi_key, XK_O, XK_grave, 0, + XK_Ooblique, XK_Multi_key, XK_O, XK_slash, 0, + XK_Otilde, XK_Multi_key, XK_O, XK_asciitilde, 0, + XK_THORN, XK_Multi_key, XK_T, XK_H, 0, + XK_Uacute, XK_Multi_key, XK_U, XK_apostrophe, 0, + XK_Ucircumflex, XK_Multi_key, XK_U, XK_asciicircum, 0, + XK_Udiaeresis, XK_Multi_key, XK_U, XK_quotedbl, 0, + XK_Ugrave, XK_Multi_key, XK_U, XK_grave, 0, + XK_Yacute, XK_Multi_key, XK_Y, XK_apostrophe, 0, + XK_aacute, XK_Multi_key, XK_a, XK_apostrophe, 0, + XK_acircumflex, XK_Multi_key, XK_a, XK_asciicircum, 0, + XK_acute, XK_Multi_key, XK_apostrophe, XK_apostrophe, 0, + XK_adiaeresis, XK_Multi_key, XK_a, XK_quotedbl, 0, + XK_ae, XK_Multi_key, XK_a, XK_e, 0, + XK_agrave, XK_Multi_key, XK_a, XK_grave, 0, + XK_aring, XK_Multi_key, XK_a, XK_asterisk, 0, + XK_at, XK_Multi_key, XK_A, XK_T, 0, + XK_atilde, XK_Multi_key, XK_a, XK_asciitilde, 0, + XK_backslash, XK_Multi_key, XK_slash, XK_slash, 0, + XK_bar, XK_Multi_key, XK_L, XK_V, 0, + XK_braceleft, XK_Multi_key, XK_parenleft, XK_minus, 0, + XK_braceright, XK_Multi_key, XK_parenright, XK_minus, 0, + XK_bracketleft, XK_Multi_key, XK_parenleft, XK_parenleft, 0, + XK_bracketright, XK_Multi_key, XK_parenright, XK_parenright, 0, + XK_brokenbar, XK_Multi_key, XK_B, XK_V, 0, + XK_ccedilla, XK_Multi_key, XK_c, XK_comma, 0, + XK_cedilla, XK_Multi_key, XK_comma, XK_comma, 0, + XK_cent, XK_Multi_key, XK_c, XK_slash, 0, + XK_copyright, XK_Multi_key, XK_parenleft, XK_c, 0, + XK_currency, XK_Multi_key, XK_o, XK_x, 0, + XK_degree, XK_Multi_key, XK_0, XK_asciicircum, 0, + XK_diaeresis, XK_Multi_key, XK_quotedbl, XK_quotedbl, 0, + XK_division, XK_Multi_key, XK_colon, XK_minus, 0, + XK_eacute, XK_Multi_key, XK_e, XK_apostrophe, 0, + XK_ecircumflex, XK_Multi_key, XK_e, XK_asciicircum, 0, + XK_ediaeresis, XK_Multi_key, XK_e, XK_quotedbl, 0, + XK_egrave, XK_Multi_key, XK_e, XK_grave, 0, + XK_eth, XK_Multi_key, XK_d, XK_minus, 0, + XK_exclamdown, XK_Multi_key, XK_exclam, XK_exclam, 0, + XK_guillemotleft, XK_Multi_key, XK_less, XK_less, 0, + XK_guillemotright, XK_Multi_key, XK_greater, XK_greater, 0, + XK_numbersign, XK_Multi_key, XK_plus, XK_plus, 0, + XK_hyphen, XK_Multi_key, XK_minus, XK_minus, 0, + XK_iacute, XK_Multi_key, XK_i, XK_apostrophe, 0, + XK_icircumflex, XK_Multi_key, XK_i, XK_asciicircum, 0, + XK_idiaeresis, XK_Multi_key, XK_i, XK_quotedbl, 0, + XK_igrave, XK_Multi_key, XK_i, XK_grave, 0, + XK_macron, XK_Multi_key, XK_minus, XK_asciicircum, 0, + XK_masculine, XK_Multi_key, XK_o, XK_underscore, 0, + XK_mu, XK_Multi_key, XK_u, XK_slash, 0, + XK_multiply, XK_Multi_key, XK_x, XK_x, 0, + XK_nobreakspace, XK_Multi_key, XK_space, XK_space, 0, + XK_notsign, XK_Multi_key, XK_comma, XK_minus, 0, + XK_ntilde, XK_Multi_key, XK_n, XK_asciitilde, 0, + XK_oacute, XK_Multi_key, XK_o, XK_apostrophe, 0, + XK_ocircumflex, XK_Multi_key, XK_o, XK_asciicircum, 0, + XK_odiaeresis, XK_Multi_key, XK_o, XK_quotedbl, 0, + XK_ograve, XK_Multi_key, XK_o, XK_grave, 0, + XK_onehalf, XK_Multi_key, XK_1, XK_2, 0, + XK_onequarter, XK_Multi_key, XK_1, XK_4, 0, + XK_onesuperior, XK_Multi_key, XK_1, XK_asciicircum, 0, + XK_ordfeminine, XK_Multi_key, XK_a, XK_underscore, 0, + XK_oslash, XK_Multi_key, XK_o, XK_slash, 0, + XK_otilde, XK_Multi_key, XK_o, XK_asciitilde, 0, + XK_paragraph, XK_Multi_key, XK_p, XK_exclam, 0, + XK_periodcentered, XK_Multi_key, XK_period, XK_period, 0, + XK_plusminus, XK_Multi_key, XK_plus, XK_minus, 0, + XK_questiondown, XK_Multi_key, XK_question, XK_question, 0, + XK_registered, XK_Multi_key, XK_parenleft, XK_r, 0, + XK_section, XK_Multi_key, XK_s, XK_o, 0, + XK_ssharp, XK_Multi_key, XK_s, XK_s, 0, + XK_sterling, XK_Multi_key, XK_L, XK_minus, 0, + XK_thorn, XK_Multi_key, XK_t, XK_h, 0, + XK_threequarters, XK_Multi_key, XK_3, XK_4, 0, + XK_threesuperior, XK_Multi_key, XK_3, XK_asciicircum, 0, + XK_twosuperior, XK_Multi_key, XK_2, XK_asciicircum, 0, + XK_uacute, XK_Multi_key, XK_u, XK_apostrophe, 0, + XK_ucircumflex, XK_Multi_key, XK_u, XK_asciicircum, 0, + XK_udiaeresis, XK_Multi_key, XK_u, XK_quotedbl, 0, + XK_ugrave, XK_Multi_key, XK_u, XK_grave, 0, + XK_yacute, XK_Multi_key, XK_y, XK_apostrophe, 0, + XK_ydiaeresis, XK_Multi_key, XK_y, XK_quotedbl, 0, + XK_yen, XK_Multi_key, XK_y, XK_equal, 0, + + // end of table + 0 +}; + // // CXWindowsUtil @@ -1033,7 +1144,8 @@ static const KeySym s_rawDecomposeTable[] = { CXWindowsUtil::CKeySymMap CXWindowsUtil::s_keySymToUCS4; CXWindowsUtil::CUCS4Map CXWindowsUtil::s_UCS4ToKeySym; -CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_decomposedKeySyms; +CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_deadKeyDecomposedKeySyms; +CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_composeDecomposedKeySyms; bool CXWindowsUtil::getWindowProperty(Display* display, Window window, @@ -1231,18 +1343,33 @@ CXWindowsUtil::mapUCS4ToKeySym(UInt32 c) } bool -CXWindowsUtil::decomposeKeySym(KeySym keysym, KeySyms& decomposed) +CXWindowsUtil::decomposeKeySymWithDeadKeys(KeySym keysym, KeySyms& decomposed) { // unfortunately, X11 doesn't appear to have any way of // decomposing a keysym into its component keysyms. we'll // use a lookup table for certain character sets. initKeyMaps(); - CKeySymsMap::const_iterator i = s_decomposedKeySyms.find(keysym); - if (i == s_decomposedKeySyms.end()) { - return false; + CKeySymsMap::const_iterator i = s_deadKeyDecomposedKeySyms.find(keysym); + if (i != s_deadKeyDecomposedKeySyms.end()) { + decomposed = i->second; + return true; } - decomposed = i->second; - return true; + return false; +} + +bool +CXWindowsUtil::decomposeKeySymWithCompose(KeySym keysym, KeySyms& decomposed) +{ + // unfortunately, X11 doesn't appear to have any way of + // decomposing a keysym into its component keysyms. we'll + // use a lookup table for certain character sets. + initKeyMaps(); + CKeySymsMap::const_iterator i = i = s_composeDecomposedKeySyms.find(keysym); + if (i != s_composeDecomposedKeySyms.end()) { + decomposed = i->second; + return true; + } + return false; } CString @@ -1296,10 +1423,22 @@ CXWindowsUtil::initKeyMaps() } // fill decomposed key table if not filled yet - if (s_decomposedKeySyms.empty()) { - for (const KeySym* scan = s_rawDecomposeTable; *scan != 0; ++scan) { + if (s_deadKeyDecomposedKeySyms.empty()) { + for (const KeySym* scan = s_rawDeadDecomposeTable; *scan != 0; ++scan) { // add an entry for this keysym - KeySyms& entry = s_decomposedKeySyms[*scan]; + KeySyms& entry = s_deadKeyDecomposedKeySyms[*scan]; + + // add the decomposed keysyms for the keysym + while (*++scan != 0) { + entry.push_back(*scan); + } + } + } + if (s_composeDecomposedKeySyms.empty()) { + for (const KeySym* scan = + s_rawComposedDecomposeTable; *scan != 0; ++scan) { + // add an entry for this keysym + KeySyms& entry = s_composeDecomposedKeySyms[*scan]; // add the decomposed keysyms for the keysym while (*++scan != 0) { diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index 5765c94a..13e9313e 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -73,13 +73,23 @@ public: */ static KeySym mapUCS4ToKeySym(UInt32); - //! Decompose a KeySym + //! Decompose a KeySym using dead keys /*! Decomposes \c keysym into its component keysyms. All but the last decomposed KeySym are dead keys. Returns true iff the decomposition was successful. */ - static bool decomposeKeySym(KeySym keysym, KeySyms& decomposed); + static bool decomposeKeySymWithDeadKeys(KeySym keysym, + KeySyms& decomposed); + + //! Decompose a KeySym using the compose key + /*! + Decomposes \c keysym into its component keysyms. The first key is + Multi_key and the rest are normal (i.e. not dead) keys. Returns + true iff the decomposition was successful. + */ + static bool decomposeKeySymWithCompose(KeySym keysym, + KeySyms& decomposed); //! Convert Atom to its string /*! @@ -162,7 +172,8 @@ private: static CKeySymMap s_keySymToUCS4; static CUCS4Map s_UCS4ToKeySym; - static CKeySymsMap s_decomposedKeySyms; + static CKeySymsMap s_deadKeyDecomposedKeySyms; + static CKeySymsMap s_composeDecomposedKeySyms; }; #endif