mirror of
https://github.com/debauchee/barrier.git
synced 2024-12-29 14:12:37 +03:00
9f6c8f937a
now includes full support for dead keys and non-ascii glyph keys.
749 lines
20 KiB
C++
749 lines
20 KiB
C++
/*
|
|
* synergy -- mouse and keyboard sharing utility
|
|
* Copyright (C) 2004 Chris Schoeneman
|
|
*
|
|
* 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 COPYING 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.
|
|
*/
|
|
|
|
#include "COSXKeyState.h"
|
|
#include "CLog.h"
|
|
#include <stdio.h>
|
|
|
|
struct CKCHRDeadKeyRecord {
|
|
public:
|
|
UInt8 m_tableIndex;
|
|
UInt8 m_virtualKey;
|
|
SInt16 m_numCompletions;
|
|
UInt8 m_completion[1][2];
|
|
};
|
|
|
|
struct CKCHRDeadKeys {
|
|
public:
|
|
SInt16 m_numRecords;
|
|
CKCHRDeadKeyRecord m_records[1];
|
|
};
|
|
|
|
struct CKeyEntry {
|
|
public:
|
|
KeyID m_keyID;
|
|
UInt32 m_virtualKey;
|
|
};
|
|
// Hardcoded virtual key table. Oddly, Apple doesn't document the
|
|
// meaning of virtual key codes. The whole point of *virtual* key
|
|
// codes is to make them hardware independent so these codes should
|
|
// be constant across OS versions and hardware. Yet they don't
|
|
// tell us what codes map to what keys so we have to figure it out
|
|
// for ourselves.
|
|
//
|
|
// Note that some virtual keys codes appear more than once. The
|
|
// first instance of a virtual key code maps to the KeyID that we
|
|
// want to generate for that code. The others are for mapping
|
|
// different KeyIDs to a single key code.
|
|
static const CKeyEntry s_controlKeys[] = {
|
|
// TTY functions
|
|
{ kKeyBackSpace, 51 },
|
|
{ kKeyTab, 48 },
|
|
{ kKeyLeftTab, 48 },
|
|
{ kKeyReturn, 36 },
|
|
{ kKeyLinefeed, 36 },
|
|
// { kKeyClear, 0xFFFF }, /* no mapping on apple */
|
|
// { kKeyPause, 0xFFFF }, /* no mapping on apple */
|
|
// { kKeyScrollLock, 0xFFFF }, /* no mapping on apple */
|
|
// { kKeySysReq, 0xFFFF }, /* no mapping on apple */
|
|
{ kKeyEscape, 53 },
|
|
{ kKeyDelete, 117 },
|
|
|
|
// cursor control
|
|
{ kKeyHome, 115 },
|
|
{ kKeyBegin, 115 },
|
|
{ kKeyLeft, 123 },
|
|
{ kKeyUp, 126 },
|
|
{ kKeyRight, 124 },
|
|
{ kKeyDown, 125 },
|
|
{ kKeyPageUp, 116 },
|
|
{ kKeyPageDown, 121 },
|
|
{ kKeyEnd, 119 },
|
|
|
|
// numeric keypad
|
|
{ kKeyKP_0, 82 },
|
|
{ kKeyKP_1, 83 },
|
|
{ kKeyKP_2, 84 },
|
|
{ kKeyKP_3, 85 },
|
|
{ kKeyKP_4, 86 },
|
|
{ kKeyKP_5, 87 },
|
|
{ kKeyKP_6, 88 },
|
|
{ kKeyKP_7, 89 },
|
|
{ kKeyKP_8, 91 },
|
|
{ kKeyKP_9, 92 },
|
|
{ kKeyKP_Enter, 76 },
|
|
{ kKeyKP_Decimal, 65 },
|
|
{ kKeyKP_Add, 69 },
|
|
{ kKeyKP_Subtract, 78 },
|
|
{ kKeyKP_Multiply, 67 },
|
|
{ kKeyKP_Divide, 75 },
|
|
|
|
// function keys
|
|
{ kKeyF1, 122 },
|
|
{ kKeyF2, 120 },
|
|
{ kKeyF3, 99 },
|
|
{ kKeyF4, 118 },
|
|
{ kKeyF5, 96 },
|
|
{ kKeyF6, 97 },
|
|
{ kKeyF7, 98 },
|
|
{ kKeyF8, 100 },
|
|
{ kKeyF9, 101 },
|
|
{ kKeyF10, 109 },
|
|
{ kKeyF11, 103 },
|
|
{ kKeyF12, 111 },
|
|
{ kKeyF13, 105 },
|
|
{ kKeyF14, 107 },
|
|
{ kKeyF15, 113 },
|
|
|
|
// misc keys
|
|
{ kKeyHelp, 114 },
|
|
|
|
// modifier keys. i don't know how to make the mac properly
|
|
// interpret the right hand versions of modifier keys so they're
|
|
// currently mapped to the left hand version.
|
|
{ kKeyShift_L, 56 },
|
|
{ kKeyShift_R, 56 /*60*/ },
|
|
{ kKeyControl_L, 59 },
|
|
{ kKeyControl_R, 59 /*62*/ },
|
|
{ kKeyAlt_L, 55 },
|
|
{ kKeyAlt_R, 55 },
|
|
{ kKeySuper_L, 58 },
|
|
{ kKeySuper_R, 58 /*61*/ },
|
|
{ kKeyMeta_L, 58 },
|
|
{ kKeyMeta_R, 58 /*61*/ },
|
|
{ kKeyCapsLock, 57 },
|
|
{ kKeyNumLock, 71 }
|
|
};
|
|
|
|
//
|
|
// COSXKeyState
|
|
//
|
|
|
|
COSXKeyState::COSXKeyState()
|
|
{
|
|
setHalfDuplexMask(0);
|
|
SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript);
|
|
SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys);
|
|
setKeyboardLayout(keyboardLayoutID);
|
|
}
|
|
|
|
COSXKeyState::~COSXKeyState()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
void
|
|
COSXKeyState::sendKeyEvent(void* target,
|
|
bool press, bool isAutoRepeat,
|
|
KeyID key, KeyModifierMask mask,
|
|
SInt32 count, KeyButton button)
|
|
{
|
|
if (press || isAutoRepeat) {
|
|
// send key
|
|
if (press) {
|
|
CKeyState::sendKeyEvent(target, true, false,
|
|
key, mask, 1, button);
|
|
if (count > 0) {
|
|
--count;
|
|
}
|
|
}
|
|
|
|
if (count >= 1) {
|
|
CKeyState::sendKeyEvent(target, true, true,
|
|
key, mask, count, button);
|
|
}
|
|
|
|
}
|
|
else {
|
|
// do key up
|
|
CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button);
|
|
}
|
|
}
|
|
|
|
void
|
|
COSXKeyState::setHalfDuplexMask(KeyModifierMask mask)
|
|
{
|
|
CKeyState::setHalfDuplexMask(mask | KeyModifierCapsLock);
|
|
}
|
|
|
|
bool
|
|
COSXKeyState::fakeCtrlAltDel()
|
|
{
|
|
// pass keys through unchanged
|
|
return false;
|
|
}
|
|
|
|
const char*
|
|
COSXKeyState::getKeyName(KeyButton button) const
|
|
{
|
|
static char name[10];
|
|
sprintf(name, "vk 0x%02x", button);
|
|
return name;
|
|
}
|
|
|
|
void
|
|
COSXKeyState::doUpdateKeys()
|
|
{
|
|
// save key mapping
|
|
m_keyMap.clear();
|
|
if (!filluchrKeysMap(m_keyMap)) {
|
|
fillKCHRKeysMap(m_keyMap);
|
|
}
|
|
fillSpecialKeys(m_keyMap, m_virtualKeyMap);
|
|
|
|
// add modifiers
|
|
KeyButtons keys;
|
|
addKeyButton(keys, kKeyShift_L);
|
|
addKeyButton(keys, kKeyShift_R);
|
|
addModifier(KeyModifierShift, keys);
|
|
keys.clear();
|
|
addKeyButton(keys, kKeyControl_L);
|
|
addKeyButton(keys, kKeyControl_R);
|
|
addModifier(KeyModifierControl, keys);
|
|
keys.clear();
|
|
addKeyButton(keys, kKeyAlt_L);
|
|
addKeyButton(keys, kKeyAlt_R);
|
|
addModifier(KeyModifierAlt, keys);
|
|
keys.clear();
|
|
addKeyButton(keys, kKeySuper_L);
|
|
addKeyButton(keys, kKeySuper_R);
|
|
addModifier(KeyModifierSuper, keys);
|
|
keys.clear();
|
|
addKeyButton(keys, kKeyCapsLock);
|
|
addModifier(KeyModifierCapsLock, keys);
|
|
keys.clear();
|
|
addKeyButton(keys, kKeyNumLock);
|
|
addModifier(KeyModifierNumLock, keys);
|
|
keys.clear();
|
|
|
|
// FIXME -- get the current keyboard state. call setKeyDown()
|
|
// and setToggled() as appropriate.
|
|
}
|
|
|
|
void
|
|
COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool)
|
|
{
|
|
LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press));
|
|
// let system figure out character for us
|
|
CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey(button), press);
|
|
}
|
|
|
|
KeyButton
|
|
COSXKeyState::mapKey(Keystrokes& keys, KeyID id,
|
|
KeyModifierMask /*desiredMask*/,
|
|
bool isAutoRepeat) const
|
|
{
|
|
// look up virtual key
|
|
CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id);
|
|
if (keyIndex == m_keyMap.end()) {
|
|
return 0;
|
|
}
|
|
const CKeySequence& sequence = keyIndex->second;
|
|
if (sequence.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
// FIXME -- for both calls to addKeystrokes below we'd prefer to use
|
|
// a required mask that generates the same character but matches
|
|
// the desiredMask as closely as possible.
|
|
// FIXME -- would prefer to not restore the modifier keys after each
|
|
// dead key since it's unnecessary but we don't have a mechanism
|
|
// for tracking the modifier state without actually updating the
|
|
// internal keyboard state. we'd have to track the state to
|
|
// ensure we adjust the right modifiers for remaining dead keys
|
|
// and the final key.
|
|
|
|
// FIXME -- required modifier masks. we should determine this for
|
|
// each key when parsing the layout resource. for now we'll just
|
|
// force shift, option and caps-lock to always match exactly in
|
|
// addition to whatever other modifiers the key needs.
|
|
// FIXME -- this doesn't work. it forces modifiers when modifiers are
|
|
// pressed (making having two modifiers at once impossible).
|
|
static const KeyModifierMask requiredMask =
|
|
KeyModifierShift |
|
|
KeyModifierSuper |
|
|
KeyModifierCapsLock;
|
|
|
|
// add dead keys
|
|
for (size_t i = 0; i < sequence.size() - 1; ++i) {
|
|
// simulate press
|
|
KeyButton keyButton =
|
|
addKeystrokes(keys, sequence[i].first,
|
|
sequence[i].second,
|
|
sequence[i].second | requiredMask, false);
|
|
|
|
// simulate release
|
|
Keystroke keystroke;
|
|
keystroke.m_key = keyButton;
|
|
keystroke.m_press = false;
|
|
keystroke.m_repeat = false;
|
|
keys.push_back(keystroke);
|
|
}
|
|
|
|
// add final key
|
|
return addKeystrokes(keys, sequence.back().first,
|
|
sequence.back().second,
|
|
sequence.back().second | requiredMask,
|
|
isAutoRepeat);
|
|
}
|
|
|
|
KeyButton
|
|
COSXKeyState::addKeystrokes(Keystrokes& keys, KeyButton keyButton,
|
|
KeyModifierMask desiredMask, KeyModifierMask requiredMask,
|
|
bool isAutoRepeat) const
|
|
{
|
|
// adjust the modifiers
|
|
Keystrokes undo;
|
|
if (!adjustModifiers(keys, undo, desiredMask, requiredMask)) {
|
|
LOG((CLOG_DEBUG2 "failed to adjust modifiers"));
|
|
return 0;
|
|
}
|
|
|
|
// add the key event
|
|
Keystroke keystroke;
|
|
keystroke.m_key = keyButton;
|
|
if (!isAutoRepeat) {
|
|
keystroke.m_press = true;
|
|
keystroke.m_repeat = false;
|
|
keys.push_back(keystroke);
|
|
}
|
|
else {
|
|
keystroke.m_press = false;
|
|
keystroke.m_repeat = true;
|
|
keys.push_back(keystroke);
|
|
keystroke.m_press = true;
|
|
keys.push_back(keystroke);
|
|
}
|
|
|
|
// put undo keystrokes at end of keystrokes in reverse order
|
|
while (!undo.empty()) {
|
|
keys.push_back(undo.back());
|
|
undo.pop_back();
|
|
}
|
|
|
|
return keyButton;
|
|
}
|
|
|
|
bool
|
|
COSXKeyState::adjustModifiers(Keystrokes& keys,
|
|
Keystrokes& undo,
|
|
KeyModifierMask desiredMask,
|
|
KeyModifierMask requiredMask) const
|
|
{
|
|
// for each modifier in requiredMask make sure the current state
|
|
// of that modifier matches the bit in desiredMask.
|
|
for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) {
|
|
if ((mask & requiredMask) != 0) {
|
|
bool active = ((desiredMask & mask) != 0);
|
|
if (!mapModifier(keys, undo, mask, active)) {
|
|
return false;
|
|
}
|
|
requiredMask ^= mask;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
KeyButton
|
|
COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
|
|
KeyModifierMask* maskOut, EventRef event) const
|
|
{
|
|
ids.clear();
|
|
|
|
// map modifier key
|
|
if (maskOut != NULL) {
|
|
KeyModifierMask activeMask = getActiveModifiers();
|
|
activeMask &= ~KeyModifierModeSwitch;
|
|
*maskOut = activeMask;
|
|
}
|
|
|
|
// get virtual key
|
|
UInt32 vkCode;
|
|
GetEventParameter(event, kEventParamKeyCode, typeUInt32,
|
|
NULL, sizeof(vkCode), NULL, &vkCode);
|
|
|
|
// handle up events
|
|
UInt32 eventKind = GetEventKind(event);
|
|
if (eventKind == kEventRawKeyUp) {
|
|
// the id isn't used. we just need the same button we used on
|
|
// the key press. note that we don't use or reset the dead key
|
|
// state; up events should not affect the dead key state.
|
|
ids.push_back(kKeyNone);
|
|
return mapVirtualKeyToKeyButton(vkCode);
|
|
}
|
|
|
|
// check for special keys
|
|
CVirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode);
|
|
if (i != m_virtualKeyMap.end()) {
|
|
m_deadKeyState = 0;
|
|
ids.push_back(i->second);
|
|
return mapVirtualKeyToKeyButton(vkCode);
|
|
}
|
|
|
|
// check for character keys
|
|
if (m_uchrResource != NULL) {
|
|
// FIXME -- implement this
|
|
}
|
|
else if (m_KCHRResource != NULL) {
|
|
// get the event modifiers and remove the command and control
|
|
// keys.
|
|
UInt32 modifiers;
|
|
GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
|
|
NULL, sizeof(modifiers), NULL, &modifiers);
|
|
modifiers &= ~(cmdKey | controlKey | rightControlKey);
|
|
|
|
// build keycode
|
|
UInt16 keycode =
|
|
static_cast<UInt16>((modifiers & 0xff00u) | (vkCode & 0x00ffu));
|
|
|
|
// translate key
|
|
UInt32 result = KeyTranslate(m_KCHRResource, keycode, &m_deadKeyState);
|
|
|
|
// get the characters
|
|
UInt8 c1 = static_cast<UInt8>((result >> 16) & 0xffu);
|
|
UInt8 c2 = static_cast<UInt8>( result & 0xffu);
|
|
if (c2 != 0) {
|
|
m_deadKeyState = 0;
|
|
if (c1 != 0) {
|
|
ids.push_back(charToKeyID(c1));
|
|
}
|
|
ids.push_back(charToKeyID(c2));
|
|
return mapVirtualKeyToKeyButton(vkCode);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
COSXKeyState::addKeyButton(KeyButtons& keys, KeyID id) const
|
|
{
|
|
CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id);
|
|
if (keyIndex == m_keyMap.end()) {
|
|
return;
|
|
}
|
|
keys.push_back(keyIndex->second[0].first);
|
|
}
|
|
|
|
void
|
|
COSXKeyState::handleModifierKeys(void* target,
|
|
KeyModifierMask oldMask, KeyModifierMask newMask)
|
|
{
|
|
// compute changed modifiers
|
|
KeyModifierMask changed = (oldMask ^ newMask);
|
|
|
|
// synthesize changed modifier keys
|
|
if ((changed & KeyModifierShift) != 0) {
|
|
handleModifierKey(target, kKeyShift_L,
|
|
(newMask & KeyModifierShift) != 0);
|
|
}
|
|
if ((changed & KeyModifierControl) != 0) {
|
|
handleModifierKey(target, kKeyControl_L,
|
|
(newMask & KeyModifierControl) != 0);
|
|
}
|
|
if ((changed & KeyModifierAlt) != 0) {
|
|
handleModifierKey(target, kKeyAlt_L,
|
|
(newMask & KeyModifierAlt) != 0);
|
|
}
|
|
if ((changed & KeyModifierSuper) != 0) {
|
|
handleModifierKey(target, kKeySuper_L,
|
|
(newMask & KeyModifierSuper) != 0);
|
|
}
|
|
if ((changed & KeyModifierCapsLock) != 0) {
|
|
handleModifierKey(target, kKeyCapsLock,
|
|
(newMask & KeyModifierCapsLock) != 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
COSXKeyState::handleModifierKey(void* target, KeyID id, bool down)
|
|
{
|
|
CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id);
|
|
if (keyIndex == m_keyMap.end()) {
|
|
return;
|
|
}
|
|
KeyButton button = keyIndex->second[0].first;
|
|
setKeyDown(button, down);
|
|
sendKeyEvent(target, down, false, id, getActiveModifiers(), 0, button);
|
|
}
|
|
|
|
void
|
|
COSXKeyState::checkKeyboardLayout()
|
|
{
|
|
SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript);
|
|
SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys);
|
|
if (keyboardLayoutID != m_keyboardLayoutID) {
|
|
// layout changed
|
|
setKeyboardLayout(keyboardLayoutID);
|
|
doUpdateKeys();
|
|
}
|
|
}
|
|
|
|
void
|
|
COSXKeyState::setKeyboardLayout(SInt16 keyboardLayoutID)
|
|
{
|
|
m_keyboardLayoutID = keyboardLayoutID;
|
|
m_deadKeyState = 0;
|
|
m_KCHRHandle = GetResource('KCHR', m_keyboardLayoutID);
|
|
m_uchrHandle = GetResource('uchr', m_keyboardLayoutID);
|
|
m_KCHRResource = NULL;
|
|
m_uchrResource = NULL;
|
|
/* FIXME -- don't use uchr resource yet
|
|
if (m_uchrHandle != NULL) {
|
|
m_uchrResource = reinterpret_cast<UCKeyboardLayout*>(*m_uchrHandle);
|
|
}
|
|
else */if (m_KCHRHandle != NULL) {
|
|
m_KCHRResource = reinterpret_cast<CKCHRResource*>(*m_KCHRHandle);
|
|
}
|
|
}
|
|
|
|
void
|
|
COSXKeyState::fillSpecialKeys(CKeyIDMap& keyMap,
|
|
CVirtualKeyMap& virtualKeyMap) const
|
|
{
|
|
// FIXME -- would like to avoid hard coded tables
|
|
for (UInt32 i = 0; i < sizeof(s_controlKeys) /
|
|
sizeof(s_controlKeys[0]); ++i) {
|
|
const CKeyEntry& entry = s_controlKeys[i];
|
|
KeyID keyID = entry.m_keyID;
|
|
KeyButton keyButton = mapVirtualKeyToKeyButton(entry.m_virtualKey);
|
|
if (keyMap.count(keyID) == 0) {
|
|
keyMap[keyID].push_back(std::make_pair(keyButton, 0));
|
|
}
|
|
if (virtualKeyMap.count(entry.m_virtualKey) == 0) {
|
|
virtualKeyMap[entry.m_virtualKey] = entry.m_keyID;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const
|
|
{
|
|
assert(m_KCHRResource != NULL);
|
|
|
|
CKCHRResource* r = m_KCHRResource;
|
|
|
|
// build non-composed keys to virtual keys mapping
|
|
std::map<UInt8, std::pair<KeyButton, KeyModifierMask> > vkMap;
|
|
for (SInt32 i = 0; i < r->m_numTables; ++i) {
|
|
// determine the modifier keys for table i
|
|
KeyModifierMask mask =
|
|
maskForTable(static_cast<UInt8>(i), r->m_tableSelectionIndex);
|
|
|
|
// build the KeyID to virtual key map
|
|
for (SInt32 j = 0; j < 128; ++j) {
|
|
// get character
|
|
UInt8 c = r->m_characterTables[i][j];
|
|
|
|
// save character to virtual key mapping
|
|
if (keyMap.count(c) == 0) {
|
|
vkMap[c] = std::make_pair(mapVirtualKeyToKeyButton(j), mask);
|
|
}
|
|
|
|
// skip non-glyph character
|
|
if (c < 32 || c == 127) {
|
|
continue;
|
|
}
|
|
|
|
// map character to KeyID
|
|
KeyID keyID = charToKeyID(c);
|
|
|
|
// if we've seen this character already then do nothing
|
|
if (keyMap.count(keyID) != 0) {
|
|
continue;
|
|
}
|
|
|
|
// save entry for character
|
|
keyMap[keyID].push_back(std::make_pair(
|
|
mapVirtualKeyToKeyButton(j), mask));
|
|
}
|
|
}
|
|
|
|
// build composed keys to virtual keys mapping
|
|
CKCHRDeadKeys* dkp =
|
|
reinterpret_cast<CKCHRDeadKeys*>(r->m_characterTables[r->m_numTables]);
|
|
CKCHRDeadKeyRecord* dkr = dkp->m_records;
|
|
for (SInt32 i = 0; i < dkp->m_numRecords; ++i) {
|
|
// determine the modifier keys for table i
|
|
KeyModifierMask mask =
|
|
maskForTable(dkr->m_tableIndex, r->m_tableSelectionIndex);
|
|
|
|
// map each completion
|
|
for (SInt32 j = 0; j < dkr->m_numCompletions; ++j) {
|
|
// get character
|
|
UInt8 c = dkr->m_completion[j][1];
|
|
|
|
// skip non-glyph character
|
|
if (c < 32 || c == 127) {
|
|
continue;
|
|
}
|
|
|
|
// map character to KeyID
|
|
KeyID keyID = charToKeyID(c);
|
|
|
|
// if we've seen this character already then do nothing
|
|
if (keyMap.count(keyID) != 0) {
|
|
continue;
|
|
}
|
|
|
|
// map keyID, first to the dead key then to uncomposed
|
|
// character. we must find a virtual key that maps to
|
|
// to the uncomposed character.
|
|
if (vkMap.count(dkr->m_completion[j][0]) != 0) {
|
|
CKeySequence& sequence = keyMap[keyID];
|
|
sequence.push_back(std::make_pair(
|
|
mapVirtualKeyToKeyButton(dkr->m_virtualKey), mask));
|
|
sequence.push_back(vkMap[dkr->m_completion[j][0]]);
|
|
}
|
|
}
|
|
|
|
// next table. skip all the completions and the no match
|
|
// pair to get the next table.
|
|
dkr = reinterpret_cast<CKCHRDeadKeyRecord*>(
|
|
dkr->m_completion[dkr->m_numCompletions + 1]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
COSXKeyState::filluchrKeysMap(CKeyIDMap&) const
|
|
{
|
|
// FIXME -- implement this
|
|
return false;
|
|
}
|
|
|
|
KeyButton
|
|
COSXKeyState::mapVirtualKeyToKeyButton(UInt32 keyCode)
|
|
{
|
|
// 'A' maps to 0 so shift every id
|
|
return static_cast<KeyButton>(keyCode + KeyButtonOffset);
|
|
}
|
|
|
|
UInt32
|
|
COSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton)
|
|
{
|
|
return static_cast<UInt32>(keyButton - KeyButtonOffset);
|
|
}
|
|
|
|
KeyID
|
|
COSXKeyState::charToKeyID(UInt8 c)
|
|
{
|
|
if (c == 0) {
|
|
return kKeyNone;
|
|
}
|
|
else if (c >= 32 && c < 127) {
|
|
// ASCII
|
|
return static_cast<KeyID>(c);
|
|
}
|
|
else {
|
|
// create string with character
|
|
char str[2];
|
|
str[0] = static_cast<char>(c);
|
|
str[1] = 0;
|
|
|
|
// convert to unicode
|
|
CFStringRef cfString =
|
|
CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
|
|
str, GetScriptManagerVariable(smKeyScript),
|
|
kCFAllocatorNull);
|
|
|
|
// convert to precomposed
|
|
CFMutableStringRef mcfString =
|
|
CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString);
|
|
CFRelease(cfString);
|
|
CFStringNormalize(mcfString, kCFStringNormalizationFormC);
|
|
|
|
// check result
|
|
int unicodeLength = CFStringGetLength(mcfString);
|
|
if (unicodeLength == 0) {
|
|
return kKeyNone;
|
|
}
|
|
if (unicodeLength > 1) {
|
|
// FIXME -- more than one character, we should handle this
|
|
return kKeyNone;
|
|
}
|
|
|
|
// get unicode character
|
|
UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0);
|
|
CFRelease(mcfString);
|
|
|
|
// convert to KeyID
|
|
return static_cast<KeyID>(uc);
|
|
}
|
|
}
|
|
|
|
KeyID
|
|
COSXKeyState::unicharToKeyID(UniChar c)
|
|
{
|
|
return static_cast<KeyID>(c);
|
|
}
|
|
|
|
KeyModifierMask
|
|
COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors)
|
|
{
|
|
// this is a table of 0 to 255 sorted by the number of 1 bits then
|
|
// numerical order.
|
|
static const UInt8 s_indexTable[] = {
|
|
0, 1, 2, 4, 8, 16, 32, 64, 128, 3, 5, 6, 9, 10, 12, 17,
|
|
18, 20, 24, 33, 34, 36, 40, 48, 65, 66, 68, 72, 80, 96, 129, 130,
|
|
132, 136, 144, 160, 192, 7, 11, 13, 14, 19, 21, 22, 25, 26, 28, 35,
|
|
37, 38, 41, 42, 44, 49, 50, 52, 56, 67, 69, 70, 73, 74, 76, 81,
|
|
82, 84, 88, 97, 98, 100, 104, 112, 131, 133, 134, 137, 138, 140, 145, 146,
|
|
148, 152, 161, 162, 164, 168, 176, 193, 194, 196, 200, 208, 224, 15, 23, 27,
|
|
29, 30, 39, 43, 45, 46, 51, 53, 54, 57, 58, 60, 71, 75, 77, 78,
|
|
83, 85, 86, 89, 90, 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120,
|
|
135, 139, 141, 142, 147, 149, 150, 153, 154, 156, 163, 165, 166, 169, 170, 172,
|
|
177, 178, 180, 184, 195, 197, 198, 201, 202, 204, 209, 210, 212, 216, 225, 226,
|
|
228, 232, 240, 31, 47, 55, 59, 61, 62, 79, 87, 91, 93, 94, 103, 107,
|
|
109, 110, 115, 117, 118, 121, 122, 124, 143, 151, 155, 157, 158, 167, 171, 173,
|
|
174, 179, 181, 182, 185, 186, 188, 199, 203, 205, 206, 211, 213, 214, 217, 218,
|
|
220, 227, 229, 230, 233, 234, 236, 241, 242, 244, 248, 63, 95, 111, 119, 123,
|
|
125, 126, 159, 175, 183, 187, 189, 190, 207, 215, 219, 221, 222, 231, 235, 237,
|
|
238, 243, 245, 246, 249, 250, 252, 127, 191, 223, 239, 247, 251, 253, 254, 255
|
|
};
|
|
|
|
// find first entry in tableSelectors that maps to i. this is the
|
|
// one that uses the fewest modifier keys.
|
|
for (UInt32 j = 0; j < 256; ++j) {
|
|
if (tableSelectors[s_indexTable[j]] == i) {
|
|
// convert our mask to a traditional mac modifier mask
|
|
// (which just means shifting it left 8 bits).
|
|
UInt16 macMask = (static_cast<UInt16>(s_indexTable[j]) << 8);
|
|
|
|
// convert the mac modifier mask to our mask.
|
|
KeyModifierMask mask = 0;
|
|
if ((macMask & (shiftKey | rightShiftKey)) != 0) {
|
|
mask |= KeyModifierShift;
|
|
}
|
|
if ((macMask & (controlKey | rightControlKey)) != 0) {
|
|
mask |= KeyModifierControl;
|
|
}
|
|
if ((macMask & cmdKey) != 0) {
|
|
mask |= KeyModifierAlt;
|
|
}
|
|
if ((macMask & (optionKey | rightOptionKey)) != 0) {
|
|
mask |= KeyModifierSuper;
|
|
}
|
|
if ((macMask & alphaLock) != 0) {
|
|
mask |= KeyModifierCapsLock;
|
|
}
|
|
return mask;
|
|
}
|
|
}
|
|
|
|
// should never get here since we've tried every 8 bit number
|
|
return 0;
|
|
}
|