diff --git a/src/gui/src/CommandProcess.h b/src/gui/src/CommandProcess.h index 508552da..ff6c1612 100644 --- a/src/gui/src/CommandProcess.h +++ b/src/gui/src/CommandProcess.h @@ -18,6 +18,7 @@ #ifndef COMMANDTHREAD_H #define COMMANDTHREAD_H +#include #include class CommandProcess : public QObject diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index b5c747b7..e5497e8e 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -18,8 +18,8 @@ if (WIN32) file(GLOB headers "MSWindows*.h") file(GLOB sources "MSWindows*.cpp") elseif (APPLE) - file(GLOB headers "OSX*.h") - file(GLOB sources "OSX*.cpp" "OSX*.m") + file(GLOB headers "OSX*.h" "*KeyResource.h") + file(GLOB sources "OSX*.cpp" "OSX*.m" "*KeyResource.cpp") elseif (UNIX) file(GLOB headers "XWindows*.h") file(GLOB sources "XWindows*.cpp") diff --git a/src/lib/platform/IKeyResource.cpp b/src/lib/platform/IKeyResource.cpp new file mode 100644 index 00000000..81e86a93 --- /dev/null +++ b/src/lib/platform/IKeyResource.cpp @@ -0,0 +1,193 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform/IKeyResource.h" + +#include + +// +// OSXKeyState::KeyResource +// + +KeyID +IKeyResource::getKeyID(UInt8 c) +{ + if (c == 0) { + return kKeyNone; + } + else if (c >= 32 && c < 127) { + // ASCII + return static_cast(c); + } + else { + // handle special keys + switch (c) { + case 0x01: + return kKeyHome; + + case 0x02: + return kKeyKP_Enter; + + case 0x03: + return kKeyKP_Enter; + + case 0x04: + return kKeyEnd; + + case 0x05: + return kKeyHelp; + + case 0x08: + return kKeyBackSpace; + + case 0x09: + return kKeyTab; + + case 0x0b: + return kKeyPageUp; + + case 0x0c: + return kKeyPageDown; + + case 0x0d: + return kKeyReturn; + + case 0x10: + // OS X maps all the function keys (F1, etc) to this one key. + // we can't determine the right key here so we have to do it + // some other way. + return kKeyNone; + + case 0x1b: + return kKeyEscape; + + case 0x1c: + return kKeyLeft; + + case 0x1d: + return kKeyRight; + + case 0x1e: + return kKeyUp; + + case 0x1f: + return kKeyDown; + + case 0x7f: + return kKeyDelete; + + case 0x06: + case 0x07: + case 0x0a: + case 0x0e: + case 0x0f: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + // discard other control characters + return kKeyNone; + + default: + // not special or unknown + break; + } + + // create string with character + char str[2]; + str[0] = static_cast(c); + str[1] = 0; + + // get current keyboard script + TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource(); + CFArrayRef langs = (CFArrayRef) TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages); + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding( + (CFStringRef)CFArrayGetValueAtIndex(langs, 0)); + // convert to unicode + CFStringRef cfString = + CFStringCreateWithCStringNoCopy( + kCFAllocatorDefault, str, encoding, kCFAllocatorNull); + + // sometimes CFStringCreate...() returns NULL (e.g. Apple Korean + // encoding with char value 214). if it did then make no key, + // otherwise CFStringCreateMutableCopy() will crash. + if (cfString == NULL) { + return kKeyNone; + } + + // convert to precomposed + CFMutableStringRef mcfString = + CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); + CFRelease(cfString); + CFStringNormalize(mcfString, kCFStringNormalizationFormC); + + // check result + int unicodeLength = CFStringGetLength(mcfString); + if (unicodeLength == 0) { + CFRelease(mcfString); + return kKeyNone; + } + if (unicodeLength > 1) { + // FIXME -- more than one character, we should handle this + CFRelease(mcfString); + return kKeyNone; + } + + // get unicode character + UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); + CFRelease(mcfString); + + // convert to KeyID + return static_cast(uc); + } +} + +KeyID +IKeyResource::unicharToKeyID(UniChar c) +{ + switch (c) { + case 3: + return kKeyKP_Enter; + + case 8: + return kKeyBackSpace; + + case 9: + return kKeyTab; + + case 13: + return kKeyReturn; + + case 27: + return kKeyEscape; + + case 127: + return kKeyDelete; + + default: + if (c < 32) { + return kKeyNone; + } + return static_cast(c); + } +} diff --git a/src/lib/platform/IKeyResource.h b/src/lib/platform/IKeyResource.h new file mode 100644 index 00000000..cc71cf39 --- /dev/null +++ b/src/lib/platform/IKeyResource.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "synergy/KeyState.h" + +class IKeyResource : public IInterface { +public: + virtual bool isValid() const = 0; + virtual UInt32 getNumModifierCombinations() const = 0; + virtual UInt32 getNumTables() const = 0; + virtual UInt32 getNumButtons() const = 0; + virtual UInt32 getTableForModifier(UInt32 mask) const = 0; + virtual KeyID getKey(UInt32 table, UInt32 button) const = 0; + + // Convert a character in the current script to the equivalent KeyID + static KeyID getKeyID(UInt8); + + // Convert a unicode character to the equivalent KeyID. + static KeyID unicharToKeyID(UniChar); +}; diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index bcf65fe1..dbba0e09 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -17,6 +17,7 @@ */ #include "platform/OSXKeyState.h" +#include "platform/UchrKeyResource.h" #include "arch/Arch.h" #include "base/Log.h" @@ -296,7 +297,7 @@ OSXKeyState::mapKeyFromEvent(KeyIDs& ids, if (count != 0 || m_deadKeyState == 0) { m_deadKeyState = 0; for (UniCharCount i = 0; i < count; ++i) { - ids.push_back(KeyResource::unicharToKeyID(chars[i])); + ids.push_back(IKeyResource::unicharToKeyID(chars[i])); } adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); @@ -576,7 +577,7 @@ OSXKeyState::getKeyMapForSpecialKeys(synergy::KeyMap& keyMap, SInt32 group) cons bool OSXKeyState::getKeyMap(synergy::KeyMap& keyMap, - SInt32 group, const KeyResource& r) const + SInt32 group, const IKeyResource& r) const { if (!r.isValid()) { return false; @@ -850,454 +851,3 @@ OSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton) { return static_cast(keyButton - KeyButtonOffset); } - - -// -// OSXKeyState::KeyResource -// - -KeyID -OSXKeyState::KeyResource::getKeyID(UInt8 c) -{ - if (c == 0) { - return kKeyNone; - } - else if (c >= 32 && c < 127) { - // ASCII - return static_cast(c); - } - else { - // handle special keys - switch (c) { - case 0x01: - return kKeyHome; - - case 0x02: - return kKeyKP_Enter; - - case 0x03: - return kKeyKP_Enter; - - case 0x04: - return kKeyEnd; - - case 0x05: - return kKeyHelp; - - case 0x08: - return kKeyBackSpace; - - case 0x09: - return kKeyTab; - - case 0x0b: - return kKeyPageUp; - - case 0x0c: - return kKeyPageDown; - - case 0x0d: - return kKeyReturn; - - case 0x10: - // OS X maps all the function keys (F1, etc) to this one key. - // we can't determine the right key here so we have to do it - // some other way. - return kKeyNone; - - case 0x1b: - return kKeyEscape; - - case 0x1c: - return kKeyLeft; - - case 0x1d: - return kKeyRight; - - case 0x1e: - return kKeyUp; - - case 0x1f: - return kKeyDown; - - case 0x7f: - return kKeyDelete; - - case 0x06: - case 0x07: - case 0x0a: - case 0x0e: - case 0x0f: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - // discard other control characters - return kKeyNone; - - default: - // not special or unknown - break; - } - - // create string with character - char str[2]; - str[0] = static_cast(c); - str[1] = 0; - - // get current keyboard script - TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource(); - CFArrayRef langs = (CFArrayRef) TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages); - CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding( - (CFStringRef)CFArrayGetValueAtIndex(langs, 0)); - // convert to unicode - CFStringRef cfString = - CFStringCreateWithCStringNoCopy( - kCFAllocatorDefault, str, encoding, kCFAllocatorNull); - - // sometimes CFStringCreate...() returns NULL (e.g. Apple Korean - // encoding with char value 214). if it did then make no key, - // otherwise CFStringCreateMutableCopy() will crash. - if (cfString == NULL) { - return kKeyNone; - } - - // convert to precomposed - CFMutableStringRef mcfString = - CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); - CFRelease(cfString); - CFStringNormalize(mcfString, kCFStringNormalizationFormC); - - // check result - int unicodeLength = CFStringGetLength(mcfString); - if (unicodeLength == 0) { - CFRelease(mcfString); - return kKeyNone; - } - if (unicodeLength > 1) { - // FIXME -- more than one character, we should handle this - CFRelease(mcfString); - return kKeyNone; - } - - // get unicode character - UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); - CFRelease(mcfString); - - // convert to KeyID - return static_cast(uc); - } -} - -KeyID -OSXKeyState::KeyResource::unicharToKeyID(UniChar c) -{ - switch (c) { - case 3: - return kKeyKP_Enter; - - case 8: - return kKeyBackSpace; - - case 9: - return kKeyTab; - - case 13: - return kKeyReturn; - - case 27: - return kKeyEscape; - - case 127: - return kKeyDelete; - - default: - if (c < 32) { - return kKeyNone; - } - return static_cast(c); - } -} - - -// -// OSXKeyState::UchrKeyResource -// - -OSXKeyState::UchrKeyResource::UchrKeyResource(const void* resource, - UInt32 keyboardType) : - m_m(NULL), - m_cti(NULL), - m_sdi(NULL), - m_sri(NULL), - m_st(NULL) -{ - m_resource = reinterpret_cast(resource); - if (m_resource == NULL) { - return; - } - - // find the keyboard info for the current keyboard type - const UCKeyboardTypeHeader* th = NULL; - const UCKeyboardLayout* r = m_resource; - for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { - if (keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && - keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { - th = r->keyboardTypeList + i; - break; - } - if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { - // found the default. use it unless we find a match. - th = r->keyboardTypeList + i; - } - } - if (th == NULL) { - // cannot find a suitable keyboard type - return; - } - - // get tables for keyboard type - const UInt8* base = reinterpret_cast(m_resource); - m_m = reinterpret_cast(base + - th->keyModifiersToTableNumOffset); - m_cti = reinterpret_cast(base + - th->keyToCharTableIndexOffset); - m_sdi = reinterpret_cast(base + - th->keySequenceDataIndexOffset); - if (th->keyStateRecordsIndexOffset != 0) { - m_sri = reinterpret_cast(base + - th->keyStateRecordsIndexOffset); - } - if (th->keyStateTerminatorsOffset != 0) { - m_st = reinterpret_cast(base + - th->keyStateTerminatorsOffset); - } - - // find the space key, but only if it can combine with dead keys. - // a dead key followed by a space yields the non-dead version of - // the dead key. - m_spaceOutput = 0xffffu; - UInt32 table = getTableForModifier(0); - for (UInt32 button = 0, n = getNumButtons(); button < n; ++button) { - KeyID id = getKey(table, button); - if (id == 0x20) { - UCKeyOutput c = - reinterpret_cast(base + - m_cti->keyToCharTableOffsets[table])[button]; - if ((c & kUCKeyOutputTestForIndexMask) == - kUCKeyOutputStateIndexMask) { - m_spaceOutput = (c & kUCKeyOutputGetIndexMask); - break; - } - } - } -} - -bool -OSXKeyState::UchrKeyResource::isValid() const -{ - return (m_m != NULL); -} - -UInt32 -OSXKeyState::UchrKeyResource::getNumModifierCombinations() const -{ - // only 32 (not 256) because the righthanded modifier bits are ignored - return 32; -} - -UInt32 -OSXKeyState::UchrKeyResource::getNumTables() const -{ - return m_cti->keyToCharTableCount; -} - -UInt32 -OSXKeyState::UchrKeyResource::getNumButtons() const -{ - return m_cti->keyToCharTableSize; -} - -UInt32 -OSXKeyState::UchrKeyResource::getTableForModifier(UInt32 mask) const -{ - if (mask >= m_m->modifiersCount) { - return m_m->defaultTableNum; - } - else { - return m_m->tableNum[mask]; - } -} - -KeyID -OSXKeyState::UchrKeyResource::getKey(UInt32 table, UInt32 button) const -{ - assert(table < getNumTables()); - assert(button < getNumButtons()); - - const UInt8* base = reinterpret_cast(m_resource); - const UCKeyOutput* cPtr = reinterpret_cast(base + - m_cti->keyToCharTableOffsets[table]); - - const UCKeyOutput c = cPtr[button]; - - KeySequence keys; - switch (c & kUCKeyOutputTestForIndexMask) { - case kUCKeyOutputStateIndexMask: - if (!getDeadKey(keys, c & kUCKeyOutputGetIndexMask)) { - return kKeyNone; - } - break; - - case kUCKeyOutputSequenceIndexMask: - default: - if (!addSequence(keys, c)) { - return kKeyNone; - } - break; - } - - // XXX -- no support for multiple characters - if (keys.size() != 1) { - return kKeyNone; - } - - return keys.front(); -} - -bool -OSXKeyState::UchrKeyResource::getDeadKey( - KeySequence& keys, UInt16 index) const -{ - if (m_sri == NULL || index >= m_sri->keyStateRecordCount) { - // XXX -- should we be using some other fallback? - return false; - } - - UInt16 state = 0; - if (!getKeyRecord(keys, index, state)) { - return false; - } - if (state == 0) { - // not a dead key - return true; - } - - // no dead keys if we couldn't find the space key - if (m_spaceOutput == 0xffffu) { - return false; - } - - // the dead key should not have put anything in the key list - if (!keys.empty()) { - return false; - } - - // get the character generated by pressing the space key after the - // dead key. if we're still in a compose state afterwards then we're - // confused so we bail. - if (!getKeyRecord(keys, m_spaceOutput, state) || state != 0) { - return false; - } - - // convert keys to their dead counterparts - for (KeySequence::iterator i = keys.begin(); i != keys.end(); ++i) { - *i = synergy::KeyMap::getDeadKey(*i); - } - - return true; -} - -bool -OSXKeyState::UchrKeyResource::getKeyRecord( - KeySequence& keys, UInt16 index, UInt16& state) const -{ - const UInt8* base = reinterpret_cast(m_resource); - const UCKeyStateRecord* sr = - reinterpret_cast(base + - m_sri->keyStateRecordOffsets[index]); - const UCKeyStateEntryTerminal* kset = - reinterpret_cast(sr->stateEntryData); - - UInt16 nextState = 0; - bool found = false; - if (state == 0) { - found = true; - nextState = sr->stateZeroNextState; - if (!addSequence(keys, sr->stateZeroCharData)) { - return false; - } - } - else { - // we have a next entry - switch (sr->stateEntryFormat) { - case kUCKeyStateEntryTerminalFormat: - for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { - if (kset[j].curState == state) { - if (!addSequence(keys, kset[j].charData)) { - return false; - } - nextState = 0; - found = true; - break; - } - } - break; - - case kUCKeyStateEntryRangeFormat: - // XXX -- not supported yet - break; - - default: - // XXX -- unknown format - return false; - } - } - if (!found) { - // use a terminator - if (m_st != NULL && state < m_st->keyStateTerminatorCount) { - if (!addSequence(keys, m_st->keyStateTerminators[state - 1])) { - return false; - } - } - nextState = sr->stateZeroNextState; - if (!addSequence(keys, sr->stateZeroCharData)) { - return false; - } - } - - // next - state = nextState; - - return true; -} - -bool -OSXKeyState::UchrKeyResource::addSequence( - KeySequence& keys, UCKeyCharSeq c) const -{ - if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { - UInt16 index = (c & kUCKeyOutputGetIndexMask); - if (index < m_sdi->charSequenceCount && - m_sdi->charSequenceOffsets[index] != - m_sdi->charSequenceOffsets[index + 1]) { - // XXX -- sequences not supported yet - return false; - } - } - - if (c != 0xfffe && c != 0xffff) { - KeyID id = unicharToKeyID(c); - if (id != kKeyNone) { - keys.push_back(id); - } - } - - return true; -} diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 9b90e224..8eb59118 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -26,6 +26,7 @@ #include typedef TISInputSourceRef KeyLayout; +class IKeyResource; //! OS X key state /*! @@ -112,7 +113,7 @@ private: // Convert keyboard resource to a key map bool getKeyMap(synergy::KeyMap& keyMap, - SInt32 group, const KeyResource& r) const; + SInt32 group, const IKeyResource& r) const; // Get the available keyboard groups bool getGroups(GroupList&) const; @@ -149,53 +150,6 @@ private: void init(); private: - class KeyResource : public IInterface { - public: - virtual bool isValid() const = 0; - virtual UInt32 getNumModifierCombinations() const = 0; - virtual UInt32 getNumTables() const = 0; - virtual UInt32 getNumButtons() const = 0; - virtual UInt32 getTableForModifier(UInt32 mask) const = 0; - virtual KeyID getKey(UInt32 table, UInt32 button) const = 0; - - // Convert a character in the current script to the equivalent KeyID - static KeyID getKeyID(UInt8); - - // Convert a unicode character to the equivalent KeyID. - static KeyID unicharToKeyID(UniChar); - }; - - - class UchrKeyResource : public KeyResource { - public: - UchrKeyResource(const void*, UInt32 keyboardType); - - // KeyResource overrides - virtual bool isValid() const; - virtual UInt32 getNumModifierCombinations() const; - virtual UInt32 getNumTables() const; - virtual UInt32 getNumButtons() const; - virtual UInt32 getTableForModifier(UInt32 mask) const; - virtual KeyID getKey(UInt32 table, UInt32 button) const; - - private: - typedef std::vector KeySequence; - - bool getDeadKey(KeySequence& keys, UInt16 index) const; - bool getKeyRecord(KeySequence& keys, - UInt16 index, UInt16& state) const; - bool addSequence(KeySequence& keys, UCKeyCharSeq c) const; - - private: - const UCKeyboardLayout* m_resource; - const UCKeyModifiersToTableNum* m_m; - const UCKeyToCharTableIndex* m_cti; - const UCKeySequenceDataIndex* m_sdi; - const UCKeyStateRecordsIndex* m_sri; - const UCKeyStateTerminators* m_st; - UInt16 m_spaceOutput; - }; - // OS X uses a physical key if 0 for the 'A' key. synergy reserves // KeyButton 0 so we offset all OS X physical key ids by this much // when used as a KeyButton and by minus this much to map a KeyButton diff --git a/src/lib/platform/UchrKeyResource.cpp b/src/lib/platform/UchrKeyResource.cpp new file mode 100644 index 00000000..21480988 --- /dev/null +++ b/src/lib/platform/UchrKeyResource.cpp @@ -0,0 +1,296 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform/UchrKeyResource.h" + +#include + +// +// OSXKeyState::UchrKeyResource +// + +UchrKeyResource::UchrKeyResource(const void* resource, + UInt32 keyboardType) : + m_m(NULL), + m_cti(NULL), + m_sdi(NULL), + m_sri(NULL), + m_st(NULL) +{ + m_resource = reinterpret_cast(resource); + if (m_resource == NULL) { + return; + } + + // find the keyboard info for the current keyboard type + const UCKeyboardTypeHeader* th = NULL; + const UCKeyboardLayout* r = m_resource; + for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { + if (keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && + keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { + th = r->keyboardTypeList + i; + break; + } + if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { + // found the default. use it unless we find a match. + th = r->keyboardTypeList + i; + } + } + if (th == NULL) { + // cannot find a suitable keyboard type + return; + } + + // get tables for keyboard type + const UInt8* base = reinterpret_cast(m_resource); + m_m = reinterpret_cast(base + + th->keyModifiersToTableNumOffset); + m_cti = reinterpret_cast(base + + th->keyToCharTableIndexOffset); + m_sdi = reinterpret_cast(base + + th->keySequenceDataIndexOffset); + if (th->keyStateRecordsIndexOffset != 0) { + m_sri = reinterpret_cast(base + + th->keyStateRecordsIndexOffset); + } + if (th->keyStateTerminatorsOffset != 0) { + m_st = reinterpret_cast(base + + th->keyStateTerminatorsOffset); + } + + // find the space key, but only if it can combine with dead keys. + // a dead key followed by a space yields the non-dead version of + // the dead key. + m_spaceOutput = 0xffffu; + UInt32 table = getTableForModifier(0); + for (UInt32 button = 0, n = getNumButtons(); button < n; ++button) { + KeyID id = getKey(table, button); + if (id == 0x20) { + UCKeyOutput c = + reinterpret_cast(base + + m_cti->keyToCharTableOffsets[table])[button]; + if ((c & kUCKeyOutputTestForIndexMask) == + kUCKeyOutputStateIndexMask) { + m_spaceOutput = (c & kUCKeyOutputGetIndexMask); + break; + } + } + } +} + +bool +UchrKeyResource::isValid() const +{ + return (m_m != NULL); +} + +UInt32 +UchrKeyResource::getNumModifierCombinations() const +{ + // only 32 (not 256) because the righthanded modifier bits are ignored + return 32; +} + +UInt32 +UchrKeyResource::getNumTables() const +{ + return m_cti->keyToCharTableCount; +} + +UInt32 +UchrKeyResource::getNumButtons() const +{ + return m_cti->keyToCharTableSize; +} + +UInt32 +UchrKeyResource::getTableForModifier(UInt32 mask) const +{ + if (mask >= m_m->modifiersCount) { + return m_m->defaultTableNum; + } + else { + return m_m->tableNum[mask]; + } +} + +KeyID +UchrKeyResource::getKey(UInt32 table, UInt32 button) const +{ + assert(table < getNumTables()); + assert(button < getNumButtons()); + + const UInt8* base = reinterpret_cast(m_resource); + const UCKeyOutput* cPtr = reinterpret_cast(base + + m_cti->keyToCharTableOffsets[table]); + + const UCKeyOutput c = cPtr[button]; + + KeySequence keys; + switch (c & kUCKeyOutputTestForIndexMask) { + case kUCKeyOutputStateIndexMask: + if (!getDeadKey(keys, c & kUCKeyOutputGetIndexMask)) { + return kKeyNone; + } + break; + + case kUCKeyOutputSequenceIndexMask: + default: + if (!addSequence(keys, c)) { + return kKeyNone; + } + break; + } + + // XXX -- no support for multiple characters + if (keys.size() != 1) { + return kKeyNone; + } + + return keys.front(); +} + +bool +UchrKeyResource::getDeadKey( + KeySequence& keys, UInt16 index) const +{ + if (m_sri == NULL || index >= m_sri->keyStateRecordCount) { + // XXX -- should we be using some other fallback? + return false; + } + + UInt16 state = 0; + if (!getKeyRecord(keys, index, state)) { + return false; + } + if (state == 0) { + // not a dead key + return true; + } + + // no dead keys if we couldn't find the space key + if (m_spaceOutput == 0xffffu) { + return false; + } + + // the dead key should not have put anything in the key list + if (!keys.empty()) { + return false; + } + + // get the character generated by pressing the space key after the + // dead key. if we're still in a compose state afterwards then we're + // confused so we bail. + if (!getKeyRecord(keys, m_spaceOutput, state) || state != 0) { + return false; + } + + // convert keys to their dead counterparts + for (KeySequence::iterator i = keys.begin(); i != keys.end(); ++i) { + *i = synergy::KeyMap::getDeadKey(*i); + } + + return true; +} + +bool +UchrKeyResource::getKeyRecord( + KeySequence& keys, UInt16 index, UInt16& state) const +{ + const UInt8* base = reinterpret_cast(m_resource); + const UCKeyStateRecord* sr = + reinterpret_cast(base + + m_sri->keyStateRecordOffsets[index]); + const UCKeyStateEntryTerminal* kset = + reinterpret_cast(sr->stateEntryData); + + UInt16 nextState = 0; + bool found = false; + if (state == 0) { + found = true; + nextState = sr->stateZeroNextState; + if (!addSequence(keys, sr->stateZeroCharData)) { + return false; + } + } + else { + // we have a next entry + switch (sr->stateEntryFormat) { + case kUCKeyStateEntryTerminalFormat: + for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { + if (kset[j].curState == state) { + if (!addSequence(keys, kset[j].charData)) { + return false; + } + nextState = 0; + found = true; + break; + } + } + break; + + case kUCKeyStateEntryRangeFormat: + // XXX -- not supported yet + break; + + default: + // XXX -- unknown format + return false; + } + } + if (!found) { + // use a terminator + if (m_st != NULL && state < m_st->keyStateTerminatorCount) { + if (!addSequence(keys, m_st->keyStateTerminators[state - 1])) { + return false; + } + } + nextState = sr->stateZeroNextState; + if (!addSequence(keys, sr->stateZeroCharData)) { + return false; + } + } + + // next + state = nextState; + + return true; +} + +bool +UchrKeyResource::addSequence( + KeySequence& keys, UCKeyCharSeq c) const +{ + if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { + UInt16 index = (c & kUCKeyOutputGetIndexMask); + if (index < m_sdi->charSequenceCount && + m_sdi->charSequenceOffsets[index] != + m_sdi->charSequenceOffsets[index + 1]) { + // XXX -- sequences not supported yet + return false; + } + } + + if (c != 0xfffe && c != 0xffff) { + KeyID id = unicharToKeyID(c); + if (id != kKeyNone) { + keys.push_back(id); + } + } + + return true; +} diff --git a/src/lib/platform/UchrKeyResource.h b/src/lib/platform/UchrKeyResource.h new file mode 100644 index 00000000..67107ee5 --- /dev/null +++ b/src/lib/platform/UchrKeyResource.h @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "synergy/KeyState.h" +#include "platform/IKeyResource.h" + +#include + +typedef TISInputSourceRef KeyLayout; + +class UchrKeyResource : public IKeyResource { +public: + UchrKeyResource(const void*, UInt32 keyboardType); + + // KeyResource overrides + virtual bool isValid() const; + virtual UInt32 getNumModifierCombinations() const; + virtual UInt32 getNumTables() const; + virtual UInt32 getNumButtons() const; + virtual UInt32 getTableForModifier(UInt32 mask) const; + virtual KeyID getKey(UInt32 table, UInt32 button) const; + +private: + typedef std::vector KeySequence; + + bool getDeadKey(KeySequence& keys, UInt16 index) const; + bool getKeyRecord(KeySequence& keys, + UInt16 index, UInt16& state) const; + bool addSequence(KeySequence& keys, UCKeyCharSeq c) const; + +private: + const UCKeyboardLayout* m_resource; + const UCKeyModifiersToTableNum* m_m; + const UCKeyToCharTableIndex* m_cti; + const UCKeySequenceDataIndex* m_sdi; + const UCKeyStateRecordsIndex* m_sri; + const UCKeyStateTerminators* m_st; + UInt16 m_spaceOutput; +};