Refactor KeyResource into own files #2765

This commit is contained in:
Jerry (Xinyu Hou) 2016-04-21 13:59:47 +01:00
parent ed0888880c
commit 7f786cc884
8 changed files with 588 additions and 503 deletions

View File

@ -18,6 +18,7 @@
#ifndef COMMANDTHREAD_H #ifndef COMMANDTHREAD_H
#define COMMANDTHREAD_H #define COMMANDTHREAD_H
#include <QObject>
#include <QStringList> #include <QStringList>
class CommandProcess : public QObject class CommandProcess : public QObject

View File

@ -18,8 +18,8 @@ if (WIN32)
file(GLOB headers "MSWindows*.h") file(GLOB headers "MSWindows*.h")
file(GLOB sources "MSWindows*.cpp") file(GLOB sources "MSWindows*.cpp")
elseif (APPLE) elseif (APPLE)
file(GLOB headers "OSX*.h") file(GLOB headers "OSX*.h" "*KeyResource.h")
file(GLOB sources "OSX*.cpp" "OSX*.m") file(GLOB sources "OSX*.cpp" "OSX*.m" "*KeyResource.cpp")
elseif (UNIX) elseif (UNIX)
file(GLOB headers "XWindows*.h") file(GLOB headers "XWindows*.h")
file(GLOB sources "XWindows*.cpp") file(GLOB sources "XWindows*.cpp")

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "platform/IKeyResource.h"
#include <Carbon/Carbon.h>
//
// OSXKeyState::KeyResource
//
KeyID
IKeyResource::getKeyID(UInt8 c)
{
if (c == 0) {
return kKeyNone;
}
else if (c >= 32 && c < 127) {
// ASCII
return static_cast<KeyID>(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<char>(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<KeyID>(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<KeyID>(c);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
};

View File

@ -17,6 +17,7 @@
*/ */
#include "platform/OSXKeyState.h" #include "platform/OSXKeyState.h"
#include "platform/UchrKeyResource.h"
#include "arch/Arch.h" #include "arch/Arch.h"
#include "base/Log.h" #include "base/Log.h"
@ -296,7 +297,7 @@ OSXKeyState::mapKeyFromEvent(KeyIDs& ids,
if (count != 0 || m_deadKeyState == 0) { if (count != 0 || m_deadKeyState == 0) {
m_deadKeyState = 0; m_deadKeyState = 0;
for (UniCharCount i = 0; i < count; ++i) { 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); adjustAltGrModifier(ids, maskOut, isCommand);
return mapVirtualKeyToKeyButton(vkCode); return mapVirtualKeyToKeyButton(vkCode);
@ -576,7 +577,7 @@ OSXKeyState::getKeyMapForSpecialKeys(synergy::KeyMap& keyMap, SInt32 group) cons
bool bool
OSXKeyState::getKeyMap(synergy::KeyMap& keyMap, OSXKeyState::getKeyMap(synergy::KeyMap& keyMap,
SInt32 group, const KeyResource& r) const SInt32 group, const IKeyResource& r) const
{ {
if (!r.isValid()) { if (!r.isValid()) {
return false; return false;
@ -850,454 +851,3 @@ OSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton)
{ {
return static_cast<UInt32>(keyButton - KeyButtonOffset); return static_cast<UInt32>(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<KeyID>(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<char>(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<KeyID>(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<KeyID>(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<const UCKeyboardLayout*>(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<const UInt8*>(m_resource);
m_m = reinterpret_cast<const UCKeyModifiersToTableNum*>(base +
th->keyModifiersToTableNumOffset);
m_cti = reinterpret_cast<const UCKeyToCharTableIndex*>(base +
th->keyToCharTableIndexOffset);
m_sdi = reinterpret_cast<const UCKeySequenceDataIndex*>(base +
th->keySequenceDataIndexOffset);
if (th->keyStateRecordsIndexOffset != 0) {
m_sri = reinterpret_cast<const UCKeyStateRecordsIndex*>(base +
th->keyStateRecordsIndexOffset);
}
if (th->keyStateTerminatorsOffset != 0) {
m_st = reinterpret_cast<const UCKeyStateTerminators*>(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<const UCKeyOutput*>(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<const UInt8*>(m_resource);
const UCKeyOutput* cPtr = reinterpret_cast<const UCKeyOutput*>(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<const UInt8*>(m_resource);
const UCKeyStateRecord* sr =
reinterpret_cast<const UCKeyStateRecord*>(base +
m_sri->keyStateRecordOffsets[index]);
const UCKeyStateEntryTerminal* kset =
reinterpret_cast<const UCKeyStateEntryTerminal*>(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;
}

View File

@ -26,6 +26,7 @@
#include <Carbon/Carbon.h> #include <Carbon/Carbon.h>
typedef TISInputSourceRef KeyLayout; typedef TISInputSourceRef KeyLayout;
class IKeyResource;
//! OS X key state //! OS X key state
/*! /*!
@ -112,7 +113,7 @@ private:
// Convert keyboard resource to a key map // Convert keyboard resource to a key map
bool getKeyMap(synergy::KeyMap& keyMap, bool getKeyMap(synergy::KeyMap& keyMap,
SInt32 group, const KeyResource& r) const; SInt32 group, const IKeyResource& r) const;
// Get the available keyboard groups // Get the available keyboard groups
bool getGroups(GroupList&) const; bool getGroups(GroupList&) const;
@ -149,53 +150,6 @@ private:
void init(); void init();
private: 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<KeyID> 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 // 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 // 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 // when used as a KeyButton and by minus this much to map a KeyButton

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "platform/UchrKeyResource.h"
#include <Carbon/Carbon.h>
//
// 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<const UCKeyboardLayout*>(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<const UInt8*>(m_resource);
m_m = reinterpret_cast<const UCKeyModifiersToTableNum*>(base +
th->keyModifiersToTableNumOffset);
m_cti = reinterpret_cast<const UCKeyToCharTableIndex*>(base +
th->keyToCharTableIndexOffset);
m_sdi = reinterpret_cast<const UCKeySequenceDataIndex*>(base +
th->keySequenceDataIndexOffset);
if (th->keyStateRecordsIndexOffset != 0) {
m_sri = reinterpret_cast<const UCKeyStateRecordsIndex*>(base +
th->keyStateRecordsIndexOffset);
}
if (th->keyStateTerminatorsOffset != 0) {
m_st = reinterpret_cast<const UCKeyStateTerminators*>(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<const UCKeyOutput*>(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<const UInt8*>(m_resource);
const UCKeyOutput* cPtr = reinterpret_cast<const UCKeyOutput*>(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<const UInt8*>(m_resource);
const UCKeyStateRecord* sr =
reinterpret_cast<const UCKeyStateRecord*>(base +
m_sri->keyStateRecordOffsets[index]);
const UCKeyStateEntryTerminal* kset =
reinterpret_cast<const UCKeyStateEntryTerminal*>(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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "synergy/KeyState.h"
#include "platform/IKeyResource.h"
#include <Carbon/Carbon.h>
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<KeyID> 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;
};