summaryrefslogtreecommitdiffstats
path: root/src/lib/platform/OSXKeyState.cpp
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2018-04-25 18:07:30 -0400
committerLibravatarUnit 193 <unit193@ubuntu.com>2018-04-25 18:07:30 -0400
commit9b1b081cfdb1c0fb6457278775e0823f8bc10f62 (patch)
treece8840148d8445055ba9e4f12263b2208f234c16 /src/lib/platform/OSXKeyState.cpp
Import Upstream version 2.0.0+dfsgupstream/2.0.0+dfsg
Diffstat (limited to 'src/lib/platform/OSXKeyState.cpp')
-rw-r--r--src/lib/platform/OSXKeyState.cpp912
1 files changed, 912 insertions, 0 deletions
diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp
new file mode 100644
index 0000000..482d7c1
--- /dev/null
+++ b/src/lib/platform/OSXKeyState.cpp
@@ -0,0 +1,912 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 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/OSXKeyState.h"
+#include "platform/OSXUchrKeyResource.h"
+#include "platform/OSXMediaKeySupport.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+
+#include <Carbon/Carbon.h>
+#include <IOKit/hidsystem/IOHIDLib.h>
+
+// 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 UInt32 s_shiftVK = kVK_Shift;
+static const UInt32 s_controlVK = kVK_Control;
+static const UInt32 s_altVK = kVK_Option;
+static const UInt32 s_superVK = kVK_Command;
+static const UInt32 s_capsLockVK = kVK_CapsLock;
+static const UInt32 s_numLockVK = kVK_ANSI_KeypadClear; // 71
+
+static const UInt32 s_brightnessUp = 144;
+static const UInt32 s_brightnessDown = 145;
+static const UInt32 s_missionControlVK = 160;
+static const UInt32 s_launchpadVK = 131;
+
+static const UInt32 s_osxNumLock = 1 << 16;
+
+struct KeyEntry {
+public:
+ KeyID m_keyID;
+ UInt32 m_virtualKey;
+};
+static const KeyEntry s_controlKeys[] = {
+ // cursor keys. if we don't do this we'll may still get these from
+ // the keyboard resource but they may not correspond to the arrow
+ // keys.
+ { kKeyLeft, kVK_LeftArrow },
+ { kKeyRight, kVK_RightArrow },
+ { kKeyUp, kVK_UpArrow },
+ { kKeyDown, kVK_DownArrow },
+ { kKeyHome, kVK_Home },
+ { kKeyEnd, kVK_End },
+ { kKeyPageUp, kVK_PageUp },
+ { kKeyPageDown, kVK_PageDown },
+ { kKeyInsert, kVK_Help }, // Mac Keyboards have 'Help' on 'Insert'
+
+ // function keys
+ { kKeyF1, kVK_F1 },
+ { kKeyF2, kVK_F2 },
+ { kKeyF3, kVK_F3 },
+ { kKeyF4, kVK_F4 },
+ { kKeyF5, kVK_F5 },
+ { kKeyF6, kVK_F6 },
+ { kKeyF7, kVK_F7 },
+ { kKeyF8, kVK_F8 },
+ { kKeyF9, kVK_F9 },
+ { kKeyF10, kVK_F10 },
+ { kKeyF11, kVK_F11 },
+ { kKeyF12, kVK_F12 },
+ { kKeyF13, kVK_F13 },
+ { kKeyF14, kVK_F14 },
+ { kKeyF15, kVK_F15 },
+ { kKeyF16, kVK_F16 },
+
+ { kKeyKP_0, kVK_ANSI_Keypad0 },
+ { kKeyKP_1, kVK_ANSI_Keypad1 },
+ { kKeyKP_2, kVK_ANSI_Keypad2 },
+ { kKeyKP_3, kVK_ANSI_Keypad3 },
+ { kKeyKP_4, kVK_ANSI_Keypad4 },
+ { kKeyKP_5, kVK_ANSI_Keypad5 },
+ { kKeyKP_6, kVK_ANSI_Keypad6 },
+ { kKeyKP_7, kVK_ANSI_Keypad7 },
+ { kKeyKP_8, kVK_ANSI_Keypad8 },
+ { kKeyKP_9, kVK_ANSI_Keypad9 },
+ { kKeyKP_Decimal, kVK_ANSI_KeypadDecimal },
+ { kKeyKP_Equal, kVK_ANSI_KeypadEquals },
+ { kKeyKP_Multiply, kVK_ANSI_KeypadMultiply },
+ { kKeyKP_Add, kVK_ANSI_KeypadPlus },
+ { kKeyKP_Divide, kVK_ANSI_KeypadDivide },
+ { kKeyKP_Subtract, kVK_ANSI_KeypadMinus },
+ { kKeyKP_Enter, kVK_ANSI_KeypadEnter },
+
+ // virtual key 110 is fn+enter and i have no idea what that's supposed
+ // to map to. also the enter key with numlock on is a modifier but i
+ // don't know which.
+
+ // modifier keys. OS X doesn't seem to support right handed versions
+ // of modifier keys so we map them to the left handed versions.
+ { kKeyShift_L, s_shiftVK },
+ { kKeyShift_R, s_shiftVK }, // 60
+ { kKeyControl_L, s_controlVK },
+ { kKeyControl_R, s_controlVK }, // 62
+ { kKeyAlt_L, s_altVK },
+ { kKeyAlt_R, s_altVK },
+ { kKeySuper_L, s_superVK },
+ { kKeySuper_R, s_superVK }, // 61
+ { kKeyMeta_L, s_superVK },
+ { kKeyMeta_R, s_superVK }, // 61
+
+ // toggle modifiers
+ { kKeyNumLock, s_numLockVK },
+ { kKeyCapsLock, s_capsLockVK },
+
+ { kKeyMissionControl, s_missionControlVK },
+ { kKeyLaunchpad, s_launchpadVK },
+ { kKeyBrightnessUp, s_brightnessUp },
+ { kKeyBrightnessDown, s_brightnessDown }
+};
+
+
+//
+// OSXKeyState
+//
+
+OSXKeyState::OSXKeyState(IEventQueue* events) :
+ KeyState(events)
+{
+ init();
+}
+
+OSXKeyState::OSXKeyState(IEventQueue* events, barrier::KeyMap& keyMap) :
+ KeyState(events, keyMap)
+{
+ init();
+}
+
+OSXKeyState::~OSXKeyState()
+{
+}
+
+void
+OSXKeyState::init()
+{
+ m_deadKeyState = 0;
+ m_shiftPressed = false;
+ m_controlPressed = false;
+ m_altPressed = false;
+ m_superPressed = false;
+ m_capsPressed = false;
+
+ // build virtual key map
+ for (size_t i = 0; i < sizeof(s_controlKeys) / sizeof(s_controlKeys[0]);
+ ++i) {
+
+ m_virtualKeyMap[s_controlKeys[i].m_virtualKey] =
+ s_controlKeys[i].m_keyID;
+ }
+}
+
+KeyModifierMask
+OSXKeyState::mapModifiersFromOSX(UInt32 mask) const
+{
+ KeyModifierMask outMask = 0;
+ if ((mask & kCGEventFlagMaskShift) != 0) {
+ outMask |= KeyModifierShift;
+ }
+ if ((mask & kCGEventFlagMaskControl) != 0) {
+ outMask |= KeyModifierControl;
+ }
+ if ((mask & kCGEventFlagMaskAlternate) != 0) {
+ outMask |= KeyModifierAlt;
+ }
+ if ((mask & kCGEventFlagMaskCommand) != 0) {
+ outMask |= KeyModifierSuper;
+ }
+ if ((mask & kCGEventFlagMaskAlphaShift) != 0) {
+ outMask |= KeyModifierCapsLock;
+ }
+ if ((mask & kCGEventFlagMaskNumericPad) != 0) {
+ outMask |= KeyModifierNumLock;
+ }
+
+ LOG((CLOG_DEBUG1 "mask=%04x outMask=%04x", mask, outMask));
+ return outMask;
+}
+
+KeyModifierMask
+OSXKeyState::mapModifiersToCarbon(UInt32 mask) const
+{
+ KeyModifierMask outMask = 0;
+ if ((mask & kCGEventFlagMaskShift) != 0) {
+ outMask |= shiftKey;
+ }
+ if ((mask & kCGEventFlagMaskControl) != 0) {
+ outMask |= controlKey;
+ }
+ if ((mask & kCGEventFlagMaskCommand) != 0) {
+ outMask |= cmdKey;
+ }
+ if ((mask & kCGEventFlagMaskAlternate) != 0) {
+ outMask |= optionKey;
+ }
+ if ((mask & kCGEventFlagMaskAlphaShift) != 0) {
+ outMask |= alphaLock;
+ }
+ if ((mask & kCGEventFlagMaskNumericPad) != 0) {
+ outMask |= s_osxNumLock;
+ }
+
+ return outMask;
+}
+
+KeyButton
+OSXKeyState::mapKeyFromEvent(KeyIDs& ids,
+ KeyModifierMask* maskOut, CGEventRef event) const
+{
+ ids.clear();
+
+ // map modifier key
+ if (maskOut != NULL) {
+ KeyModifierMask activeMask = getActiveModifiers();
+ activeMask &= ~KeyModifierAltGr;
+ *maskOut = activeMask;
+ }
+
+ // get virtual key
+ UInt32 vkCode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
+
+ // handle up events
+ UInt32 eventKind = CGEventGetType(event);
+ if (eventKind == kCGEventKeyUp) {
+ // 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
+ VirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode);
+ if (i != m_virtualKeyMap.end()) {
+ m_deadKeyState = 0;
+ ids.push_back(i->second);
+ return mapVirtualKeyToKeyButton(vkCode);
+ }
+
+ // get keyboard info
+ TISInputSourceRef currentKeyboardLayout = TISCopyCurrentKeyboardLayoutInputSource();
+
+ if (currentKeyboardLayout == NULL) {
+ return kKeyNone;
+ }
+
+ // get the event modifiers and remove the command and control
+ // keys. note if we used them though.
+ // UCKeyTranslate expects old-style Carbon modifiers, so convert.
+ UInt32 modifiers;
+ modifiers = mapModifiersToCarbon(CGEventGetFlags(event));
+ static const UInt32 s_commandModifiers =
+ cmdKey | controlKey | rightControlKey;
+ bool isCommand = ((modifiers & s_commandModifiers) != 0);
+ modifiers &= ~s_commandModifiers;
+
+ // if we've used a command key then we want the glyph produced without
+ // the option key (i.e. the base glyph).
+ //if (isCommand) {
+ modifiers &= ~optionKey;
+ //}
+
+ // choose action
+ UInt16 action;
+ if (eventKind==kCGEventKeyDown) {
+ action = kUCKeyActionDown;
+ }
+ else if (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat)==1) {
+ action = kUCKeyActionAutoKey;
+ }
+ else {
+ return 0;
+ }
+
+ // translate via uchr resource
+ CFDataRef ref = (CFDataRef) TISGetInputSourceProperty(currentKeyboardLayout,
+ kTISPropertyUnicodeKeyLayoutData);
+ const UCKeyboardLayout* layout = (const UCKeyboardLayout*) CFDataGetBytePtr(ref);
+ const bool layoutValid = (layout != NULL);
+
+ if (layoutValid) {
+ // translate key
+ UniCharCount count;
+ UniChar chars[2];
+ LOG((CLOG_DEBUG2 "modifiers: %08x", modifiers & 0xffu));
+ OSStatus status = UCKeyTranslate(layout,
+ vkCode & 0xffu, action,
+ (modifiers >> 8) & 0xffu,
+ LMGetKbdType(), 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(IOSXKeyResource::unicharToKeyID(chars[i]));
+ }
+ adjustAltGrModifier(ids, maskOut, isCommand);
+ return mapVirtualKeyToKeyButton(vkCode);
+ }
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+bool
+OSXKeyState::fakeCtrlAltDel()
+{
+ // pass keys through unchanged
+ return false;
+}
+
+bool
+OSXKeyState::fakeMediaKey(KeyID id)
+{
+ return fakeNativeMediaKey(id);;
+}
+
+CGEventFlags
+OSXKeyState::getModifierStateAsOSXFlags()
+{
+ CGEventFlags modifiers = 0;
+
+ if (m_shiftPressed) {
+ modifiers |= kCGEventFlagMaskShift;
+ }
+
+ if (m_controlPressed) {
+ modifiers |= kCGEventFlagMaskControl;
+ }
+
+ if (m_altPressed) {
+ modifiers |= kCGEventFlagMaskAlternate;
+ }
+
+ if (m_superPressed) {
+ modifiers |= kCGEventFlagMaskCommand;
+ }
+
+ if (m_capsPressed) {
+ modifiers |= kCGEventFlagMaskAlphaShift;
+ }
+
+ return modifiers;
+}
+
+KeyModifierMask
+OSXKeyState::pollActiveModifiers() const
+{
+ // falsely assumed that the mask returned by GetCurrentKeyModifiers()
+ // was the same as a CGEventFlags (which is what mapModifiersFromOSX
+ // expects). patch by Marc
+ UInt32 mask = GetCurrentKeyModifiers();
+ KeyModifierMask outMask = 0;
+
+ if ((mask & shiftKey) != 0) {
+ outMask |= KeyModifierShift;
+ }
+ if ((mask & controlKey) != 0) {
+ outMask |= KeyModifierControl;
+ }
+ if ((mask & optionKey) != 0) {
+ outMask |= KeyModifierAlt;
+ }
+ if ((mask & cmdKey) != 0) {
+ outMask |= KeyModifierSuper;
+ }
+ if ((mask & alphaLock) != 0) {
+ outMask |= KeyModifierCapsLock;
+ }
+ if ((mask & s_osxNumLock) != 0) {
+ outMask |= KeyModifierNumLock;
+ }
+
+ LOG((CLOG_DEBUG1 "mask=%04x outMask=%04x", mask, outMask));
+ return outMask;
+}
+
+SInt32
+OSXKeyState::pollActiveGroup() const
+{
+ TISInputSourceRef keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource();
+ CFDataRef id = (CFDataRef)TISGetInputSourceProperty(
+ keyboardLayout, kTISPropertyInputSourceID);
+
+ GroupMap::const_iterator i = m_groupMap.find(id);
+ if (i != m_groupMap.end()) {
+ return i->second;
+ }
+
+ LOG((CLOG_DEBUG "can't get the active group, use the first group instead"));
+
+ return 0;
+}
+
+void
+OSXKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
+{
+ ::KeyMap km;
+ GetKeys(km);
+ const UInt8* m = reinterpret_cast<const UInt8*>(km);
+ for (UInt32 i = 0; i < 16; ++i) {
+ for (UInt32 j = 0; j < 8; ++j) {
+ if ((m[i] & (1u << j)) != 0) {
+ pressedKeys.insert(mapVirtualKeyToKeyButton(8 * i + j));
+ }
+ }
+ }
+}
+
+void
+OSXKeyState::getKeyMap(barrier::KeyMap& keyMap)
+{
+ // update keyboard groups
+ if (getGroups(m_groups)) {
+ m_groupMap.clear();
+ SInt32 numGroups = (SInt32)m_groups.size();
+ for (SInt32 g = 0; g < numGroups; ++g) {
+ CFDataRef id = (CFDataRef)TISGetInputSourceProperty(
+ m_groups[g], kTISPropertyInputSourceID);
+ m_groupMap[id] = g;
+ }
+ }
+
+ UInt32 keyboardType = LMGetKbdType();
+ for (SInt32 g = 0, n = (SInt32)m_groups.size(); g < n; ++g) {
+ // add special keys
+ getKeyMapForSpecialKeys(keyMap, g);
+
+ const void* resource;
+ bool layoutValid = false;
+
+ // add regular keys
+ // try uchr resource first
+ CFDataRef resourceRef = (CFDataRef)TISGetInputSourceProperty(
+ m_groups[g], kTISPropertyUnicodeKeyLayoutData);
+
+ layoutValid = resourceRef != NULL;
+ if (layoutValid)
+ resource = CFDataGetBytePtr(resourceRef);
+
+ if (layoutValid) {
+ OSXUchrKeyResource uchr(resource, keyboardType);
+ if (uchr.isValid()) {
+ LOG((CLOG_DEBUG1 "using uchr resource for group %d", g));
+ getKeyMap(keyMap, g, uchr);
+ continue;
+ }
+ }
+
+ LOG((CLOG_DEBUG1 "no keyboard resource for group %d", g));
+ }
+}
+
+static io_connect_t getEventDriver(void)
+{
+ static mach_port_t sEventDrvrRef = 0;
+ mach_port_t masterPort, service, iter;
+ kern_return_t kr;
+
+ if (!sEventDrvrRef) {
+ // Get master device port
+ kr = IOMasterPort(bootstrap_port, &masterPort);
+ assert(KERN_SUCCESS == kr);
+
+ kr = IOServiceGetMatchingServices(masterPort,
+ IOServiceMatching(kIOHIDSystemClass), &iter);
+ assert(KERN_SUCCESS == kr);
+
+ service = IOIteratorNext(iter);
+ assert(service);
+
+ kr = IOServiceOpen(service, mach_task_self(),
+ kIOHIDParamConnectType, &sEventDrvrRef);
+ assert(KERN_SUCCESS == kr);
+
+ IOObjectRelease(service);
+ IOObjectRelease(iter);
+ }
+
+ return sEventDrvrRef;
+}
+
+void
+OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode,
+ const bool postDown)
+{
+ static UInt32 modifiers = 0;
+
+ NXEventData event;
+ IOGPoint loc = { 0, 0 };
+ UInt32 modifiersDelta = 0;
+
+ bzero(&event, sizeof(NXEventData));
+
+ switch (virtualKeyCode)
+ {
+ case s_shiftVK:
+ case s_superVK:
+ case s_altVK:
+ case s_controlVK:
+ case s_capsLockVK:
+ switch (virtualKeyCode)
+ {
+ case s_shiftVK:
+ modifiersDelta = NX_SHIFTMASK;
+ m_shiftPressed = postDown;
+ break;
+ case s_superVK:
+ modifiersDelta = NX_COMMANDMASK;
+ m_superPressed = postDown;
+ break;
+ case s_altVK:
+ modifiersDelta = NX_ALTERNATEMASK;
+ m_altPressed = postDown;
+ break;
+ case s_controlVK:
+ modifiersDelta = NX_CONTROLMASK;
+ m_controlPressed = postDown;
+ break;
+ case s_capsLockVK:
+ modifiersDelta = NX_ALPHASHIFTMASK;
+ m_capsPressed = postDown;
+ break;
+ }
+
+ // update the modifier bit
+ if (postDown) {
+ modifiers |= modifiersDelta;
+ }
+ else {
+ modifiers &= ~modifiersDelta;
+ }
+
+ kern_return_t kr;
+ kr = IOHIDPostEvent(getEventDriver(), NX_FLAGSCHANGED, loc,
+ &event, kNXEventDataVersion, modifiers, true);
+ assert(KERN_SUCCESS == kr);
+ break;
+
+ default:
+ event.key.repeat = false;
+ event.key.keyCode = virtualKeyCode;
+ event.key.origCharSet = event.key.charSet = NX_ASCIISET;
+ event.key.origCharCode = event.key.charCode = 0;
+ kr = IOHIDPostEvent(getEventDriver(),
+ postDown ? NX_KEYDOWN : NX_KEYUP,
+ loc, &event, kNXEventDataVersion, 0, false);
+ assert(KERN_SUCCESS == kr);
+ break;
+ }
+}
+
+void
+OSXKeyState::fakeKey(const Keystroke& keystroke)
+{
+ switch (keystroke.m_type) {
+ case Keystroke::kButton: {
+
+ KeyButton button = keystroke.m_data.m_button.m_button;
+ bool keyDown = keystroke.m_data.m_button.m_press;
+ CGKeyCode virtualKey = mapKeyButtonToVirtualKey(button);
+
+ LOG((CLOG_DEBUG1
+ " button=0x%04x virtualKey=0x%04x keyDown=%s",
+ button, virtualKey, keyDown ? "down" : "up"));
+
+ postHIDVirtualKey(virtualKey, keyDown);
+
+ break;
+ }
+
+ case Keystroke::kGroup: {
+ SInt32 group = keystroke.m_data.m_group.m_group;
+ if (keystroke.m_data.m_group.m_absolute) {
+ LOG((CLOG_DEBUG1 " group %d", group));
+ setGroup(group);
+ }
+ else {
+ LOG((CLOG_DEBUG1 " group %+d", group));
+ setGroup(getEffectiveGroup(pollActiveGroup(), group));
+ }
+ break;
+ }
+ }
+}
+
+void
+OSXKeyState::getKeyMapForSpecialKeys(barrier::KeyMap& keyMap, SInt32 group) const
+{
+ // special keys are insensitive to modifers and none are dead keys
+ barrier::KeyMap::KeyItem item;
+ for (size_t i = 0; i < sizeof(s_controlKeys) /
+ sizeof(s_controlKeys[0]); ++i) {
+ const KeyEntry& entry = s_controlKeys[i];
+ item.m_id = entry.m_keyID;
+ item.m_group = group;
+ item.m_button = mapVirtualKeyToKeyButton(entry.m_virtualKey);
+ item.m_required = 0;
+ item.m_sensitive = 0;
+ item.m_dead = false;
+ item.m_client = 0;
+ barrier::KeyMap::initModifierKey(item);
+ keyMap.addKeyEntry(item);
+
+ if (item.m_lock) {
+ // all locking keys are half duplex on OS X
+ keyMap.addHalfDuplexButton(item.m_button);
+ }
+ }
+
+ // note: we don't special case the number pad keys. querying the
+ // mac keyboard returns the non-keypad version of those keys but
+ // a KeyState always provides a mapping from keypad keys to
+ // non-keypad keys so we'll be able to generate the characters
+ // anyway.
+}
+
+bool
+OSXKeyState::getKeyMap(barrier::KeyMap& keyMap,
+ SInt32 group, const IOSXKeyResource& r) const
+{
+ if (!r.isValid()) {
+ return false;
+ }
+
+ // space for all possible modifier combinations
+ std::vector<bool> modifiers(r.getNumModifierCombinations());
+
+ // make space for the keys that any single button can synthesize
+ std::vector<std::pair<KeyID, bool> > buttonKeys(r.getNumTables());
+
+ // iterate over each button
+ barrier::KeyMap::KeyItem item;
+ for (UInt32 i = 0; i < r.getNumButtons(); ++i) {
+ item.m_button = mapVirtualKeyToKeyButton(i);
+
+ // the KeyIDs we've already handled
+ std::set<KeyID> keys;
+
+ // convert the entry in each table for this button to a KeyID
+ for (UInt32 j = 0; j < r.getNumTables(); ++j) {
+ buttonKeys[j].first = r.getKey(j, i);
+ buttonKeys[j].second = barrier::KeyMap::isDeadKey(buttonKeys[j].first);
+ }
+
+ // iterate over each character table
+ for (UInt32 j = 0; j < r.getNumTables(); ++j) {
+ // get the KeyID for the button/table
+ KeyID id = buttonKeys[j].first;
+ if (id == kKeyNone) {
+ continue;
+ }
+
+ // if we've already handled the KeyID in the table then
+ // move on to the next table
+ if (keys.count(id) > 0) {
+ continue;
+ }
+ keys.insert(id);
+
+ // prepare item. the client state is 1 for dead keys.
+ item.m_id = id;
+ item.m_group = group;
+ item.m_dead = buttonKeys[j].second;
+ item.m_client = buttonKeys[j].second ? 1 : 0;
+ barrier::KeyMap::initModifierKey(item);
+ if (item.m_lock) {
+ // all locking keys are half duplex on OS X
+ keyMap.addHalfDuplexButton(i);
+ }
+
+ // collect the tables that map to the same KeyID. we know it
+ // can't be any earlier tables because of the check above.
+ std::set<UInt8> tables;
+ tables.insert(static_cast<UInt8>(j));
+ for (UInt32 k = j + 1; k < r.getNumTables(); ++k) {
+ if (buttonKeys[k].first == id) {
+ tables.insert(static_cast<UInt8>(k));
+ }
+ }
+
+ // collect the modifier combinations that map to any of the
+ // tables we just collected
+ for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) {
+ modifiers[k] = (tables.count(r.getTableForModifier(k)) > 0);
+ }
+
+ // figure out which modifiers the key is sensitive to. the
+ // key is insensitive to a modifier if for every modifier mask
+ // with the modifier bit unset in the modifiers we also find
+ // the same mask with the bit set.
+ //
+ // we ignore a few modifiers that we know aren't important
+ // for generating characters. in fact, we want to ignore any
+ // characters generated by the control key. we don't map
+ // those and instead expect the control modifier plus a key.
+ UInt32 sensitive = 0;
+ for (UInt32 k = 0; (1u << k) <
+ r.getNumModifierCombinations(); ++k) {
+ UInt32 bit = (1u << k);
+ if ((bit << 8) == cmdKey ||
+ (bit << 8) == controlKey ||
+ (bit << 8) == rightControlKey) {
+ continue;
+ }
+ for (UInt32 m = 0; m < r.getNumModifierCombinations(); ++m) {
+ if (modifiers[m] != modifiers[m ^ bit]) {
+ sensitive |= bit;
+ break;
+ }
+ }
+ }
+
+ // find each required modifier mask. the key can be synthesized
+ // using any of the masks.
+ std::set<UInt32> required;
+ for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) {
+ if ((k & sensitive) == k && modifiers[k & sensitive]) {
+ required.insert(k);
+ }
+ }
+
+ // now add a key entry for each key/required modifier pair.
+ item.m_sensitive = mapModifiersFromOSX(sensitive << 16);
+ for (std::set<UInt32>::iterator k = required.begin();
+ k != required.end(); ++k) {
+ item.m_required = mapModifiersFromOSX(*k << 16);
+ keyMap.addKeyEntry(item);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+OSXKeyState::mapBarrierHotKeyToMac(KeyID key, KeyModifierMask mask,
+ UInt32 &macVirtualKey, UInt32 &macModifierMask) const
+{
+ // look up button for key
+ KeyButton button = getButton(key, pollActiveGroup());
+ if (button == 0 && key != kKeyNone) {
+ return false;
+ }
+ macVirtualKey = mapKeyButtonToVirtualKey(button);
+
+ // calculate modifier mask
+ macModifierMask = 0;
+ if ((mask & KeyModifierShift) != 0) {
+ macModifierMask |= shiftKey;
+ }
+ if ((mask & KeyModifierControl) != 0) {
+ macModifierMask |= controlKey;
+ }
+ if ((mask & KeyModifierAlt) != 0) {
+ macModifierMask |= cmdKey;
+ }
+ if ((mask & KeyModifierSuper) != 0) {
+ macModifierMask |= optionKey;
+ }
+ if ((mask & KeyModifierCapsLock) != 0) {
+ macModifierMask |= alphaLock;
+ }
+ if ((mask & KeyModifierNumLock) != 0) {
+ macModifierMask |= s_osxNumLock;
+ }
+
+ return true;
+}
+
+void
+OSXKeyState::handleModifierKeys(void* target,
+ KeyModifierMask oldMask, KeyModifierMask newMask)
+{
+ // compute changed modifiers
+ KeyModifierMask changed = (oldMask ^ newMask);
+
+ // synthesize changed modifier keys
+ if ((changed & KeyModifierShift) != 0) {
+ handleModifierKey(target, s_shiftVK, kKeyShift_L,
+ (newMask & KeyModifierShift) != 0, newMask);
+ }
+ if ((changed & KeyModifierControl) != 0) {
+ handleModifierKey(target, s_controlVK, kKeyControl_L,
+ (newMask & KeyModifierControl) != 0, newMask);
+ }
+ if ((changed & KeyModifierAlt) != 0) {
+ handleModifierKey(target, s_altVK, kKeyAlt_L,
+ (newMask & KeyModifierAlt) != 0, newMask);
+ }
+ if ((changed & KeyModifierSuper) != 0) {
+ handleModifierKey(target, s_superVK, kKeySuper_L,
+ (newMask & KeyModifierSuper) != 0, newMask);
+ }
+ if ((changed & KeyModifierCapsLock) != 0) {
+ handleModifierKey(target, s_capsLockVK, kKeyCapsLock,
+ (newMask & KeyModifierCapsLock) != 0, newMask);
+ }
+ if ((changed & KeyModifierNumLock) != 0) {
+ handleModifierKey(target, s_numLockVK, kKeyNumLock,
+ (newMask & KeyModifierNumLock) != 0, newMask);
+ }
+}
+
+void
+OSXKeyState::handleModifierKey(void* target,
+ UInt32 virtualKey, KeyID id,
+ bool down, KeyModifierMask newMask)
+{
+ KeyButton button = mapVirtualKeyToKeyButton(virtualKey);
+ onKey(button, down, newMask);
+ sendKeyEvent(target, down, false, id, newMask, 0, button);
+}
+
+bool
+OSXKeyState::getGroups(GroupList& groups) const
+{
+ CFIndex n;
+ bool gotLayouts = false;
+
+ // get number of layouts
+ CFStringRef keys[] = { kTISPropertyInputSourceCategory };
+ CFStringRef values[] = { kTISCategoryKeyboardInputSource };
+ CFDictionaryRef dict = CFDictionaryCreate(NULL, (const void **)keys, (const void **)values, 1, NULL, NULL);
+ CFArrayRef kbds = TISCreateInputSourceList(dict, false);
+ n = CFArrayGetCount(kbds);
+ gotLayouts = (n != 0);
+
+ if (!gotLayouts) {
+ LOG((CLOG_DEBUG1 "can't get keyboard layouts"));
+ return false;
+ }
+
+ // get each layout
+ groups.clear();
+ for (CFIndex i = 0; i < n; ++i) {
+ bool addToGroups = true;
+ TISInputSourceRef keyboardLayout =
+ (TISInputSourceRef)CFArrayGetValueAtIndex(kbds, i);
+
+ if (addToGroups)
+ groups.push_back(keyboardLayout);
+ }
+ return true;
+}
+
+void
+OSXKeyState::setGroup(SInt32 group)
+{
+ TISSetInputMethodKeyboardLayoutOverride(m_groups[group]);
+}
+
+void
+OSXKeyState::checkKeyboardLayout()
+{
+ // XXX -- should call this when notified that groups have changed.
+ // if no notification for that then we should poll.
+ GroupList groups;
+ if (getGroups(groups) && groups != m_groups) {
+ updateKeyMap();
+ updateKeyState();
+ }
+}
+
+void
+OSXKeyState::adjustAltGrModifier(const KeyIDs& ids,
+ KeyModifierMask* mask, bool isCommand) const
+{
+ if (!isCommand) {
+ for (KeyIDs::const_iterator i = ids.begin(); i != ids.end(); ++i) {
+ KeyID id = *i;
+ if (id != kKeyNone &&
+ ((id < 0xe000u || id > 0xefffu) ||
+ (id >= kKeyKP_Equal && id <= kKeyKP_9))) {
+ *mask |= KeyModifierAltGr;
+ return;
+ }
+ }
+ }
+}
+
+KeyButton
+OSXKeyState::mapVirtualKeyToKeyButton(UInt32 keyCode)
+{
+ // 'A' maps to 0 so shift every id
+ return static_cast<KeyButton>(keyCode + KeyButtonOffset);
+}
+
+UInt32
+OSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton)
+{
+ return static_cast<UInt32>(keyButton - KeyButtonOffset);
+}