Added support for X11 compose key (Multi_key). This change fixes

the handling of compose key sequences.  The key presses were
suppressed but not the corresponding releases, confusing the
clients.  It also adds support for generating keysyms via the
compose key if the necessary dead keys or Mode_switch are not
available.
This commit is contained in:
crs 2004-11-04 21:26:43 +00:00
parent 4be95841d2
commit bdd3635f4b
6 changed files with 285 additions and 58 deletions

View File

@ -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

View File

@ -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,

View File

@ -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<KeyButton>(xkey.keycode);
if (keycode == 0) {
isFake = true;
keycode = static_cast<KeyButton>(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);
}
}
}

View File

@ -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<bool> 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];

View File

@ -17,6 +17,7 @@
#include "CLog.h"
#include "CStringUtil.h"
#include <X11/Xatom.h>
#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) {

View File

@ -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