diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 22db4143..5b3bdf81 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -39,6 +39,10 @@ CServerProxy::CServerProxy(IClient* client, assert(m_client != NULL); assert(m_input != NULL); assert(m_output != NULL); + + // initialize modifier translation table + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) + m_modifierTranslationTable[id] = id; } CServerProxy::~CServerProxy() @@ -313,6 +317,110 @@ CServerProxy::sendInfo(const CClientInfo& info) info.m_mx, info.m_my); } +KeyID +CServerProxy::translateKey(KeyID id) const +{ + static const KeyID s_translationTable[kKeyModifierIDLast][2] = { + { kKeyNone, kKeyNone }, + { kKeyShift_L, kKeyShift_R }, + { kKeyControl_L, kKeyControl_R }, + { kKeyAlt_L, kKeyAlt_R }, + { kKeyMeta_L, kKeyMeta_R }, + { kKeySuper_L, kKeySuper_R } + }; + + KeyModifierID id2 = kKeyModifierIDNull; + UInt32 side = 0; + switch (id) { + case kKeyShift_L: + id2 = kKeyModifierIDShift; + side = 0; + break; + + case kKeyShift_R: + id2 = kKeyModifierIDShift; + side = 1; + break; + + case kKeyControl_L: + id2 = kKeyModifierIDControl; + side = 0; + break; + + case kKeyControl_R: + id2 = kKeyModifierIDControl; + side = 1; + break; + + case kKeyAlt_L: + id2 = kKeyModifierIDAlt; + side = 0; + break; + + case kKeyAlt_R: + id2 = kKeyModifierIDAlt; + side = 1; + break; + + case kKeyMeta_L: + id2 = kKeyModifierIDMeta; + side = 0; + break; + + case kKeyMeta_R: + id2 = kKeyModifierIDMeta; + side = 1; + break; + + case kKeySuper_L: + id2 = kKeyModifierIDSuper; + side = 0; + break; + + case kKeySuper_R: + id2 = kKeyModifierIDSuper; + side = 1; + break; + } + + if (id2 != kKeyModifierIDNull) { + return s_translationTable[m_modifierTranslationTable[id2]][side]; + } + else { + return id; + } +} + +KeyModifierMask +CServerProxy::translateModifierMask(KeyModifierMask mask) const +{ + static const KeyModifierMask s_masks[kKeyModifierIDLast] = { + 0x0000, + KeyModifierShift, + KeyModifierControl, + KeyModifierAlt, + KeyModifierMeta, + KeyModifierSuper + }; + + KeyModifierMask newMask = 0; + if ((mask & KeyModifierShift) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDShift]]; + } + if ((mask & KeyModifierControl) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDControl]]; + } + if ((mask & KeyModifierAlt) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDAlt]]; + } + if ((mask & KeyModifierMeta) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDMeta]]; + } + if ((mask & KeyModifierSuper) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]]; + } +} + void CServerProxy::enter() { @@ -397,9 +505,16 @@ CServerProxy::keyDown() CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, &id, &mask); LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); - // forward - getClient()->keyDown(static_cast(id), + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyDown(id2, mask2); } void @@ -414,10 +529,16 @@ CServerProxy::keyRepeat() kMsgDKeyRepeat + 4, &id, &mask, &count); LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( + static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + // forward - getClient()->keyRepeat(static_cast(id), - static_cast(mask), - count); + getClient()->keyRepeat(id2, mask2, count); } void @@ -431,9 +552,16 @@ CServerProxy::keyUp() CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask); LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); - // forward - getClient()->keyUp(static_cast(id), + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyUp(id2, mask2); } void @@ -534,6 +662,10 @@ CServerProxy::resetOptions() // forward getClient()->resetOptions(); + + // reset modifier translation table + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) + m_modifierTranslationTable[id] = id; } void @@ -546,6 +678,31 @@ CServerProxy::setOptions() // forward getClient()->setOptions(options); + + // update modifier table + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + KeyModifierID id = kKeyModifierIDNull; + if (options[i] == kOptionModifierMapForShift) { + id = kKeyModifierIDShift; + } + else if (options[i] == kOptionModifierMapForControl) { + id = kKeyModifierIDControl; + } + else if (options[i] == kOptionModifierMapForAlt) { + id = kKeyModifierIDAlt; + } + else if (options[i] == kOptionModifierMapForMeta) { + id = kKeyModifierIDMeta; + } + else if (options[i] == kOptionModifierMapForSuper) { + id = kKeyModifierIDSuper; + } + if (id != kKeyModifierIDNull) { + m_modifierTranslationTable[id] = + static_cast(options[i + 1]); + LOG((CLOG_DEBUG1 "modifier %d mapped to %d", id, m_modifierTranslationTable[id])); + } + } } void diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index a54ccdff..7d7eb2dd 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -16,6 +16,7 @@ #define CSERVERPROXY_H #include "IScreenReceiver.h" +#include "KeyTypes.h" #include "CMutex.h" class IClient; @@ -92,6 +93,10 @@ private: void sendInfo(const CClientInfo&); + // modifier key translation + KeyID translateKey(KeyID) const; + KeyModifierMask translateModifierMask(KeyModifierMask) const; + // message handlers void enter(); void leave(); @@ -123,6 +128,8 @@ private: SInt32 m_xMouse, m_yMouse; bool m_ignoreMouse; + + KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; }; #endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 9ed619db..5728bd05 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -13,7 +13,7 @@ */ #include "CConfig.h" -#include "ProtocolTypes.h" +#include "KeyTypes.h" #include "XSocket.h" #include "stdistream.h" #include "stdostream.h" @@ -514,6 +514,24 @@ CConfig::parseBoolean(const CString& arg) throw XConfigRead("invalid argument"); } +OptionValue +CConfig::parseModifierKey(const CString& arg) +{ + if (CStringUtil::CaselessCmp::equal(arg, "shift")) + return static_cast(kKeyModifierIDShift); + if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) + return static_cast(kKeyModifierIDControl); + if (CStringUtil::CaselessCmp::equal(arg, "alt")) + return static_cast(kKeyModifierIDAlt); + if (CStringUtil::CaselessCmp::equal(arg, "meta")) + return static_cast(kKeyModifierIDMeta); + if (CStringUtil::CaselessCmp::equal(arg, "super")) + return static_cast(kKeyModifierIDSuper); + if (CStringUtil::CaselessCmp::equal(arg, "none")) + return static_cast(kKeyModifierIDNull); + throw XConfigRead("invalid argument"); +} + const char* CConfig::getOptionName(OptionID id) { @@ -523,6 +541,21 @@ CConfig::getOptionName(OptionID id) if (id == kOptionHalfDuplexNumLock) { return "halfDuplexNumLock"; } + if (id == kOptionModifierMapForShift) { + return "shift"; + } + if (id == kOptionModifierMapForControl) { + return "ctrl"; + } + if (id == kOptionModifierMapForAlt) { + return "alt"; + } + if (id == kOptionModifierMapForMeta) { + return "meta"; + } + if (id == kOptionModifierMapForSuper) { + return "super"; + } return NULL; } @@ -533,6 +566,31 @@ CConfig::getOptionValue(OptionID id, OptionValue value) id == kOptionHalfDuplexNumLock) { return (value != 0) ? "true" : "false"; } + if (id == kOptionModifierMapForShift || + id == kOptionModifierMapForControl || + id == kOptionModifierMapForAlt || + id == kOptionModifierMapForMeta || + id == kOptionModifierMapForSuper) { + switch (value) { + case kKeyModifierIDShift: + return "shift"; + + case kKeyModifierIDControl: + return "ctrl"; + + case kKeyModifierIDAlt: + return "alt"; + + case kKeyModifierIDMeta: + return "meta"; + + case kKeyModifierIDSuper: + return "super"; + + default: + return "none"; + } + } return ""; } @@ -700,6 +758,26 @@ CConfig::readSectionScreens(std::istream& s) addOption(screen, kOptionHalfDuplexNumLock, parseBoolean(value)); } + else if (name == "shift") { + addOption(screen, kOptionModifierMapForShift, + parseModifierKey(value)); + } + else if (name == "ctrl") { + addOption(screen, kOptionModifierMapForControl, + parseModifierKey(value)); + } + else if (name == "alt") { + addOption(screen, kOptionModifierMapForAlt, + parseModifierKey(value)); + } + else if (name == "meta") { + addOption(screen, kOptionModifierMapForMeta, + parseModifierKey(value)); + } + else if (name == "super") { + addOption(screen, kOptionModifierMapForSuper, + parseModifierKey(value)); + } else { // unknown argument throw XConfigRead("unknown argument"); diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index a60b3abc..599e436e 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -293,6 +293,7 @@ public: private: static bool readLine(std::istream&, CString&); static bool parseBoolean(const CString&); + static OptionValue parseModifierKey(const CString&); static const char* getOptionName(OptionID); static const char* getOptionValue(OptionID, OptionValue); void readSection(std::istream&); diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 49b10c63..21028824 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -25,13 +25,19 @@ keys, function keys, modifier keys, etc). */ typedef UInt32 KeyID; -//! Modifier key ID +//! Modifier key mask /*! Type to hold a bitmask of key modifiers (e.g. shift keys). */ typedef UInt32 KeyModifierMask; -//! @name Modifier key identifiers +//! Modifier key ID +/*! +Type to hold the id of a key modifier (e.g. a shift key). +*/ +typedef UInt32 KeyModifierID; + +//! @name Modifier key masks //@{ static const KeyModifierMask KeyModifierShift = 0x0001; static const KeyModifierMask KeyModifierControl = 0x0002; @@ -44,6 +50,17 @@ static const KeyModifierMask KeyModifierNumLock = 0x2000; static const KeyModifierMask KeyModifierScrollLock = 0x4000; //@} +//! @name Modifier key identifiers +//@{ +static const KeyModifierID kKeyModifierIDNull = 0; +static const KeyModifierID kKeyModifierIDShift = 1; +static const KeyModifierID kKeyModifierIDControl = 2; +static const KeyModifierID kKeyModifierIDAlt = 3; +static const KeyModifierID kKeyModifierIDMeta = 4; +static const KeyModifierID kKeyModifierIDSuper = 5; +static const KeyModifierID kKeyModifierIDLast = 6; +//@} + //! @name Key identifiers //@{ // all identifiers except kKeyNone are equal to the corresponding diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index 3310ace0..31c72185 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -42,9 +42,14 @@ typedef std::vector COptionsList; //! @name Option identifiers //@{ -static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); -static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); -static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); +static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); +static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS"); +static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); +static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); +static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); +static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); //@} #undef OPTION_CODE