mirror of
https://github.com/debauchee/barrier.git
synced 2024-11-23 20:12:39 +03:00
Refactor KeyResource into own files #2765
This commit is contained in:
parent
ed0888880c
commit
7f786cc884
@ -18,6 +18,7 @@
|
||||
#ifndef COMMANDTHREAD_H
|
||||
#define COMMANDTHREAD_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
class CommandProcess : public QObject
|
||||
|
@ -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")
|
||||
|
193
src/lib/platform/IKeyResource.cpp
Normal file
193
src/lib/platform/IKeyResource.cpp
Normal 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);
|
||||
}
|
||||
}
|
36
src/lib/platform/IKeyResource.h
Normal file
36
src/lib/platform/IKeyResource.h
Normal 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);
|
||||
};
|
@ -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<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;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
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<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
|
||||
// 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
|
||||
|
296
src/lib/platform/UchrKeyResource.cpp
Normal file
296
src/lib/platform/UchrKeyResource.cpp
Normal 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;
|
||||
}
|
55
src/lib/platform/UchrKeyResource.h
Normal file
55
src/lib/platform/UchrKeyResource.h
Normal 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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user