Added support for unicode keyboard layouts on OS X.
This commit is contained in:
parent
fedd2224e8
commit
ee787415c2
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "COSXKeyState.h"
|
#include "COSXKeyState.h"
|
||||||
#include "CLog.h"
|
#include "CLog.h"
|
||||||
|
#include "CArch.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
struct CKCHRDeadKeyRecord {
|
struct CKCHRDeadKeyRecord {
|
||||||
|
@ -112,6 +113,7 @@ static const CKeyEntry s_controlKeys[] = {
|
||||||
// modifier keys. i don't know how to make the mac properly
|
// modifier keys. i don't know how to make the mac properly
|
||||||
// interpret the right hand versions of modifier keys so they're
|
// interpret the right hand versions of modifier keys so they're
|
||||||
// currently mapped to the left hand version.
|
// currently mapped to the left hand version.
|
||||||
|
// { kKeyNumLock, 71 },
|
||||||
{ kKeyShift_L, 56 },
|
{ kKeyShift_L, 56 },
|
||||||
{ kKeyShift_R, 56 /*60*/ },
|
{ kKeyShift_R, 56 /*60*/ },
|
||||||
{ kKeyControl_L, 59 },
|
{ kKeyControl_L, 59 },
|
||||||
|
@ -122,15 +124,20 @@ static const CKeyEntry s_controlKeys[] = {
|
||||||
{ kKeySuper_R, 58 /*61*/ },
|
{ kKeySuper_R, 58 /*61*/ },
|
||||||
{ kKeyMeta_L, 58 },
|
{ kKeyMeta_L, 58 },
|
||||||
{ kKeyMeta_R, 58 /*61*/ },
|
{ kKeyMeta_R, 58 /*61*/ },
|
||||||
{ kKeyCapsLock, 57 },
|
{ kKeyCapsLock, 57 }
|
||||||
{ kKeyNumLock, 71 }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// special key that synthesizes a delay. see doFakeKeyEvent() and
|
||||||
|
// mapKey().
|
||||||
|
static const KeyButton kDelayKey = 510;
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// COSXKeyState
|
// COSXKeyState
|
||||||
//
|
//
|
||||||
|
|
||||||
COSXKeyState::COSXKeyState()
|
COSXKeyState::COSXKeyState() :
|
||||||
|
m_uchrFound(false)
|
||||||
{
|
{
|
||||||
setHalfDuplexMask(0);
|
setHalfDuplexMask(0);
|
||||||
SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript);
|
SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript);
|
||||||
|
@ -197,8 +204,12 @@ COSXKeyState::doUpdateKeys()
|
||||||
{
|
{
|
||||||
// save key mapping
|
// save key mapping
|
||||||
m_keyMap.clear();
|
m_keyMap.clear();
|
||||||
if (!filluchrKeysMap(m_keyMap, m_capsLockSet)) {
|
m_uchrFound = false;
|
||||||
fillKCHRKeysMap(m_keyMap, m_capsLockSet);
|
if (m_uchrResource != NULL) {
|
||||||
|
m_uchrFound = filluchrKeysMap(m_keyMap);
|
||||||
|
}
|
||||||
|
if (!m_uchrFound && m_KCHRResource != NULL) {
|
||||||
|
fillKCHRKeysMap(m_keyMap);
|
||||||
}
|
}
|
||||||
fillSpecialKeys(m_keyMap, m_virtualKeyMap);
|
fillSpecialKeys(m_keyMap, m_virtualKeyMap);
|
||||||
|
|
||||||
|
@ -235,15 +246,26 @@ void
|
||||||
COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool)
|
COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool)
|
||||||
{
|
{
|
||||||
LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press));
|
LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press));
|
||||||
|
|
||||||
|
// if it's the special delay key then just pause briefly
|
||||||
|
if (button == kDelayKey) {
|
||||||
|
ARCH->sleep(0.01);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// let system figure out character for us
|
// let system figure out character for us
|
||||||
CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey(button), press);
|
CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey(button), press);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyButton
|
KeyButton
|
||||||
COSXKeyState::mapKey(Keystrokes& keys, KeyID id,
|
COSXKeyState::mapKey(Keystrokes& keys, KeyID id,
|
||||||
KeyModifierMask /*desiredMask*/,
|
KeyModifierMask desiredMask,
|
||||||
bool isAutoRepeat) const
|
bool isAutoRepeat) const
|
||||||
{
|
{
|
||||||
|
// see if the keyboard layout has changed since we last checked it.
|
||||||
|
// reload the keyboard info if so.
|
||||||
|
const_cast<COSXKeyState*>(this)->checkKeyboardLayout();
|
||||||
|
|
||||||
// look up virtual key
|
// look up virtual key
|
||||||
CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id);
|
CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id);
|
||||||
if (keyIndex == m_keyMap.end()) {
|
if (keyIndex == m_keyMap.end()) {
|
||||||
|
@ -254,12 +276,6 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the virtual key is caps-lock sensitive then suppress shift
|
|
||||||
KeyModifierMask mask = ~0;
|
|
||||||
if (m_capsLockSet.count(id) != 0) {
|
|
||||||
mask &= ~KeyModifierShift;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME -- for both calls to addKeystrokes below we'd prefer to use
|
// FIXME -- for both calls to addKeystrokes below we'd prefer to use
|
||||||
// a required mask that generates the same character but matches
|
// a required mask that generates the same character but matches
|
||||||
// the desiredMask as closely as possible.
|
// the desiredMask as closely as possible.
|
||||||
|
@ -284,14 +300,37 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id,
|
||||||
keystroke.m_press = false;
|
keystroke.m_press = false;
|
||||||
keystroke.m_repeat = false;
|
keystroke.m_repeat = false;
|
||||||
keys.push_back(keystroke);
|
keys.push_back(keystroke);
|
||||||
|
|
||||||
|
// pause briefly before sending the next key because some
|
||||||
|
// apps (e.g. TextEdit) seem to ignore dead keys that occur
|
||||||
|
// very shortly before the next key. why they'd do that i
|
||||||
|
// don't know.
|
||||||
|
keystroke.m_key = kDelayKey;
|
||||||
|
keystroke.m_press = false;
|
||||||
|
keystroke.m_repeat = false;
|
||||||
|
keys.push_back(keystroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add final key
|
// add final key.
|
||||||
|
// if the desired mask includes Alt or Control then match the
|
||||||
|
// desired mask. this ensures that combinations like
|
||||||
|
// Command+Shift+S use the Command and Shift modifiers and
|
||||||
|
// those like Command+S do not use the shift modifier.
|
||||||
|
if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) != 0) {
|
||||||
|
return addKeystrokes(keys, sequence.back().m_button,
|
||||||
|
desiredMask,
|
||||||
|
KeyModifierShift | KeyModifierSuper |
|
||||||
|
KeyModifierAlt | KeyModifierControl |
|
||||||
|
KeyModifierCapsLock,
|
||||||
|
isAutoRepeat);
|
||||||
|
}
|
||||||
|
else {
|
||||||
return addKeystrokes(keys, sequence.back().m_button,
|
return addKeystrokes(keys, sequence.back().m_button,
|
||||||
sequence.back().m_requiredState,
|
sequence.back().m_requiredState,
|
||||||
sequence.back().m_requiredMask & mask,
|
sequence.back().m_requiredMask,
|
||||||
isAutoRepeat);
|
isAutoRepeat);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KeyButton
|
KeyButton
|
||||||
COSXKeyState::addKeystrokes(Keystrokes& keys, KeyButton keyButton,
|
COSXKeyState::addKeystrokes(Keystrokes& keys, KeyButton keyButton,
|
||||||
|
@ -387,8 +426,50 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for character keys
|
// check for character keys
|
||||||
if (m_uchrResource != NULL) {
|
if (m_uchrFound) {
|
||||||
// FIXME -- implement this
|
// 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));
|
||||||
|
|
||||||
|
// choose action
|
||||||
|
UInt16 action;
|
||||||
|
switch (eventKind) {
|
||||||
|
case kEventRawKeyDown:
|
||||||
|
action = kUCKeyActionDown;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kEventRawKeyRepeat:
|
||||||
|
action = kUCKeyActionAutoKey;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate key
|
||||||
|
UniCharCount count;
|
||||||
|
UniChar chars[2];
|
||||||
|
OSStatus status = UCKeyTranslate(m_uchrResource, keycode, action,
|
||||||
|
modifiers, m_keyboardType, 0, &m_deadKeyState,
|
||||||
|
sizeof(chars) / sizeof(chars[0]), &count, chars);
|
||||||
|
|
||||||
|
// get the characters
|
||||||
|
if (status == 0) {
|
||||||
|
if (count != 0 || m_deadKeyState == 0) {
|
||||||
|
m_deadKeyState = 0;
|
||||||
|
for (UniCharCount i = 0; i < count; ++i) {
|
||||||
|
ids.push_back(unicharToKeyID(chars[i]));
|
||||||
|
}
|
||||||
|
return mapVirtualKeyToKeyButton(vkCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (m_KCHRResource != NULL) {
|
else if (m_KCHRResource != NULL) {
|
||||||
// get the event modifiers and remove the command and control
|
// get the event modifiers and remove the command and control
|
||||||
|
@ -478,10 +559,13 @@ COSXKeyState::checkKeyboardLayout()
|
||||||
{
|
{
|
||||||
SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript);
|
SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript);
|
||||||
SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys);
|
SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys);
|
||||||
if (keyboardLayoutID != m_keyboardLayoutID) {
|
UInt32 keyboardType = LMGetKbdType();
|
||||||
|
if (keyboardLayoutID != m_keyboardLayoutID ||
|
||||||
|
keyboardType != m_keyboardType) {
|
||||||
// layout changed
|
// layout changed
|
||||||
|
m_keyboardType = keyboardType;
|
||||||
setKeyboardLayout(keyboardLayoutID);
|
setKeyboardLayout(keyboardLayoutID);
|
||||||
doUpdateKeys();
|
updateKeys();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,11 +578,10 @@ COSXKeyState::setKeyboardLayout(SInt16 keyboardLayoutID)
|
||||||
m_uchrHandle = GetResource('uchr', m_keyboardLayoutID);
|
m_uchrHandle = GetResource('uchr', m_keyboardLayoutID);
|
||||||
m_KCHRResource = NULL;
|
m_KCHRResource = NULL;
|
||||||
m_uchrResource = NULL;
|
m_uchrResource = NULL;
|
||||||
/* FIXME -- don't use uchr resource yet
|
|
||||||
if (m_uchrHandle != NULL) {
|
if (m_uchrHandle != NULL) {
|
||||||
m_uchrResource = reinterpret_cast<UCKeyboardLayout*>(*m_uchrHandle);
|
m_uchrResource = reinterpret_cast<UCKeyboardLayout*>(*m_uchrHandle);
|
||||||
}
|
}
|
||||||
else */if (m_KCHRHandle != NULL) {
|
if (m_KCHRHandle != NULL) {
|
||||||
m_KCHRResource = reinterpret_cast<CKCHRResource*>(*m_KCHRHandle);
|
m_KCHRResource = reinterpret_cast<CKCHRResource*>(*m_KCHRHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,29 +609,19 @@ COSXKeyState::fillSpecialKeys(CKeyIDMap& keyMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const
|
COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const
|
||||||
{
|
{
|
||||||
assert(m_KCHRResource != NULL);
|
assert(m_KCHRResource != NULL);
|
||||||
|
|
||||||
CKCHRResource* r = m_KCHRResource;
|
CKCHRResource* r = m_KCHRResource;
|
||||||
|
|
||||||
// note caps-lock sensitive keys
|
|
||||||
SInt32 uIndex = r->m_tableSelectionIndex[0];
|
|
||||||
SInt32 clIndex = r->m_tableSelectionIndex[alphaLock >> 8];
|
|
||||||
for (SInt32 j = 0; j < 128; ++j) {
|
|
||||||
UInt8 c = r->m_characterTables[clIndex][j];
|
|
||||||
if (r->m_characterTables[uIndex][j] != c) {
|
|
||||||
KeyID keyID = charToKeyID(c);
|
|
||||||
capsLockSet.insert(keyID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// build non-composed keys to virtual keys mapping
|
// build non-composed keys to virtual keys mapping
|
||||||
std::map<UInt8, CKeyEventInfo> vkMap;
|
std::map<UInt8, CKeyEventInfo> vkMap;
|
||||||
for (SInt32 i = 0; i < r->m_numTables; ++i) {
|
for (SInt32 i = 0; i < r->m_numTables; ++i) {
|
||||||
// determine the modifier keys for table i
|
// determine the modifier keys for table i
|
||||||
KeyModifierMask mask =
|
KeyModifierMask mask =
|
||||||
maskForTable(static_cast<UInt8>(i), r->m_tableSelectionIndex);
|
maskForTable(static_cast<UInt8>(i), r->m_tableSelectionIndex,
|
||||||
|
256, static_cast<UInt8>(i));
|
||||||
|
|
||||||
// build the KeyID to virtual key map
|
// build the KeyID to virtual key map
|
||||||
for (SInt32 j = 0; j < 128; ++j) {
|
for (SInt32 j = 0; j < 128; ++j) {
|
||||||
|
@ -561,7 +634,9 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const
|
||||||
// generate the character. this mostly works as-is, though.
|
// generate the character. this mostly works as-is, though.
|
||||||
CKeyEventInfo info;
|
CKeyEventInfo info;
|
||||||
info.m_button = mapVirtualKeyToKeyButton(j);
|
info.m_button = mapVirtualKeyToKeyButton(j);
|
||||||
info.m_requiredMask = mask;
|
info.m_requiredMask =
|
||||||
|
KeyModifierShift | KeyModifierSuper |
|
||||||
|
KeyModifierCapsLock;
|
||||||
info.m_requiredState = mask;
|
info.m_requiredState = mask;
|
||||||
|
|
||||||
// save character to virtual key mapping
|
// save character to virtual key mapping
|
||||||
|
@ -594,7 +669,8 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const
|
||||||
for (SInt32 i = 0; i < dkp->m_numRecords; ++i) {
|
for (SInt32 i = 0; i < dkp->m_numRecords; ++i) {
|
||||||
// determine the modifier keys for table i
|
// determine the modifier keys for table i
|
||||||
KeyModifierMask mask =
|
KeyModifierMask mask =
|
||||||
maskForTable(dkr->m_tableIndex, r->m_tableSelectionIndex);
|
maskForTable(dkr->m_tableIndex, r->m_tableSelectionIndex,
|
||||||
|
256, dkr->m_tableIndex);
|
||||||
|
|
||||||
// map each completion
|
// map each completion
|
||||||
for (SInt32 j = 0; j < dkr->m_numCompletions; ++j) {
|
for (SInt32 j = 0; j < dkr->m_numCompletions; ++j) {
|
||||||
|
@ -627,9 +703,11 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const
|
||||||
CKeyEventInfo info;
|
CKeyEventInfo info;
|
||||||
info.m_button = mapVirtualKeyToKeyButton(
|
info.m_button = mapVirtualKeyToKeyButton(
|
||||||
dkr->m_virtualKey);
|
dkr->m_virtualKey);
|
||||||
info.m_requiredMask = mask;
|
info.m_requiredMask =
|
||||||
|
KeyModifierShift | KeyModifierSuper |
|
||||||
|
KeyModifierAlt | KeyModifierControl |
|
||||||
|
KeyModifierCapsLock;
|
||||||
info.m_requiredState = mask;
|
info.m_requiredState = mask;
|
||||||
|
|
||||||
sequence.push_back(info);
|
sequence.push_back(info);
|
||||||
sequence.push_back(vkMap[dkr->m_completion[j][0]]);
|
sequence.push_back(vkMap[dkr->m_completion[j][0]]);
|
||||||
}
|
}
|
||||||
|
@ -645,12 +723,200 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
COSXKeyState::filluchrKeysMap(CKeyIDMap&, CKeySet&) const
|
COSXKeyState::filluchrKeysMap(CKeyIDMap& keyMap) const
|
||||||
{
|
{
|
||||||
// FIXME -- implement this
|
assert(m_uchrResource != NULL);
|
||||||
|
|
||||||
|
UCKeyboardLayout* r = m_uchrResource;
|
||||||
|
UInt8* base = reinterpret_cast<UInt8*>(r);
|
||||||
|
|
||||||
|
UCKeyLayoutFeatureInfo* fi = NULL;
|
||||||
|
if (r->keyLayoutFeatureInfoOffset != 0) {
|
||||||
|
fi = reinterpret_cast<UCKeyLayoutFeatureInfo*>(
|
||||||
|
base + r->keyLayoutFeatureInfoOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the keyboard info for the current keyboard type
|
||||||
|
UCKeyboardTypeHeader* th = NULL;
|
||||||
|
for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) {
|
||||||
|
if (m_keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst &&
|
||||||
|
m_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 false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get tables for keyboard type
|
||||||
|
UCKeyModifiersToTableNum* m =
|
||||||
|
reinterpret_cast<UCKeyModifiersToTableNum*>(
|
||||||
|
base + th->keyModifiersToTableNumOffset);
|
||||||
|
UCKeyToCharTableIndex* cti =
|
||||||
|
reinterpret_cast<UCKeyToCharTableIndex*>(
|
||||||
|
base + th->keyToCharTableIndexOffset);
|
||||||
|
UCKeySequenceDataIndex* sdi =
|
||||||
|
reinterpret_cast<UCKeySequenceDataIndex*>(
|
||||||
|
base + th->keySequenceDataIndexOffset);
|
||||||
|
|
||||||
|
// build non-composed keys to virtual keys mapping
|
||||||
|
CDeadKeyMap dkMap;
|
||||||
|
for (UInt32 i = 0; i < cti->keyToCharTableCount; ++i) {
|
||||||
|
// determine the modifier keys for table i
|
||||||
|
KeyModifierMask mask =
|
||||||
|
maskForTable(static_cast<UInt8>(i), m->tableNum,
|
||||||
|
m->modifiersCount, m->defaultTableNum);
|
||||||
|
|
||||||
|
// get the character table
|
||||||
|
UCKeyOutput* ct =
|
||||||
|
reinterpret_cast<UCKeyOutput*>(
|
||||||
|
base + cti->keyToCharTableOffsets[i]);
|
||||||
|
|
||||||
|
// build the KeyID to virtual key map
|
||||||
|
for (SInt32 j = 0; j < cti->keyToCharTableSize; ++j) {
|
||||||
|
// get character
|
||||||
|
UInt16 c = ct[j];
|
||||||
|
|
||||||
|
// ignore key sequence mappings
|
||||||
|
if ((c & 0xc000) == 0x8000) {
|
||||||
|
UInt16 index = (c & 0x3fff);
|
||||||
|
if (index < sdi->charSequenceCount &&
|
||||||
|
sdi->charSequenceOffsets[index] !=
|
||||||
|
sdi->charSequenceOffsets[index + 1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not a sequence mapping. use character as-is.
|
||||||
|
}
|
||||||
|
|
||||||
|
// just record dead key mappings
|
||||||
|
else if ((c & 0xc000) == 0x4000) {
|
||||||
|
UInt16 index = (c & 0x3fff);
|
||||||
|
if (dkMap.count(index) == 0) {
|
||||||
|
dkMap[index] = std::make_pair(j, mask);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip non-glyph character
|
||||||
|
if (c < 32 || c == 127 || c == 0xfffe || c == 0xffff) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// map character to KeyID
|
||||||
|
KeyID keyID = unicharToKeyID(c);
|
||||||
|
|
||||||
|
// if we've seen this character already then do nothing
|
||||||
|
if (keyMap.count(keyID) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save entry for character
|
||||||
|
// FIXME -- should set only those bits in m_requiredMask that
|
||||||
|
// correspond to modifiers that are truly necessary to
|
||||||
|
// generate the character.
|
||||||
|
CKeyEventInfo info;
|
||||||
|
info.m_button = mapVirtualKeyToKeyButton(j);
|
||||||
|
info.m_requiredMask =
|
||||||
|
KeyModifierShift | KeyModifierSuper |
|
||||||
|
KeyModifierCapsLock;
|
||||||
|
info.m_requiredState = mask;
|
||||||
|
keyMap[keyID].push_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build composed keys to virtual keys mapping
|
||||||
|
UCKeyStateRecordsIndex* sri = NULL;
|
||||||
|
if (th->keyStateRecordsIndexOffset != NULL) {
|
||||||
|
sri = reinterpret_cast<UCKeyStateRecordsIndex*>(
|
||||||
|
base + th->keyStateRecordsIndexOffset);
|
||||||
|
}
|
||||||
|
UCKeyStateTerminators* st = NULL;
|
||||||
|
if (th->keyStateTerminatorsOffset != NULL) {
|
||||||
|
st = reinterpret_cast<UCKeyStateTerminators*>(
|
||||||
|
base + th->keyStateTerminatorsOffset);
|
||||||
|
}
|
||||||
|
CKeySequence sequence;
|
||||||
|
mapDeadKeySequence(keyMap, sequence, 0, base, sri, st, dkMap);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
COSXKeyState::mapDeadKeySequence(CKeyIDMap& keyMap,
|
||||||
|
CKeySequence& sequence,
|
||||||
|
UInt16 state, const UInt8* base,
|
||||||
|
const UCKeyStateRecordsIndex* sri,
|
||||||
|
const UCKeyStateTerminators* st,
|
||||||
|
CDeadKeyMap& dkMap)
|
||||||
|
{
|
||||||
|
for (CDeadKeyMap::const_iterator i = dkMap.begin(); i != dkMap.end(); ++i) {
|
||||||
|
UInt16 index = i->first;
|
||||||
|
const UCKeyStateRecord* sr =
|
||||||
|
reinterpret_cast<const UCKeyStateRecord*>(
|
||||||
|
base + sri->keyStateRecordOffsets[index]);
|
||||||
|
const UCKeyStateEntryTerminal* kset =
|
||||||
|
reinterpret_cast<const UCKeyStateEntryTerminal*>(
|
||||||
|
sr->stateEntryData);
|
||||||
|
|
||||||
|
UInt16 c = 0;
|
||||||
|
UInt16 nextState = 0;
|
||||||
|
if (state == 0) {
|
||||||
|
c = sr->stateZeroCharData;
|
||||||
|
nextState = sr->stateZeroNextState;
|
||||||
|
}
|
||||||
|
else if (sr->stateEntryFormat == kUCKeyStateEntryTerminalFormat) {
|
||||||
|
for (UInt16 j = 0; j < sr->stateEntryCount; ++j) {
|
||||||
|
if (kset[j].curState == state) {
|
||||||
|
c = kset[j].charData;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// XXX -- default state terminator not supported yet
|
||||||
|
}
|
||||||
|
else if (sr->stateEntryFormat == kUCKeyStateEntryRangeFormat) {
|
||||||
|
// XXX -- not supported yet
|
||||||
|
}
|
||||||
|
|
||||||
|
// push character onto sequence. m_requiredMask should only
|
||||||
|
// have those modifiers that are truly necessary to generate
|
||||||
|
// the character.
|
||||||
|
CKeyEventInfo info;
|
||||||
|
info.m_button = mapVirtualKeyToKeyButton(
|
||||||
|
i->second.first);
|
||||||
|
info.m_requiredMask =
|
||||||
|
KeyModifierShift | KeyModifierSuper |
|
||||||
|
KeyModifierCapsLock;
|
||||||
|
info.m_requiredState = i->second.second;
|
||||||
|
sequence.push_back(info);
|
||||||
|
|
||||||
|
if (nextState != 0) {
|
||||||
|
// next in dead key sequence
|
||||||
|
mapDeadKeySequence(keyMap, sequence,
|
||||||
|
nextState, base, sri, st, dkMap);
|
||||||
|
}
|
||||||
|
else if (c >= 32 && c != 127 && c != 0xfffe && c != 0xffff) {
|
||||||
|
// terminate sequence
|
||||||
|
KeyID keyID = unicharToKeyID(c);
|
||||||
|
|
||||||
|
// if we've seen this character already then do nothing,
|
||||||
|
// otherwise save it in the key map.
|
||||||
|
if (keyMap.count(keyID) == 0) {
|
||||||
|
keyMap[keyID] = sequence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop character from sequence
|
||||||
|
sequence.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KeyButton
|
KeyButton
|
||||||
COSXKeyState::mapVirtualKeyToKeyButton(UInt32 keyCode)
|
COSXKeyState::mapVirtualKeyToKeyButton(UInt32 keyCode)
|
||||||
{
|
{
|
||||||
|
@ -718,7 +984,8 @@ COSXKeyState::unicharToKeyID(UniChar c)
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyModifierMask
|
KeyModifierMask
|
||||||
COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors)
|
COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors,
|
||||||
|
UInt32 numEntries, UInt8 defaultIndex)
|
||||||
{
|
{
|
||||||
// this is a table of 0 to 255 sorted by the number of 1 bits then
|
// this is a table of 0 to 255 sorted by the number of 1 bits then
|
||||||
// numerical order.
|
// numerical order.
|
||||||
|
@ -741,10 +1008,13 @@ COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors)
|
||||||
238, 243, 245, 246, 249, 250, 252, 127, 191, 223, 239, 247, 251, 253, 254, 255
|
238, 243, 245, 246, 249, 250, 252, 127, 191, 223, 239, 247, 251, 253, 254, 255
|
||||||
};
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
// find first entry in tableSelectors that maps to i. this is the
|
// find first entry in tableSelectors that maps to i. this is the
|
||||||
// one that uses the fewest modifier keys.
|
// one that uses the fewest modifier keys.
|
||||||
|
UInt8 maxIndex = static_cast<UInt8>(numEntries - 1);
|
||||||
for (UInt32 j = 0; j < 256; ++j) {
|
for (UInt32 j = 0; j < 256; ++j) {
|
||||||
if (tableSelectors[s_indexTable[j]] == i) {
|
if (s_indexTable[j] <= maxIndex &&
|
||||||
|
tableSelectors[s_indexTable[j]] == i) {
|
||||||
// convert our mask to a traditional mac modifier mask
|
// convert our mask to a traditional mac modifier mask
|
||||||
// (which just means shifting it left 8 bits).
|
// (which just means shifting it left 8 bits).
|
||||||
UInt16 macMask = (static_cast<UInt16>(s_indexTable[j]) << 8);
|
UInt16 macMask = (static_cast<UInt16>(s_indexTable[j]) << 8);
|
||||||
|
@ -770,6 +1040,13 @@ COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// should never get here since we've tried every 8 bit number
|
// no match. try defaultIndex.
|
||||||
|
if (i == defaultIndex) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i = defaultIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// should never get here.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ private:
|
||||||
typedef std::vector<CKeyEventInfo> CKeySequence;
|
typedef std::vector<CKeyEventInfo> CKeySequence;
|
||||||
typedef std::map<KeyID, CKeySequence> CKeyIDMap;
|
typedef std::map<KeyID, CKeySequence> CKeyIDMap;
|
||||||
typedef std::map<UInt32, KeyID> CVirtualKeyMap;
|
typedef std::map<UInt32, KeyID> CVirtualKeyMap;
|
||||||
typedef std::set<KeyID> CKeySet;
|
typedef std::map<UInt16, std::pair<SInt32, KeyModifierMask> > CDeadKeyMap;
|
||||||
|
|
||||||
KeyButton addKeystrokes(Keystrokes& keys,
|
KeyButton addKeystrokes(Keystrokes& keys,
|
||||||
KeyButton keyButton,
|
KeyButton keyButton,
|
||||||
|
@ -109,15 +109,14 @@ private:
|
||||||
// map maps each KeyID to the sequence of keys (with modifiers)
|
// map maps each KeyID to the sequence of keys (with modifiers)
|
||||||
// that would have to be synthesized to generate the KeyID character.
|
// that would have to be synthesized to generate the KeyID character.
|
||||||
// Returns false iff no KCHR resource was found.
|
// Returns false iff no KCHR resource was found.
|
||||||
bool fillKCHRKeysMap(CKeyIDMap& keyMap,
|
bool fillKCHRKeysMap(CKeyIDMap& keyMap) const;
|
||||||
CKeySet& capsLockSet) const;
|
|
||||||
|
|
||||||
// Convert the uchr resource to a KeyID to key sequence map. the
|
// Convert the uchr resource to a KeyID to key sequence map. the
|
||||||
// map maps each KeyID to the sequence of keys (with modifiers)
|
// map maps each KeyID to the sequence of keys (with modifiers)
|
||||||
// that would have to be synthesized to generate the KeyID character.
|
// that would have to be synthesized to generate the KeyID character.
|
||||||
// Returns false iff no uchr resource was found.
|
// Returns false iff no uchr resource was found or it couldn't be
|
||||||
bool filluchrKeysMap(CKeyIDMap& keyMap,
|
// mapped.
|
||||||
CKeySet& capsLockSet) const;
|
bool filluchrKeysMap(CKeyIDMap& keyMap) const;
|
||||||
|
|
||||||
// Maps an OS X virtual key id to a KeyButton. This simply remaps
|
// Maps an OS X virtual key id to a KeyButton. This simply remaps
|
||||||
// the ids so we don't use KeyButton 0.
|
// the ids so we don't use KeyButton 0.
|
||||||
|
@ -134,9 +133,19 @@ private:
|
||||||
static KeyID unicharToKeyID(UniChar);
|
static KeyID unicharToKeyID(UniChar);
|
||||||
|
|
||||||
// Choose the modifier mask with the fewest modifiers for character
|
// Choose the modifier mask with the fewest modifiers for character
|
||||||
// mapping table i.
|
// mapping table i. The tableSelectors table has numEntries. If
|
||||||
|
// no mapping is found for i, try mapping defaultIndex.
|
||||||
static KeyModifierMask
|
static KeyModifierMask
|
||||||
maskForTable(UInt8 i, UInt8* tableSelectors);
|
maskForTable(UInt8 i, UInt8* tableSelectors,
|
||||||
|
UInt32 numEntries, UInt8 defaultIndex);
|
||||||
|
|
||||||
|
// Save characters built from dead key sequences.
|
||||||
|
static void mapDeadKeySequence(CKeyIDMap& keyMap,
|
||||||
|
CKeySequence& sequence,
|
||||||
|
UInt16 state, const UInt8* base,
|
||||||
|
const UCKeyStateRecordsIndex* sri,
|
||||||
|
const UCKeyStateTerminators* st,
|
||||||
|
CDeadKeyMap& dkMap);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 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
|
||||||
|
@ -157,6 +166,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
SInt16 m_keyboardLayoutID;
|
SInt16 m_keyboardLayoutID;
|
||||||
|
UInt32 m_keyboardType;
|
||||||
mutable UInt32 m_deadKeyState;
|
mutable UInt32 m_deadKeyState;
|
||||||
Handle m_KCHRHandle;
|
Handle m_KCHRHandle;
|
||||||
Handle m_uchrHandle;
|
Handle m_uchrHandle;
|
||||||
|
@ -164,7 +174,7 @@ private:
|
||||||
UCKeyboardLayout* m_uchrResource;
|
UCKeyboardLayout* m_uchrResource;
|
||||||
CKeyIDMap m_keyMap;
|
CKeyIDMap m_keyMap;
|
||||||
CVirtualKeyMap m_virtualKeyMap;
|
CVirtualKeyMap m_virtualKeyMap;
|
||||||
CKeySet m_capsLockSet;
|
bool m_uchrFound;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue