mirror of
https://github.com/debauchee/barrier.git
synced 2026-02-08 12:53:53 +08:00
297 lines
8.4 KiB
C++
297 lines
8.4 KiB
C++
/*
|
|
* barrier -- 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/OSXUchrKeyResource.h"
|
|
|
|
#include <Carbon/Carbon.h>
|
|
|
|
//
|
|
// OSXUchrKeyResource
|
|
//
|
|
|
|
OSXUchrKeyResource::OSXUchrKeyResource(const void* resource,
|
|
UInt32 keyboardType) :
|
|
m_m(NULL),
|
|
m_cti(NULL),
|
|
m_sdi(NULL),
|
|
m_sri(NULL),
|
|
m_st(NULL)
|
|
{
|
|
m_resource = static_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* const 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
|
|
OSXUchrKeyResource::isValid() const
|
|
{
|
|
return (m_m != NULL);
|
|
}
|
|
|
|
UInt32
|
|
OSXUchrKeyResource::getNumModifierCombinations() const
|
|
{
|
|
// only 32 (not 256) because the righthanded modifier bits are ignored
|
|
return 32;
|
|
}
|
|
|
|
UInt32
|
|
OSXUchrKeyResource::getNumTables() const
|
|
{
|
|
return m_cti->keyToCharTableCount;
|
|
}
|
|
|
|
UInt32
|
|
OSXUchrKeyResource::getNumButtons() const
|
|
{
|
|
return m_cti->keyToCharTableSize;
|
|
}
|
|
|
|
UInt32
|
|
OSXUchrKeyResource::getTableForModifier(UInt32 mask) const
|
|
{
|
|
if (mask >= m_m->modifiersCount) {
|
|
return m_m->defaultTableNum;
|
|
}
|
|
else {
|
|
return m_m->tableNum[mask];
|
|
}
|
|
}
|
|
|
|
KeyID
|
|
OSXUchrKeyResource::getKey(UInt32 table, UInt32 button) const
|
|
{
|
|
assert(table < getNumTables());
|
|
assert(button < getNumButtons());
|
|
|
|
const UInt8* const 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
|
|
OSXUchrKeyResource::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 = barrier::KeyMap::getDeadKey(*i);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
OSXUchrKeyResource::getKeyRecord(
|
|
KeySequence& keys, UInt16 index, UInt16& state) const
|
|
{
|
|
const UInt8* const 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
|
|
OSXUchrKeyResource::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;
|
|
}
|