IOHIDKeyboardFilter.cpp [plain text]
#include "IOHIDKeyboardFilter.h"
#include <TargetConditionals.h>
#include <new>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDServiceFilterPlugIn.h>
#include <IOKit/hid/IOHIDEventSystemPrivate.h>
#include <IOKit/hid/IOHIDEventTypes.h>
#include <IOKit/hid/IOHIDEventData.h>
#include <IOKit/hid/IOHIDSession.h>
#include <IOKit/hid/IOHIDService.h>
#include <IOKit/hid/IOHIDPrivateKeys.h>
#include <IOKit/hid/IOHIDEventSystemKeys.h>
#include <IOKit/hid/AppleHIDUsageTables.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <IOKit/hidsystem/event_status_driver.h>
#include <IOKit/hid/IOHIDServiceKeys.h>
#include "IOHIDFamilyPrivate.h"
#include <IOKit/IOKitLib.h>
#include <IOKit/usb/USB.h>
#include <notify.h>
#include <pthread.h>
#include <asl.h>
#include <fcntl.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <IOKit/hid/IOHIDLibPrivate.h>
#include "IOHIDDebug.h"
#include "CF.h"
#include <sstream>
#include "IOHIDPrivateKeys.h"
#include "IOHIDKeys.h"
#include "IOHIDevicePrivateKeys.h"
#define kCapsLockDelayMS 75
#define kEjectKeyDelayMS 0
#define kSlowKeyMinMS 1
#define kSlowRepeatDelayMS 420
#define kMouseKeyActivationCount 5
#define kMouseKeyActivationReset 30
#if TARGET_OS_EMBEDDED // Repeats handled in backboard on embedded platforms
#define kInitialKeyRepeatMS 0
#define kKeyRepeatMS 0
#define kMinKeyRepeatMS 0
#else
#define kInitialKeyRepeatMS 500
#define kKeyRepeatMS 83 // 1/12 sec
#define kMinKeyRepeatMS 16 // 1/60 sec
#endif
#define NX_MODIFIERKEY_NOACTION -1
#define NX_MODIFIERKEY_ALPHALOCK 0
#define NX_MODIFIERKEY_SHIFT 1
#define NX_MODIFIERKEY_CONTROL 2
#define NX_MODIFIERKEY_ALTERNATE 3
#define NX_MODIFIERKEY_COMMAND 4
#define NX_MODIFIERKEY_NUMERICPAD 5
#define NX_MODIFIERKEY_HELP 6
#define NX_MODIFIERKEY_SECONDARYFN 7
#define NX_MODIFIERKEY_NUMLOCK 8
#define NX_MODIFIERKEY_RSHIFT 9
#define NX_MODIFIERKEY_RCONTROL 10
#define NX_MODIFIERKEY_RALTERNATE 11
#define NX_MODIFIERKEY_RCOMMAND 12
#define NX_MODIFIERKEY_ALPHALOCK_STATELESS 13
#define kIOHIDKeyboardFilterFactory CFUUIDGetConstantUUIDWithBytes(kCFAllocatorSystemDefault, 0x55, 0x12, 0x66, 0x8E, 0xFF, 0x47, 0x4E, 0x70, 0xB3, 0x3E, 0xE1, 0xFF, 0xFA, 0xEF, 0x01, 0xA8)
#ifdef DEBUG
#define DEBUG_CODE(x) x
#else
#define DEBUG_CODE(x)
#endif
#define IOHIDEventIsSlowKeyPhaseEvent(e) \
(IOHIDEventGetIntegerValue (e, kIOHIDEventFieldKeyboardSlowKeyPhase) == kIOHIDKeyboardSlowKeyPhaseStart || \
IOHIDEventGetIntegerValue (e, kIOHIDEventFieldKeyboardSlowKeyPhase) == kIOHIDKeyboardSlowKeyPhaseAbort)
#define IOHIDEventIsDelayedEvent(e) \
(_IOHIDEventCopyAttachment(e, kIOHIDEventAttachment_Delayed, 0) == kCFBooleanTrue)
extern "C" void * IOHIDKeyboardFilterFactory(CFAllocatorRef allocator, CFUUIDRef typeUUID);
bool getUsageForLegacyModifier(SInt8 modifier, UInt32& usagePage, UInt32& usage);
UInt32 makeModifierLeftHanded(UInt32 usage);
UInt8 getModifierIndex(UInt32 usagePage, UInt32 usage);
void getUsageForIndex(UInt32 index, UInt32& usagePage, UInt32& usage);
bool isModifier(UInt32 usagePage, UInt32 usage);
bool isShiftKey(UInt32 usagePage, UInt32 usage);
bool isNotRepeated(UInt32 usagePage, UInt32 usage);
bool isStickyModifier(UInt32 usagePage, UInt32 usage);
const CFStringRef kIOHIDEventAttachment_Modified = CFSTR("Modified");
const CFStringRef kIOHIDEventAttachment_Delayed = CFSTR("Delayed");
void *IOHIDKeyboardFilterFactory(CFAllocatorRef allocator __unused, CFUUIDRef typeUUID)
{
if (CFEqual(typeUUID, kIOHIDServiceFilterPlugInTypeID)) {
void *p = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(IOHIDKeyboardFilter), 0);
return new(p) IOHIDKeyboardFilter(kIOHIDKeyboardFilterFactory);
}
return NULL;
}
IOHIDServiceFilterPlugInInterface IOHIDKeyboardFilter::sIOHIDKeyboardFilterFtbl =
{
NULL,
IOHIDKeyboardFilter::QueryInterface,
IOHIDKeyboardFilter::AddRef,
IOHIDKeyboardFilter::Release,
IOHIDKeyboardFilter::match,
IOHIDKeyboardFilter::filter,
NULL,
IOHIDKeyboardFilter::open,
IOHIDKeyboardFilter::close,
IOHIDKeyboardFilter::scheduleWithDispatchQueue,
IOHIDKeyboardFilter::unscheduleFromDispatchQueue,
IOHIDKeyboardFilter::copyPropertyForClient,
IOHIDKeyboardFilter::setPropertyForClient,
NULL,
IOHIDKeyboardFilter::setEventCallback,
};
IOHIDKeyboardFilter::IOHIDKeyboardFilter(CFUUIDRef factoryID)
:
_serviceInterface(&sIOHIDKeyboardFilterFtbl),
_factoryID( static_cast<CFUUIDRef>( CFRetain(factoryID) ) ),
_refCount(1),
_service(NULL),
_eventCallback(defaultEventCallback),
_eventTarget(0),
_eventContext(0),
_fnKeyMode(0),
_supportedModifiers(0),
_slowKeysSlowEvent(0),
_slowKeysDelayMS(0),
_stickyKeysShiftKeyCount(0),
_stickyKeyToggle(false),
_stickyKeyOn (false),
_stickyKeyDisable(false),
_keyRepeatEvent(NULL),
_keyRepeatInitialDelayMS(kMinKeyRepeatMS),
_keyRepeatDelayMS (kMinKeyRepeatMS),
_delayedCapsLockEvent(0),
_capsLockDelayMS(kCapsLockDelayMS),
_capsLockDelayOverrideMS(-1),
_numLockOn (0),
#if !TARGET_OS_EMBEDDED
_delayedEjectKeyEvent(NULL),
_ejectKeyDelayMS(kEjectKeyDelayMS),
_mouseKeyActivationEnable(false),
_mouseKeyActivationCount (0),
_ejectKeyDelayTimer(0),
_mouseKeyActivationResetTimer(0),
#endif
_capsLockState (false),
_capsLockLEDInhibit (false),
_capsLockLED(kIOHIDServiceCapsLockLEDKey_Auto),
_capsLockLEDState(false),
_queue(NULL),
_stickyKeysShiftResetTimer(0),
_slowKeysTimer(0),
_keyRepeatTimer(0),
_capsLockDelayTimer(0)
{
CFPlugInAddInstanceForFactory( factoryID );
for (int i = 0; i < MAX_STICKY_KEYS; i++) {
_stickyKeyState[i] = kStickyKeyState_Reset;
}
}
IOHIDKeyboardFilter::~IOHIDKeyboardFilter()
{
CFPlugInRemoveInstanceForFactory( _factoryID );
CFRelease( _factoryID );
}
HRESULT IOHIDKeyboardFilter::QueryInterface( void *self, REFIID iid, LPVOID *ppv )
{
return static_cast<IOHIDKeyboardFilter *>(self)->QueryInterface(iid, ppv);
}
HRESULT IOHIDKeyboardFilter::QueryInterface( REFIID iid, LPVOID *ppv )
{
CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes( NULL, iid );
if (CFEqual(interfaceID, kIOHIDSimpleServiceFilterPlugInInterfaceID) || CFEqual(interfaceID, kIOHIDServiceFilterPlugInInterfaceID)) {
AddRef();
*ppv = this;
CFRelease(interfaceID);
return S_OK;
}
if (CFEqual(interfaceID, IUnknownUUID)) {
AddRef();
*ppv = this;
CFRelease(interfaceID);
return S_OK;
}
*ppv = NULL;
CFRelease( interfaceID );
return E_NOINTERFACE;
}
ULONG IOHIDKeyboardFilter::AddRef( void *self )
{
return static_cast<IOHIDKeyboardFilter *>(self)->AddRef();
}
ULONG IOHIDKeyboardFilter::AddRef()
{
_refCount += 1;
return _refCount;
}
ULONG IOHIDKeyboardFilter::Release( void *self )
{
return static_cast<IOHIDKeyboardFilter *>(self)->Release();
}
ULONG IOHIDKeyboardFilter::Release()
{
_refCount -= 1;
if (_refCount == 0) {
delete this;
return 0;
}
return _refCount;
}
void IOHIDKeyboardFilter::open(void * self, IOHIDServiceRef service, IOOptionBits options)
{
static_cast<IOHIDKeyboardFilter *>(self)->open(service, options);
}
void IOHIDKeyboardFilter::open(IOHIDServiceRef service, IOOptionBits options)
{
CFDictionaryRef propDict;
CFTypeRef value = NULL;
(void)options;
_service = service;
#if !TARGET_OS_EMBEDDED
uint32_t keyboardID;
value = IOHIDServiceCopyProperty(_service, CFSTR(kIOHIDSubinterfaceIDKey));
if (value == NULL) {
keyboardID = getKeyboardID();
value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keyboardID);
if (value) {
IOHIDServiceSetProperty(_service, CFSTR(kIOHIDSubinterfaceIDKey), value);
CFRelease(value);
}
} else {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &keyboardID);
CFRelease(value);
}
value = IOHIDServiceCopyProperty(_service, CFSTR(kIOHIDKeyboardSupportsF12EjectKey));
if (value == NULL) {
boolean_t f12Support = false;
if (((keyboardID >= 0xc3) && (keyboardID <= 0xc9)) ||
((keyboardID >= 0x28) && (keyboardID <= 0x2a)) ||
keyboardID <= 0x1e) {
f12Support = true;
}
IOHIDServiceSetProperty(_service, CFSTR(kIOHIDKeyboardSupportsF12EjectKey), f12Support ? kCFBooleanTrue : kCFBooleanFalse);
} else {
CFRelease(value);
}
#endif
value = IOHIDServiceCopyProperty(_service, CFSTR(kIOHIDKeyboardSupportedModifiersKey));
if (value) {
if (CFGetTypeID(value) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &_supportedModifiers);
}
CFRelease(value);
}
value = IOHIDServiceCopyProperty(_service, CFSTR(kFnFunctionUsageMapKey));
if (value != NULL) {
if (CFGetTypeID(value) == CFStringGetTypeID()) {
_fnFunctionUsageMapKeyMap = createMapFromStringMap((CFStringRef)value);
}
CFRelease(value);
}
value = IOHIDServiceCopyProperty(_service, CFSTR(kFnKeyboardUsageMapKey));
if (value != NULL ) {
if (CFGetTypeID(value) == CFStringGetTypeID()) {
_fnKeyboardUsageMapKeyMap = createMapFromStringMap((CFStringRef)value);
}
CFRelease(value);
}
value = IOHIDServiceCopyProperty(_service, CFSTR(kNumLockKeyboardUsageMapKey));
if (value != NULL ) {
if (CFGetTypeID(value) == CFStringGetTypeID()) {
_numLockKeyboardUsageMapKeyMap = createMapFromStringMap((CFStringRef)value);
}
CFRelease(value);
}
propDict = (CFDictionaryRef)IOHIDServiceCopyProperty(_service, CFSTR(kIOHIDEventServicePropertiesKey));
if (propDict) {
value = CFDictionaryGetValue(propDict, CFSTR("HIDCapsLockStateCache"));
_capsLockState = ( value ? CFBooleanGetValue((CFBooleanRef)value) : false );
value = CFDictionaryGetValue(propDict, CFSTR(kIOHIDServiceCapsLockLEDInhibitKey));
_capsLockLEDInhibit = ( value ? CFBooleanGetValue((CFBooleanRef)value) : false );
_capsLockLED = kIOHIDServiceCapsLockLEDKey_Auto;
value = CFDictionaryGetValue(propDict, kIOHIDServiceCapsLockLEDKey);
if (value) {
if (CFEqual (value, kIOHIDServiceCapsLockLEDKey_On)) {
_capsLockLED = kIOHIDServiceCapsLockLEDKey_On;
} else if (CFEqual (value, kIOHIDServiceCapsLockLEDKey_Off)) {
_capsLockLED = kIOHIDServiceCapsLockLEDKey_Off;
} else if (CFEqual (value, kIOHIDServiceCapsLockLEDKey_Inhibit)) {
_capsLockLED = kIOHIDServiceCapsLockLEDKey_Inhibit;
}
}
value = CFDictionaryGetValue(propDict, CFSTR(kIOHIDServiceModifierMappingPairsKey));
if (value && CFGetTypeID(value) == CFArrayGetTypeID()) {
_modifiersKeyMap = createMapFromArrayOfPairs((CFArrayRef) value);
}
value = CFDictionaryGetValue(propDict, CFSTR(kIOHIDServiceSlowKeysDelayKey));
if (value) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &_slowKeysDelayMS);
if (_slowKeysDelayMS && _slowKeysDelayMS < kSlowKeyMinMS) {
_slowKeysDelayMS = kSlowKeyMinMS;
}
}
value = CFDictionaryGetValue(propDict, CFSTR(kIOHIDServiceInitialKeyRepeatDelayKey));
if (value) {
uint64_t valueProp = 0;
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt64Type, &valueProp);
_keyRepeatInitialDelayMS = valueProp / 1000000;
if (_keyRepeatInitialDelayMS && _keyRepeatInitialDelayMS < kMinKeyRepeatMS) {
_keyRepeatInitialDelayMS = kMinKeyRepeatMS;
}
}
value = CFDictionaryGetValue(propDict, CFSTR(kIOHIDServiceKeyRepeatDelayKey));
if (value) {
uint64_t valueProp = 0;
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt64Type, &valueProp);
_keyRepeatDelayMS = valueProp / 1000000;
if (_keyRepeatDelayMS && _keyRepeatDelayMS < kMinKeyRepeatMS) {
_keyRepeatDelayMS = kMinKeyRepeatMS;
}
}
#if !TARGET_OS_EMBEDDED
value = CFDictionaryGetValue(propDict, CFSTR(kIOHIDServiceEjectDelayKey));
if (value) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &_ejectKeyDelayMS);
}
#endif
value = CFDictionaryGetValue(propDict, CFSTR(kIOHIDServiceCapsLockDelayKey));
if (value) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &_capsLockDelayMS);
}
value = CFDictionaryGetValue(propDict, CFSTR(kIOHIDKeyboardCapsLockDelayOverride));
if (value) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &_capsLockDelayOverrideMS);
}
CFRelease(propDict);
}
}
void IOHIDKeyboardFilter::close(void * self, IOHIDServiceRef service, IOOptionBits options)
{
static_cast<IOHIDKeyboardFilter *>(self)->close(service, options);
}
void IOHIDKeyboardFilter::close(IOHIDServiceRef service __unused, IOOptionBits options __unused)
{
}
void IOHIDKeyboardFilter::scheduleWithDispatchQueue(void * self, dispatch_queue_t queue)
{
static_cast<IOHIDKeyboardFilter *>(self)->scheduleWithDispatchQueue(queue);
}
void IOHIDKeyboardFilter::scheduleWithDispatchQueue(dispatch_queue_t queue)
{
_queue = queue;
_stickyKeysShiftResetTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
if (_stickyKeysShiftResetTimer != NULL) {
dispatch_source_set_event_handler(_stickyKeysShiftResetTimer, ^{
dispatch_source_set_timer(_stickyKeysShiftResetTimer, DISPATCH_TIME_FOREVER, 0, 0);
_stickyKeysShiftKeyCount = 0;
});
dispatch_source_set_timer(_stickyKeysShiftResetTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatch_resume(_stickyKeysShiftResetTimer);
}
_slowKeysTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
if (_slowKeysTimer != NULL) {
dispatch_source_set_event_handler(_slowKeysTimer, ^{
dispatch_source_set_timer(_slowKeysTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatchSlowKey();
});
dispatch_source_set_timer(_slowKeysTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatch_resume(_slowKeysTimer);
}
_keyRepeatTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
if (_keyRepeatTimer != NULL) {
dispatch_source_set_event_handler(_keyRepeatTimer, ^{
dispatch_source_set_timer(_keyRepeatTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatchKeyRepeat();
});
dispatch_source_set_timer(_keyRepeatTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatch_resume(_keyRepeatTimer);
}
_capsLockDelayTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
if (_capsLockDelayTimer != NULL) {
dispatch_source_set_event_handler(_capsLockDelayTimer, ^{
dispatch_source_set_timer(_capsLockDelayTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatchCapsLock();
});
dispatch_source_set_timer(_capsLockDelayTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatch_resume(_capsLockDelayTimer);
}
#if !TARGET_OS_EMBEDDED
_mouseKeyActivationResetTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
if (_mouseKeyActivationResetTimer != NULL) {
dispatch_source_set_event_handler(_mouseKeyActivationResetTimer, ^{
_mouseKeyActivationCount = 0;
});
dispatch_source_set_timer(_mouseKeyActivationResetTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatch_resume(_mouseKeyActivationResetTimer);
}
_ejectKeyDelayTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
if (_ejectKeyDelayTimer != NULL) {
dispatch_source_set_event_handler(_ejectKeyDelayTimer, ^(void){
dispatch_source_set_timer(_ejectKeyDelayTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatchEjectKey();
});
dispatch_source_set_timer(_ejectKeyDelayTimer, DISPATCH_TIME_FOREVER, 0, 0);
dispatch_resume(_ejectKeyDelayTimer);
}
#endif
}
void IOHIDKeyboardFilter::unscheduleFromDispatchQueue(void * self, dispatch_queue_t queue)
{
static_cast<IOHIDKeyboardFilter *>(self)->unscheduleFromDispatchQueue(queue);
}
void IOHIDKeyboardFilter::unscheduleFromDispatchQueue(__unused dispatch_queue_t queue)
{
#if !TARGET_OS_EMBEDDED
if (_mouseKeyActivationResetTimer) {
dispatch_source_cancel(_mouseKeyActivationResetTimer);
dispatch_release(_mouseKeyActivationResetTimer);
}
#endif
if (_stickyKeysShiftResetTimer) {
dispatch_source_cancel(_stickyKeysShiftResetTimer);
dispatch_release(_stickyKeysShiftResetTimer);
}
if (_slowKeysTimer) {
dispatch_source_cancel(_slowKeysTimer);
dispatch_release(_slowKeysTimer);
}
if (_keyRepeatTimer) {
dispatch_source_cancel(_keyRepeatTimer);
dispatch_release(_keyRepeatTimer);
}
if (_capsLockDelayTimer) {
dispatch_source_cancel(_capsLockDelayTimer);
dispatch_release(_capsLockDelayTimer);
}
#if !TARGET_OS_EMBEDDED
if (_ejectKeyDelayTimer) {
dispatch_source_cancel(_ejectKeyDelayTimer);
dispatch_release (_ejectKeyDelayTimer);
}
#endif
__block IOHIDServiceRef service = (IOHIDServiceRef)CFRetain(_service);
dispatch_async(_queue, ^(void){
stopStickyKey();
if (_slowKeysSlowEvent) {
CFRelease(_slowKeysSlowEvent);
_slowKeysSlowEvent = NULL;
}
#if !TARGET_OS_EMBEDDED
if (_keyRepeatEvent) {
CFRelease(_keyRepeatEvent);
_keyRepeatEvent = NULL;
}
if (_delayedEjectKeyEvent) {
CFRelease(_delayedEjectKeyEvent);
_delayedEjectKeyEvent = NULL;
}
#endif
if (_delayedCapsLockEvent) {
CFRelease(_delayedCapsLockEvent);
_delayedCapsLockEvent = NULL;
}
CFRelease(service);
});
}
void IOHIDKeyboardFilter::setEventCallback(void * self, IOHIDServiceEventCallback callback, void * target, void * refcon)
{
static_cast<IOHIDKeyboardFilter *>(self)->setEventCallback(callback, target, refcon);
}
void IOHIDKeyboardFilter::setEventCallback(IOHIDServiceEventCallback callback, void * target, void * refcon)
{
HIDLogDebug("callback = %p target = %p refcon = %p", callback, target, refcon);
_eventCallback = callback ? callback : defaultEventCallback;
_eventTarget = target;
_eventContext = refcon;
}
CFTypeRef IOHIDKeyboardFilter::copyPropertyForClient(void * self,CFStringRef key,CFTypeRef client __unused)
{
return static_cast<IOHIDKeyboardFilter *>(self)->copyPropertyForClient(key, client);
}
CFTypeRef IOHIDKeyboardFilter::copyPropertyForClient(CFStringRef key, CFTypeRef client __unused)
{
CFTypeRef result = NULL;
if (CFEqual(key, CFSTR(kIOHIDServiceCapsLockStateKey))) {
return _capsLockState ? kCFBooleanTrue : kCFBooleanFalse;
} else if (CFEqual(key, CFSTR(kIOHIDServiceFilterDebugKey))) {
CFMutableDictionaryRefWrap serializer;
if (serializer) {
serialize(serializer);
result = CFRetain(serializer.Reference());
}
} else if (CFEqual(key, kIOHIDServiceCapsLockLEDKey)) {
result = _capsLockLEDState ? kIOHIDServiceCapsLockLEDKey_On : kIOHIDServiceCapsLockLEDKey_Off;
} else if (CFEqual(key, CFSTR(kIOHIDStickyKeysOnKey))) {
result = CFNumberRefWrap((SInt32)_stickyKeyOn);
}
return result;
}
void IOHIDKeyboardFilter::setPropertyForClient(void * self,CFStringRef key,CFTypeRef property,CFTypeRef client)
{
static_cast<IOHIDKeyboardFilter *>(self)->setPropertyForClient(key, property, client);
}
void IOHIDKeyboardFilter::setPropertyForClient(CFStringRef key,CFTypeRef property, CFTypeRef client __unused)
{
boolean_t stickyKeyOn = _stickyKeyOn;
boolean_t stickyKeyDisable = _stickyKeyDisable;
CFBooleanRef boolProp = (CFBooleanRef)property;
if (!boolProp) boolProp = kCFBooleanFalse;
if (!key) {
goto exit;
}
if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysDisabledKey), kNilOptions) == kCFCompareEqualTo) {
stickyKeyDisable = (property && CFBooleanGetValue((CFBooleanRef)property)) ? true : false;
HIDLogDebug("_stickyKeyDisable: %d", _stickyKeyDisable);
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysOnKey), kNilOptions) == kCFCompareEqualTo) {
stickyKeyOn = (property && CFBooleanGetValue((CFBooleanRef)property)) ? true : false;
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysShiftTogglesKey), kNilOptions) == kCFCompareEqualTo) {
_stickyKeyToggle = (property && CFBooleanGetValue((CFBooleanRef)property)) ? true : false;
_stickyKeysShiftKeyCount = 0;
HIDLogDebug("_stickyKeyToggle: %d", _stickyKeyToggle);
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceInitialKeyRepeatDelayKey), kNilOptions) == kCFCompareEqualTo) {
CFNumberRef numProp = (CFNumberRef)property;
if (numProp) {
uint64_t valueProp = 0;
CFNumberGetValue(numProp, kCFNumberSInt64Type, &valueProp);
_keyRepeatInitialDelayMS = valueProp / 1000000;
if (_keyRepeatInitialDelayMS && _keyRepeatInitialDelayMS < kMinKeyRepeatMS) {
_keyRepeatInitialDelayMS = kMinKeyRepeatMS;
}
HIDLogDebug("_keyRepeatInitialDelayMS: %d", (int)_keyRepeatInitialDelayMS);
}
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceKeyRepeatDelayKey), kNilOptions) == kCFCompareEqualTo) {
CFNumberRef numProp = (CFNumberRef)property;
if (numProp) {
uint64_t valueProp = 0;
CFNumberGetValue(numProp, kCFNumberSInt64Type, &valueProp);
_keyRepeatDelayMS = valueProp / 1000000;
if (_keyRepeatDelayMS && _keyRepeatDelayMS < kMinKeyRepeatMS) {
_keyRepeatDelayMS = kMinKeyRepeatMS;
}
HIDLogDebug("_keyRepeatDelayMS: %d", (int)_keyRepeatDelayMS);
}
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceCapsLockStateKey), kNilOptions) == kCFCompareEqualTo) {
bool capsLockState = (property && CFBooleanGetTypeID() == CFGetTypeID(property)) ? CFBooleanGetValue((CFBooleanRef)property) : false;
setCapsLockState(capsLockState);
HIDLogDebug("capsLockState: %d", capsLockState);
}
else if (CFEqual(key, kIOHIDServiceCapsLockLEDKey)) {
if (property) {
_capsLockLED = kIOHIDServiceCapsLockLEDKey_Auto;
if (CFEqual (property, kIOHIDServiceCapsLockLEDKey_On)) {
_capsLockLED = kIOHIDServiceCapsLockLEDKey_On;
} else if (CFEqual (property, kIOHIDServiceCapsLockLEDKey_Off)) {
_capsLockLED = kIOHIDServiceCapsLockLEDKey_Off;
} else if (CFEqual (property, kIOHIDServiceCapsLockLEDKey_Inhibit)) {
_capsLockLED = kIOHIDServiceCapsLockLEDKey_Inhibit;
}
updateCapslockLED();
}
HIDLogDebug("_capsLockLED: %@", _capsLockLED);
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceCapsLockLEDInhibitKey), kNilOptions) == kCFCompareEqualTo) {
_capsLockLEDInhibit = CFBooleanGetValue(boolProp);
updateCapslockLED();
HIDLogDebug("_capsLockLEDInhibit: %d", _capsLockLEDInhibit);
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceModifierMappingPairsKey), kNilOptions) == kCFCompareEqualTo) {
if (property && CFGetTypeID(property) == CFArrayGetTypeID()) {
_modifiersKeyMap = createMapFromArrayOfPairs((CFArrayRef) property);
HIDLogDebug("_modifiersKeyMap initialized");
}
} else if (CFStringCompare(key, CFSTR(kIOHIDFKeyModeKey), kNilOptions) == kCFCompareEqualTo) {
if (property && CFGetTypeID(property) == CFNumberGetTypeID()) {
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, &_fnKeyMode);
HIDLogDebug("_fnKeyMode: %x", _fnKeyMode);
}
} else if (CFStringCompare(key, CFSTR(kIOHIDUserKeyUsageMapKey), kNilOptions) == kCFCompareEqualTo) {
if (property && CFGetTypeID(property) == CFArrayGetTypeID()) {
_userKeyMap = createMapFromArrayOfPairs((CFArrayRef) property);
HIDLogDebug("_userKeyMap initialized");
}
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceSlowKeysDelayKey), kNilOptions) == kCFCompareEqualTo) {
if ( property && CFGetTypeID(property) == CFNumberGetTypeID() )
{
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, &_slowKeysDelayMS);
HIDLogDebug("_slowKeysDelayMS = %d", (unsigned int)_slowKeysDelayMS);
if (_slowKeysDelayMS == 0) {
resetSlowKey();
} else {
_slowKeysDelayMS = _slowKeysDelayMS < kSlowKeyMinMS ? kSlowKeyMinMS : _slowKeysDelayMS;
resetCapsLockDelay();
#if !TARGET_OS_EMBEDDED
resetEjectKeyDelay();
#endif
}
}
#if !TARGET_OS_EMBEDDED
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceEjectDelayKey), kNilOptions) == kCFCompareEqualTo) {
if ( property && CFGetTypeID(property) == CFNumberGetTypeID() )
{
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, &_ejectKeyDelayMS);
HIDLogDebug("_ejectKeyDelayMS: %d", _ejectKeyDelayMS);
if (_ejectKeyDelayMS == 0) {
resetEjectKeyDelay();
}
}
#endif
} else if (CFStringCompare(key, CFSTR(kIOHIDServiceCapsLockDelayKey), kNilOptions) == kCFCompareEqualTo) {
if ( property && CFGetTypeID(property) == CFNumberGetTypeID() ) {
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, &_capsLockDelayMS);
HIDLogDebug("_capsLockDelayMS: %d", (unsigned int)_capsLockDelayMS);
if (_capsLockDelayMS == 0) {
resetCapsLockDelay();
}
}
} else if (CFStringCompare(key, CFSTR(kIOHIDKeyboardCapsLockDelayOverride), kNilOptions) == kCFCompareEqualTo) {
if ( property && CFGetTypeID(property) == CFNumberGetTypeID() ) {
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, &_capsLockDelayOverrideMS);
HIDLogDebug("_capsLockDelayOverrideMS: %d", (int)_capsLockDelayOverrideMS);
if ((SInt32)_capsLockDelayMS == _capsLockDelayOverrideMS) {
_capsLockDelayOverrideMS = -1;
}
}
} else if (CFEqual(key,CFSTR(kIOHIDResetStickyKeyNotification))) {
if (_queue) {
updateStickyKeysState(kStickyKeyState_Down_Locked, kStickyKeyState_Down_Unlocked);
dispatch_async(_queue, ^{
dispatchStickyKeys(kStickyKeyState_Down);
});
}
#if !TARGET_OS_EMBEDDED
_mouseKeyActivationCount = 0;
#endif
_stickyKeysShiftKeyCount = 0;
#if !TARGET_OS_EMBEDDED
} else if (CFEqual(key, CFSTR (kIOHIDMouseKeysOnKey)) && property) {
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, &_numLockOn);
HIDLogDebug("_numLockOn: %d", _numLockOn);
} else if (CFEqual(key, CFSTR (kIOHIDMouseKeysOptionTogglesKey)) && property) {
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, &_mouseKeyActivationEnable);
HIDLogDebug("_mouseKeyActivationEnable: %d", _mouseKeyActivationEnable);
if (!_mouseKeyActivationEnable) {
if (_mouseKeyActivationResetTimer) {
dispatch_source_set_timer(_mouseKeyActivationResetTimer, DISPATCH_TIME_FOREVER, 0, 0);
}
_mouseKeyActivationCount = 0;
}
#endif
}
if (stickyKeyDisable != _stickyKeyDisable) {
_stickyKeyDisable = stickyKeyDisable;
if (stickyKeyDisable) {
stickyKeyOn = false;
}
}
if (stickyKeyOn != _stickyKeyOn) {
_stickyKeyOn = stickyKeyOn;
HIDLogDebug("_stickyKeyOn: %d", stickyKeyOn);
if (_queue) {
dispatch_async(_queue, ^{
if (stickyKeyOn) {
startStickyKey ();
} else {
stopStickyKey();
}
});
}
}
exit:
return;
}
SInt32 IOHIDKeyboardFilter::match(void * self, IOHIDServiceRef service, IOOptionBits options)
{
return static_cast<IOHIDKeyboardFilter *>(self)->match(service, options);
}
SInt32 IOHIDKeyboardFilter::match(IOHIDServiceRef service, IOOptionBits options)
{
SInt32 score = 0;
(void) options;
score += IOHIDServiceConformsTo(service, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
score += IOHIDServiceConformsTo(service, kHIDPage_Consumer, kHIDUsage_Csmr_ConsumerControl);
return score;
}
IOHIDEventRef IOHIDKeyboardFilter::filter(void * self, IOHIDEventRef event)
{
return static_cast<IOHIDKeyboardFilter *>(self)->filter(event);
}
IOHIDEventRef IOHIDKeyboardFilter::filter(IOHIDEventRef event)
{
IOHIDEventSenderID serviceID = 0;
if (!event || IOHIDEventGetType(event) != kIOHIDEventTypeKeyboard) {
return event;
}
serviceID = IOHIDEventGetSenderID(event);
do
{
processFnKeyState(event);
event = processKeyMappings(event);
if (_slowKeysDelayMS) {
event = processSlowKeys(event);
}
if (!_slowKeysDelayMS && _capsLockDelayMS && _capsLockDelayOverrideMS != 0) {
event = processCapsLockDelay(event);
}
#if !TARGET_OS_EMBEDDED
if (!_slowKeysDelayMS && _ejectKeyDelayMS && !isModifiersPressed()) {
event = processEjectKeyDelay(event);
}
#endif
if (!_stickyKeyDisable) {
event = processStickyKeys(event);
}
processCapsLockState(event);
#if !TARGET_OS_EMBEDDED
if (_mouseKeyActivationEnable) {
event = processMouseKeys(event);
}
#endif
if (_keyRepeatInitialDelayMS && _keyRepeatDelayMS) {
if (_slowKeysDelayMS) {
event = processKeyRepeats(event, kSlowRepeatDelayMS, kSlowRepeatDelayMS);
} else {
event = processKeyRepeats(event, _keyRepeatInitialDelayMS, _keyRepeatDelayMS);
}
}
processKeyState (event);
} while (0);
if (!event) {
HIDLogDebug("Event cancelled for serviceID: 0x%llx", serviceID);
}
return event;
}
#define MakeUsageIntoIndex(usage) ((usage) - kHIDUsage_KeyboardLeftControl + 1)
#define MakeIndexIntoUsage(index) (kHIDUsage_KeyboardLeftControl + (index) - 1)
#define kHighestKeyboardUsageIndex MakeUsageIntoIndex(kHIDUsage_KeyboardRightGUI)
#define kModifierIndexForFn (kHighestKeyboardUsageIndex + 1)
bool getUsageForLegacyModifier(SInt8 modifier, UInt32& usagePage, UInt32& usage)
{
switch (modifier) {
case NX_MODIFIERKEY_NOACTION: usagePage = 0;
usage = 0;
return true;
case NX_MODIFIERKEY_ALPHALOCK:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardCapsLock;
return true;
case NX_MODIFIERKEY_CONTROL:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardLeftControl;
return true;
case NX_MODIFIERKEY_SHIFT:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardLeftShift;
return true;
case NX_MODIFIERKEY_ALTERNATE:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardLeftAlt;
return true;
case NX_MODIFIERKEY_COMMAND:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardLeftGUI;
return true;
case NX_MODIFIERKEY_RCONTROL:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardRightControl;
return true;
case NX_MODIFIERKEY_RSHIFT:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardRightShift;
return true;
case NX_MODIFIERKEY_RALTERNATE:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardRightAlt;
return true;
case NX_MODIFIERKEY_RCOMMAND:
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardRightGUI;
return true;
case NX_MODIFIERKEY_SECONDARYFN:
usagePage = kHIDPage_AppleVendorTopCase;
usage = kHIDUsage_AV_TopCase_KeyboardFn;
return true;
default:
return false;
}
}
bool isShiftKey(UInt32 usagePage, UInt32 usage)
{
if (usagePage == kHIDPage_KeyboardOrKeypad)
return ((usage == kHIDUsage_KeyboardLeftShift) || (usage == kHIDUsage_KeyboardRightShift));
else
return false;
}
bool isStickyModifier(UInt32 usagePage, UInt32 usage)
{
if (usagePage == kHIDPage_KeyboardOrKeypad && usage == kHIDUsage_KeyboardCapsLock) {
return false;
}
return isModifier(usagePage, usage);
}
bool isModifier(UInt32 usagePage, UInt32 usage)
{
bool isModifier = false;
if ((usagePage == kHIDPage_KeyboardOrKeypad) &&
(((usage >= kHIDUsage_KeyboardLeftControl) && (usage <= kHIDUsage_KeyboardRightGUI)) || (usage == kHIDUsage_KeyboardCapsLock))) {
isModifier = true;
}
else if ((usagePage == kHIDPage_AppleVendorTopCase) && (usage == kHIDUsage_AV_TopCase_KeyboardFn)) {
isModifier = true;
}
return isModifier;
}
UInt32 makeModifierLeftHanded(UInt32 usage)
{
if (usage >= kHIDUsage_KeyboardRightControl)
usage -= (kHIDUsage_KeyboardRightControl - kHIDUsage_KeyboardLeftControl);
return usage;
}
UInt8 getModifierIndex(UInt32 usagePage, UInt32 usage)
{
if (usage == kHIDUsage_KeyboardCapsLock)
return 0;
if ((usagePage == kHIDPage_AppleVendorTopCase) && (usage == kHIDUsage_AV_TopCase_KeyboardFn))
return (kModifierIndexForFn);
usage = makeModifierLeftHanded(usage);
return MakeUsageIntoIndex(usage);
}
void getUsageForIndex(UInt32 index, UInt32& usagePage, UInt32& usage)
{
if (index == 0) {
usagePage = kHIDPage_KeyboardOrKeypad;
usage = kHIDUsage_KeyboardCapsLock;
}
else if (index < kModifierIndexForFn) {
usagePage = kHIDPage_KeyboardOrKeypad;
usage = MakeIndexIntoUsage(index);
}
else if (index == kModifierIndexForFn) {
usagePage = kHIDPage_AppleVendorTopCase;
usage = kHIDUsage_AV_TopCase_KeyboardFn;
}
}
bool isNotRepeated(UInt32 usagePage, UInt32 usage)
{
bool isNotRepeated = false;
if (isModifier(usagePage, usage)) {
isNotRepeated = true;
}
else if ((usagePage == kHIDPage_KeyboardOrKeypad) &&
((usage == kHIDUsage_KeypadNumLock) ||
(usage == kHIDUsage_KeyboardPower) ||
(usage == kHIDUsage_KeyboardMute))) {
isNotRepeated = true;
}
else if ((usagePage == kHIDPage_Consumer) &&
((usage == kHIDUsage_Csmr_Play) ||
(usage == kHIDUsage_Csmr_Eject)||
(usage == kHIDUsage_Csmr_PlayOrPause) ||
(usage == kHIDUsage_Csmr_Menu) ||
(usage == kHIDUsage_Csmr_Power) ||
(usage == kHIDUsage_Csmr_Sleep)
)) {
isNotRepeated = true;
}
else if ((usagePage == kHIDPage_AppleVendorTopCase) &&
((usage == kHIDUsage_AV_TopCase_IlluminationToggle) ||
(usage == kHIDUsage_AV_TopCase_VideoMirror))) {
isNotRepeated = true;
}
return isNotRepeated;
}
KeyMap IOHIDKeyboardFilter::createMapFromArrayOfPairs(CFArrayRef mappings) {
KeyMap map;
if ( mappings == NULL || !CFArrayGetCount(mappings) ) {
return map;
}
for ( CFIndex i = 0; i < CFArrayGetCount(mappings); i++ ) {
CFDictionaryRef pair = NULL;
CFNumberRef num = NULL;
uint64_t src = 0;
uint64_t dst = 0;
pair = (CFDictionaryRef)CFArrayGetValueAtIndex(mappings, i);
if ( pair == NULL || CFGetTypeID(pair) != CFDictionaryGetTypeID()) {
continue;
}
num = (CFNumberRef)CFDictionaryGetValue(pair, CFSTR(kIOHIDServiceModifierMappingSrcKey));
if ( !num ) {
continue;
}
CFNumberGetValue(num, kCFNumberSInt64Type, &src);
num = (CFNumberRef)CFDictionaryGetValue(pair, CFSTR(kIOHIDServiceModifierMappingDstKey));
if ( !num ) {
continue;
}
CFNumberGetValue(num, kCFNumberSInt64Type, &dst);
map.insert(std::make_pair(Key (src), Key (dst)));
}
return map;
}
KeyMap IOHIDKeyboardFilter::createMapFromStringMap(CFStringRef mappings) {
KeyMap map;
const char *stringMap;
if ( mappings == NULL || (stringMap = (CFStringGetCStringPtr(mappings, kCFStringEncodingMacRoman))) == NULL) {
return map;
}
std::istringstream ss (stringMap);
std::string srcS, dstS;
while(std::getline(ss, srcS, ',') && std::getline(ss, dstS, ',')) {
uint64_t usageAndPage;
usageAndPage = std::stoul(dstS, nullptr, 16);
if (usageAndPage == 0) {
continue;
}
Key dstKey ((uint32_t)(usageAndPage >> 16), usageAndPage & 0xffff);
usageAndPage = std::stoul(srcS, nullptr, 16);
Key srcKey ((uint32_t)(usageAndPage >> 16), usageAndPage & 0xffff);
map.insert(std::make_pair(srcKey, dstKey));
}
return map;
}
void IOHIDKeyboardFilter::processFnKeyState(IOHIDEventRef event) {
UInt32 usage;
UInt32 usagePage;
UInt32 flags;
bool keyDown;
if (!event) {
return;
}
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
if (keyDown) {
return;
}
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
if ( !((usagePage == kHIDPage_AppleVendorTopCase) && (usage == kHIDUsage_AV_TopCase_KeyboardFn)) &&
!((usagePage == kHIDPage_AppleVendorKeyboard) && (usage == kHIDUsage_AppleVendorKeyboard_Function))) {
return;
}
std::map<Key,KeyAttribute> activeKey = _activeKeys;
auto iter = activeKey.begin();
for (;iter != activeKey.end();++iter) {
if (!iter->first._modified) {
if (!((iter->first.usagePage() == kHIDPage_KeyboardOrKeypad) &&
((iter->first.usage() >= kHIDUsage_KeyboardF1) && (iter->first.usage() <= kHIDUsage_KeyboardF12)))) {
continue;
}
}
IOHIDEventRef ev = IOHIDEventCreateKeyboardEvent(
kCFAllocatorDefault,
mach_absolute_time(),
iter->first.usagePage(),
iter->first.usage(),
0,
0);
if (ev) {
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, ev, 0);
CFRelease(ev);
}
}
}
IOHIDEventRef IOHIDKeyboardFilter::processKeyMappings(IOHIDEventRef event)
{
UInt32 usage;
UInt32 usagePage;
UInt32 flags;
CFTypeRef modified;
if (!event) {
goto exit;
}
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
flags = (UInt32)IOHIDEventGetEventFlags(event);
if (_keyRepeatEvent &&
usage == (UInt32)IOHIDEventGetIntegerValue(_keyRepeatEvent, kIOHIDEventFieldKeyboardUsage) &&
usagePage == (UInt32)IOHIDEventGetIntegerValue(_keyRepeatEvent, kIOHIDEventFieldKeyboardUsagePage)
) {
goto exit;
}
if ((flags & kKeyboardOptionMask) == 0 &&
IOHIDEventGetIntegerValue (event, kIOHIDEventFieldKeyboardSlowKeyPhase) == kIOHIDKeyboardSlowKeyNone &&
!IOHIDEventIsDelayedEvent(event)) {
Key src = Key (usagePage, usage);
Key key = remapKey (src);
if (!key.isValid()) {
return NULL;
}
if (key.usage() != usage || key.usagePage() != usagePage) {
_IOHIDEventSetAttachment(event, kIOHIDEventAttachment_Modified, kCFBooleanTrue, 0);
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardUsage, key.usage());
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage, key.usagePage());
}
}
exit:
return event;
}
Key IOHIDKeyboardFilter::remapKey(Key key) {
KeyMap::const_iterator iter;
if (key == Key(kHIDPage_KeyboardOrKeypad, kHIDUsage_KeyboardF5) &&
(isKeyPressed (Key(kHIDPage_KeyboardOrKeypad, kHIDUsage_KeyboardLeftGUI)) ||
isKeyPressed (Key(kHIDPage_KeyboardOrKeypad, kHIDUsage_KeyboardRightGUI)))) {
return key;
}
boolean_t FnKeyDown = isKeyPressed(Key(kHIDPage_AppleVendorTopCase, kHIDUsage_AV_TopCase_KeyboardFn)) ||
isKeyPressed(Key(kHIDPage_AppleVendorKeyboard, kHIDUsage_AppleVendorKeyboard_Function));
if (!(FnKeyDown ^ _fnKeyMode)) {
iter = _fnFunctionUsageMapKeyMap.find(key);
if (iter != _fnFunctionUsageMapKeyMap.end()) {
key = Key (iter->second._value);
}
}
if (FnKeyDown) {
iter = _fnKeyboardUsageMapKeyMap.find(key);
if (iter != _fnKeyboardUsageMapKeyMap.end()) {
key = Key (iter->second._value);
}
}
if (isNumLockMode()) {
iter = _numLockKeyboardUsageMapKeyMap.find (key);
if (iter != _numLockKeyboardUsageMapKeyMap.end()) {
key = Key (iter->second._value);
}
}
iter = _modifiersKeyMap.find(key);
if (iter != _modifiersKeyMap.end()) {
key = Key (iter->second._value);
}
iter = _userKeyMap.find(key);
if (iter != _userKeyMap.end()) {
key = Key (iter->second._value);
}
return key;
}
bool IOHIDKeyboardFilter::isNumLockMode() {
return _numLockOn ? true : false;
}
bool IOHIDKeyboardFilter::isKeyPressed (Key key) {
return _activeKeys.find (key) != _activeKeys.end();
}
StickyKeyState IOHIDKeyboardFilter::getStickyKeyState(UInt32 usagePage, UInt32 usage)
{
return _stickyKeyState[getModifierIndex(usagePage, usage)];
}
void IOHIDKeyboardFilter::setStickyKeyState(UInt32 usagePage, UInt32 usage, StickyKeyState state)
{
uint8_t index = getModifierIndex(usagePage, usage);
HIDLogDebug("StickyKey state %x -> %x", (unsigned int)_stickyKeyState[index], (unsigned int)state);
_stickyKeyState[index] = state;
}
uint32_t IOHIDKeyboardFilter::processStickyKeyDown(UInt32 usagePage, UInt32 usage)
{
StickyKeyState state = getStickyKeyState(usagePage, usage);
StickyKeyState newState = state;
uint32_t flags = 0;
switch (state) {
case kStickyKeyState_Reset:
flags = kIOHIDKeyboardStickyKeyDown;
newState = kStickyKeyState_Down_Locked;
break;
case kStickyKeyState_Down:
flags = kIOHIDKeyboardStickyKeyLocked;
newState = kStickyKeyState_Locked;
break;
case kStickyKeyState_Locked:
newState = kStickyKeyState_Reset;
break;
default:
HIDLogError("StickyKey DOWN in bad state for 0x%x:0x%x", (int)usagePage, (int)usage);
}
setStickyKeyState(usagePage, usage, newState);
HIDLogDebug("StickyKey DOWN 0x%x:0x%x flags 0x%x",
(unsigned int)usage,
(unsigned int)usagePage,
flags
);
return flags;
}
uint32_t IOHIDKeyboardFilter::processStickyKeyUp(UInt32 usagePage, UInt32 usage)
{
StickyKeyState state = getStickyKeyState(usagePage, usage);
StickyKeyState newState = state;
uint32_t flags = 0;
switch (state) {
case kStickyKeyState_Reset:
flags = kIOHIDKeyboardStickyKeyUp;
break;
case kStickyKeyState_Down_Locked:
newState = kStickyKeyState_Down;
break;
case kStickyKeyState_Down_Unlocked:
flags = kIOHIDKeyboardStickyKeyUp;
newState = kStickyKeyState_Reset;
break;
case kStickyKeyState_Locked:
break;
default:
HIDLogError("StickyKey UP in bad state for 0x%x:0x%x", (int)usagePage, (int)usage);
}
if (state != newState) {
setStickyKeyState(usagePage, usage, newState);
}
HIDLogDebug("StickyKey UP 0x%x:0x%x flags 0x%x",
(unsigned int)usage,
(unsigned int)usagePage,
flags
);
return flags;
}
void IOHIDKeyboardFilter::updateStickyKeysState(StickyKeyState from, StickyKeyState to) {
int i = 0;
for (;i < MAX_STICKY_KEYS; i++) {
UInt32 usage = 0;
UInt32 usagePage = 0;
StickyKeyState state;
getUsageForIndex(i, usagePage, usage);
state = getStickyKeyState(usagePage, usage);
if (state == from) {
setStickyKeyState(usagePage, usage, to);
}
}
}
void IOHIDKeyboardFilter::dispatchStickyKeys(int stateMask)
{
int i = 0;
for (;i < MAX_STICKY_KEYS; i++) {
UInt32 usage = 0;
UInt32 usagePage = 0;
StickyKeyState state;
getUsageForIndex(i, usagePage, usage);
state = getStickyKeyState(usagePage, usage);
HIDLogDebug("StickyKey [%d] 0x%x:0x%x state 0x%x mask 0x%x", (int)i, (int)usagePage, (int)usage, (unsigned int)state, stateMask);
if ((state & stateMask) == 0)
continue;
IOHIDEventRef event = IOHIDEventCreateKeyboardEvent(kCFAllocatorDefault, mach_absolute_time(), usagePage, usage, 0, kIOHIDKeyboardStickyKeyUp);
if (!event) {
continue;
}
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, event, 0);
CFRelease(event);
setStickyKeyState(usagePage, usage, kStickyKeyState_Reset);
}
}
void IOHIDKeyboardFilter::processShiftKey(void)
{
if (_stickyKeyToggle == false) {
return;
}
bool timerWasEnabed = _stickyKeysShiftKeyCount++;
if (!timerWasEnabed)
dispatch_source_set_timer(_stickyKeysShiftResetTimer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * kStickyKeysShiftKeyInterval),
DISPATCH_TIME_FOREVER,
0);
if (_stickyKeysShiftKeyCount >= kStickyKeysEnableCount) {
_stickyKeysShiftKeyCount = 0;
_stickyKeyOn = !_stickyKeyOn;
HIDLogDebug("StickyKey state change (5xSHIFT) to %s", _stickyKeyOn ? "ON" : "OFF");
IOHIDServiceSetProperty(_service, CFSTR(kIOHIDServiceStickyKeysOnKey), (_stickyKeyOn ? kCFBooleanTrue : kCFBooleanFalse));
#if !TARGET_OS_EMBEDDED
setHIDSystemParam (CFSTR(kIOHIDStickyKeysOnKey), _stickyKeyOn ? 0 : 1);
#endif
if (_stickyKeyOn) {
startStickyKey();
} else {
stopStickyKey();
}
}
}
IOHIDEventRef IOHIDKeyboardFilter::processStickyKeys(IOHIDEventRef event)
{
UInt32 usage;
UInt32 usagePage;
UInt32 flags;
bool keyDown;
bool stickyKeysOn = _stickyKeyOn;
if (!event || (IOHIDEventGetEventFlags(event) & kIOHIDKeyboardStickyKeyUp) || IOHIDEventIsSlowKeyPhaseEvent(event)) {
goto exit;
}
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
if (isShiftKey(usagePage, usage)) {
if (keyDown == 0) {
processShiftKey();
}
} else {
_stickyKeysShiftKeyCount = 0;
}
if (!_stickyKeyOn) {
if (_stickyKeyOn != stickyKeysOn) {
IOHIDEventSetEventFlags(event, IOHIDEventGetEventFlags(event) | kIOHIDKeyboardStickyKeysOff);
}
goto exit;
}
if (!isStickyModifier(usagePage, usage)) {
if (_stickyKeysShiftKeyCount)
dispatch_source_set_timer(_stickyKeysShiftResetTimer, DISPATCH_TIME_FOREVER, 0, 0);
if (!keyDown){
dispatch_async(_queue, ^{
dispatchStickyKeys(kStickyKeyState_Down);
});
_stickyKeysShiftKeyCount = 0;
}
updateStickyKeysState(kStickyKeyState_Down_Locked, kStickyKeyState_Down_Unlocked);
} else {
if (_stickyKeyOn != stickyKeysOn) {
IOHIDEventSetEventFlags(event, IOHIDEventGetEventFlags(event) | kIOHIDKeyboardStickyKeysOn);
goto exit;
}
if (keyDown) {
flags = processStickyKeyDown(usagePage, usage);
} else {
flags = processStickyKeyUp(usagePage, usage);
}
if (!flags) {
event = NULL;
} else {
usage = makeModifierLeftHanded(usage);
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardUsage, usage);
IOHIDEventSetEventFlags(event, IOHIDEventGetEventFlags(event) | flags);
}
}
exit:
return event;
}
void IOHIDKeyboardFilter::startStickyKey () {
std::map<Key,KeyAttribute> activeKey = _activeKeys;
auto iter = activeKey.begin();
for (;iter != activeKey.end();++iter) {
if (iter->first.isModifier()) {
IOHIDEventRef event = IOHIDEventCreateKeyboardEvent(
kCFAllocatorDefault,
mach_absolute_time(),
iter->first.usagePage(),
iter->first.usage(),
0,
kIOHIDKeyboardStickyKeyUp);
if (event) {
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, event, 0);
CFRelease(event);
}
}
}
}
void IOHIDKeyboardFilter::stopStickyKey () {
dispatchStickyKeys (kStickyKeyState_Down | kStickyKeyState_Down_Locked | kStickyKeyState_Locked);
}
void IOHIDKeyboardFilter::resetSlowKey(void)
{
if (_slowKeysSlowEvent) {
CFRelease(_slowKeysSlowEvent);
_slowKeysSlowEvent = NULL;
}
}
void IOHIDKeyboardFilter::dispatchSlowKey(void)
{
IOHIDEventRef event = _slowKeysSlowEvent;
_slowKeysSlowEvent = NULL;
if (!event)
return;
IOHIDEventSetTimeStamp(event, mach_absolute_time());
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardSlowKeyPhase, kIOHIDKeyboardSlowKeyOn);
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, event, 0);
CFRelease(event);
}
IOHIDEventRef IOHIDKeyboardFilter::processSlowKeys(IOHIDEventRef event)
{
UInt32 usage = 0;
UInt32 usagePage = 0;
bool keyDown = 0;
if (!event)
goto exit;
if (IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardSlowKeyPhase) == kIOHIDKeyboardSlowKeyOn) {
goto exit;
}
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
if (keyDown) {
#if !TARGET_OS_EMBEDDED
if (_slowKeysSlowEvent &&
((UInt32)IOHIDEventGetIntegerValue(_slowKeysSlowEvent, kIOHIDEventFieldKeyboardUsage) != usage ||
(UInt32)IOHIDEventGetIntegerValue(_slowKeysSlowEvent, kIOHIDEventFieldKeyboardUsagePage)!= usagePage)) {
IOHIDEventSetIntegerValue (event, kIOHIDEventFieldKeyboardSlowKeyPhase, kIOHIDKeyboardSlowKeyPhaseAbort);
dispatch_source_set_timer(_slowKeysTimer, DISPATCH_TIME_FOREVER, 0, 0);
resetSlowKey();
} else {
if (_slowKeysSlowEvent) {
CFRelease(_slowKeysSlowEvent);
}
_slowKeysSlowEvent = IOHIDEventCreateCopy (CFGetAllocator(event), event);
IOHIDEventSetIntegerValue (event, kIOHIDEventFieldKeyboardSlowKeyPhase, kIOHIDKeyboardSlowKeyPhaseStart);
dispatch_source_set_timer(_slowKeysTimer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * _slowKeysDelayMS),
DISPATCH_TIME_FOREVER, 0);
}
#else
CFRetain(event);
if (_slowKeysSlowEvent) {
CFRelease(_slowKeysSlowEvent);
}
_slowKeysSlowEvent = event;
dispatch_source_set_timer(_slowKeysTimer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * _slowKeysDelayMS),
DISPATCH_TIME_FOREVER, 0);
event = NULL;
#endif
} else {
if (_slowKeysSlowEvent) {
if (((UInt32)IOHIDEventGetIntegerValue(_slowKeysSlowEvent, kIOHIDEventFieldKeyboardUsage) == usage) &&
((UInt32)IOHIDEventGetIntegerValue(_slowKeysSlowEvent, kIOHIDEventFieldKeyboardUsagePage) == usagePage)) {
dispatch_source_set_timer(_slowKeysTimer, DISPATCH_TIME_FOREVER, 0, 0);
if ((IOHIDEventGetEventFlags(_slowKeysSlowEvent) & kIOHIDKeyboardIsRepeat) == 0) {
event = NULL;
}
resetSlowKey();
}
}
}
exit:
return event;
}
void IOHIDKeyboardFilter::dispatchKeyRepeat(void)
{
IOHIDEventRef event = _keyRepeatEvent;
_keyRepeatEvent = NULL;
if (!event) {
return ;
}
IOHIDEventSetTimeStamp(event, mach_absolute_time());
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, event, 0);
CFRelease(event);
}
IOHIDEventRef IOHIDKeyboardFilter::processKeyRepeats(IOHIDEventRef event, UInt32 keyRepeatInitialDelayMS, UInt32 keyRepeatDelayMS)
{
UInt32 usage = 0;
UInt32 usagePage = 0;
bool keyDown = 0;
if (!event || IOHIDEventIsSlowKeyPhaseEvent(event)) {
goto exit;
}
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
if (isNotRepeated(usagePage, usage)) {
goto exit;
}
if (keyDown) {
if (_keyRepeatEvent) {
CFRelease(_keyRepeatEvent);
}
_keyRepeatEvent = IOHIDEventCreateKeyboardEvent(CFGetAllocator(event), mach_absolute_time(), usagePage, usage, true, kIOHIDKeyboardIsRepeat);
dispatch_source_set_timer(_keyRepeatTimer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC *
((IOHIDEventGetEventFlags(event) & kIOHIDKeyboardIsRepeat) ? keyRepeatDelayMS : keyRepeatInitialDelayMS)),
DISPATCH_TIME_FOREVER, 0);
} else {
if (_keyRepeatEvent &&
usage == (UInt32)IOHIDEventGetIntegerValue(_keyRepeatEvent, kIOHIDEventFieldKeyboardUsage) &&
usagePage == (UInt32)IOHIDEventGetIntegerValue(_keyRepeatEvent, kIOHIDEventFieldKeyboardUsagePage)
) {
dispatch_source_set_timer(_keyRepeatTimer, DISPATCH_TIME_FOREVER, 0, 0);
CFRelease(_keyRepeatEvent);
_keyRepeatEvent = NULL;
}
}
exit:
return event;
}
void IOHIDKeyboardFilter::processCapsLockState(IOHIDEventRef event)
{
UInt32 usage;
UInt32 usagePage;
bool keyDown;
if (!event || IOHIDEventIsSlowKeyPhaseEvent(event)) {
goto exit;
}
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
if ( (usagePage == kHIDPage_KeyboardOrKeypad) && (usage == kHIDUsage_KeyboardCapsLock)) {
if (keyDown) {
_capsLockState = !_capsLockState;
HIDLogDebug("CapsLock state: %s", _capsLockState ? "ON" : "OFF");
#if !TARGET_OS_EMBEDDED
updateCapslockLED ();
#endif
IOHIDServiceSetProperty(_service, CFSTR("HIDCapsLockStateCache"), _capsLockState ? kCFBooleanTrue : kCFBooleanFalse);
}
}
exit:
return;
}
void IOHIDKeyboardFilter::resetCapsLockDelay(void)
{
if (_delayedCapsLockEvent) {
CFRelease(_delayedCapsLockEvent);
_delayedCapsLockEvent = NULL;
}
}
void IOHIDKeyboardFilter::dispatchCapsLock(void)
{
IOHIDEventRef event = _delayedCapsLockEvent;
_delayedCapsLockEvent = NULL;
if (!event)
return;
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, event, 0);
CFRelease(event);
}
IOHIDEventRef IOHIDKeyboardFilter::processCapsLockDelay(IOHIDEventRef event)
{
UInt32 usage;
UInt32 usagePage;
bool keyDown;
UInt32 capsLockDelayMS;
if (!event)
goto exit;
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
if ( !((usagePage == kHIDPage_KeyboardOrKeypad) && (usage == kHIDUsage_KeyboardCapsLock))) {
goto exit;
}
if (IOHIDEventIsDelayedEvent(event)) {
_IOHIDEventRemoveAttachment(event, kIOHIDEventAttachment_Delayed, 0);
goto exit;
}
capsLockDelayMS = _capsLockDelayOverrideMS > 0 ? _capsLockDelayOverrideMS : _capsLockDelayMS;
if (keyDown && !_capsLockState) {
dispatch_source_set_timer(_capsLockDelayTimer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * capsLockDelayMS),
DISPATCH_TIME_FOREVER, 0);
_IOHIDEventSetAttachment(event, kIOHIDEventAttachment_Delayed, kCFBooleanTrue, 0);
_delayedCapsLockEvent = event;
CFRetain(event);
event = NULL;
} else {
if (_delayedCapsLockEvent) {
dispatch_source_set_timer(_capsLockDelayTimer, DISPATCH_TIME_FOREVER, 0, 0);
resetCapsLockDelay();
event = NULL;
}
}
exit:
return event;
}
#if !TARGET_OS_EMBEDDED
void IOHIDKeyboardFilter::resetEjectKeyDelay(void)
{
if (_delayedEjectKeyEvent) {
CFRelease(_delayedEjectKeyEvent);
_delayedEjectKeyEvent = NULL;
}
}
void IOHIDKeyboardFilter::dispatchEjectKey(void)
{
IOHIDEventRef event = _delayedEjectKeyEvent;
_delayedEjectKeyEvent = NULL;
if (!event)
return;
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, event, 0);
CFRelease (event);
}
IOHIDEventRef IOHIDKeyboardFilter::processEjectKeyDelay(IOHIDEventRef event)
{
UInt32 usage = 0;
UInt32 usagePage = 0;
bool keyDown = 0;
if (!event)
goto exit;
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
if (!((usagePage == kHIDPage_Consumer) && (usage == kHIDUsage_Csmr_Eject))) {
goto exit;
}
if (IOHIDEventIsDelayedEvent(event)) {
_IOHIDEventRemoveAttachment(event, kIOHIDEventAttachment_Delayed, 0);
goto exit;
}
HIDLogDebug("keyDown = %d _delayedEjectKeyEvent = %p", (int)keyDown, _delayedEjectKeyEvent);
if (keyDown) {
dispatch_source_set_timer(_ejectKeyDelayTimer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * _ejectKeyDelayMS),
DISPATCH_TIME_FOREVER, 0);
_IOHIDEventSetAttachment(event, kIOHIDEventAttachment_Delayed, kCFBooleanTrue, 0);
_delayedEjectKeyEvent = event;
CFRetain(_delayedEjectKeyEvent);
event = NULL;
}
else {
if (_delayedEjectKeyEvent) {
dispatch_source_set_timer(_ejectKeyDelayTimer, DISPATCH_TIME_FOREVER, 0, 0);
resetEjectKeyDelay();
event = NULL;
}
}
exit:
return event;
}
IOHIDEventRef IOHIDKeyboardFilter::processMouseKeys (IOHIDEventRef event) {
uint32_t usage;
uint32_t usagePage;
uint32_t keyDown;
if (!event || IOHIDEventIsSlowKeyPhaseEvent(event)) {
goto exit;
}
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
if (usagePage == kHIDPage_KeyboardOrKeypad &&
(usage == kHIDUsage_KeyboardLeftAlt || usage == kHIDUsage_KeyboardRightAlt)
) {
if (_mouseKeyActivationCount == 0) {
dispatch_source_set_timer(_mouseKeyActivationResetTimer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * kMouseKeyActivationReset),
DISPATCH_TIME_FOREVER,
0
);
}
if (keyDown) {
_mouseKeyActivationCount++;
} else if (_mouseKeyActivationCount >= kMouseKeyActivationCount) {
HIDLogDebug("MouseKey (5xALT) Toggle");
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardMouseKeyToggle, kIOHIDKeyboardMouseKeyToggle);
_mouseKeyActivationCount = 0;
}
} else {
_mouseKeyActivationCount = 0;
}
exit:
return event;
}
kern_return_t IOHIDKeyboardFilter::setHIDSystemParam(CFStringRef key, uint32_t property) {
kern_return_t kr = kIOReturnInvalid;
NXEventHandle hidSystem = NXOpenEventStatus();
if (hidSystem) {
CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &property);
if (value) {
kr = IOConnectSetCFProperty(hidSystem, key , value);
CFRelease(value);
}
NXCloseEventStatus(hidSystem);
}
return kr;
}
#endif
void IOHIDKeyboardFilter::processKeyState (IOHIDEventRef event) {
uint32_t usage;
uint32_t usagePage;
uint32_t keyDown;
uint32_t flags;
CFBooleanRef modified = NULL;
Key key;
if (!event || IOHIDEventIsSlowKeyPhaseEvent(event)) {
return;
}
usage = (uint32_t)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (uint32_t)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (uint32_t)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
flags = (uint32_t)IOHIDEventGetEventFlags(event);
modified = (CFBooleanRef)_IOHIDEventCopyAttachment(event, kIOHIDEventAttachment_Modified, 0);
if (modified == kCFBooleanTrue) {
key = Key(usagePage, usage, true);
} else {
key = Key(usagePage, usage);
}
if (modified) {
CFRelease(modified);
}
if (keyDown) {
_activeKeys.insert(std::make_pair(key, KeyAttribute(flags)));
} else {
auto iter = _activeKeys.find(key);
if (iter!=_activeKeys.end()) {
_activeKeys.erase(iter);
}
}
DEBUG_CODE ({
std::stringstream s;
for(auto const entry : _activeKeys) {
s <<"0x"<<std::hex<<entry.first.usagePage()<<":0x"<<std::hex<<entry.first.usage()<<" ";
}
HIDLogDebug("ActiveKeys: %s", s.str().c_str());
});
}
void IOHIDKeyboardFilter::setCapsLockState(boolean_t state) {
#if !TARGET_OS_EMBEDDED
if (state == _capsLockState) {
return;
}
_capsLockState = state;
updateCapslockLED();
#endif
}
void IOHIDKeyboardFilter::updateCapslockLED() {
if ((_supportedModifiers & NX_ALPHASHIFT_STATELESS_MASK) == 0) {
return;
}
_capsLockLEDState = _capsLockState;
if (CFEqual(_capsLockLED, kIOHIDServiceCapsLockLEDKey_Inhibit)) {
return;
} else if (CFEqual(_capsLockLED, kIOHIDServiceCapsLockLEDKey_On)) {
_capsLockLEDState = true;
} else if (CFEqual(_capsLockLED, kIOHIDServiceCapsLockLEDKey_Off)) {
_capsLockLEDState = false;
} else if (_capsLockLEDInhibit) {
_capsLockLEDState = false;
}
if (_service) {
HIDLogDebug ("Set Capslock LED: %d", _capsLockLEDState);
IOHIDServiceSetElementValue(_service, kHIDPage_LEDs, kHIDUsage_LED_CapsLock, _capsLockLEDState);
}
}
CFMutableArrayRefWrap IOHIDKeyboardFilter::serializeMapper (const KeyMap &mapper) const {
CFMutableArrayRefWrap result ((int)mapper.size());
auto iter = mapper.begin();
for ( ;iter!= mapper.end();iter++) {
CFMutableDictionaryRefWrap pair (2);
pair.SetValueForKey(CFSTR("Src"), iter->first._value);
pair.SetValueForKey(CFSTR("Dst"), iter->second._value);
result.Append(pair);
}
return result;
}
void IOHIDKeyboardFilter::serialize (CFMutableDictionaryRef dict) const {
CFMutableDictionaryRefWrap serializer (dict);
serializer.SetValueForKey(CFSTR("Class"), CFSTR("IOHIDKeyboardFilter"));
serializer.SetValueForKey(CFSTR(kFnFunctionUsageMapKey),serializeMapper(_fnFunctionUsageMapKeyMap));
serializer.SetValueForKey(CFSTR(kFnKeyboardUsageMapKey), serializeMapper(_fnKeyboardUsageMapKeyMap));
serializer.SetValueForKey(CFSTR(kIOHIDServiceModifierMappingPairsKey), serializeMapper(_modifiersKeyMap));
serializer.SetValueForKey(CFSTR(kNumLockKeyboardUsageMapKey), serializeMapper(_numLockKeyboardUsageMapKeyMap));
serializer.SetValueForKey(CFSTR(kIOHIDUserKeyUsageMapKey), serializeMapper(_userKeyMap));
}
void IOHIDKeyboardFilter::defaultEventCallback (void * target, void * refcon, void * sender, IOHIDEventRef event, IOOptionBits options) {
HIDLogDebug("Event dropped: %@", event);
}
#if !TARGET_OS_EMBEDDED
uint32_t IOHIDKeyboardFilter::getKeyboardID () {
CFTypeRef value;
uint32_t keyboardID = kgestUSBUnknownANSIkd;
value = IOHIDServiceCopyProperty (_service, CFSTR(kIOHIDAltHandlerIdKey));
if (value) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &keyboardID);
CFRelease(value);
} else {
uint16_t productID = 0xffff;
uint16_t vendorID = 0xffff;
value = IOHIDServiceCopyProperty (_service, CFSTR(kIOHIDProductIDKey));
if (value) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt16Type, &productID);
CFRelease(value);
}
value = IOHIDServiceCopyProperty (_service, CFSTR(kIOHIDVendorIDKey));
if (value) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt16Type, &vendorID);
CFRelease(value);
}
keyboardID = getKeyboardID(productID, vendorID);
}
return keyboardID;
}
uint32_t IOHIDKeyboardFilter::getKeyboardID (uint16_t productID, uint16_t vendorID) {
uint16_t keyboardID = kgestUSBUnknownANSIkd;
if (vendorID == kIOUSBVendorIDAppleComputer)
{
switch (productID)
{
case kprodUSBCosmoANSIKbd: keyboardID = kgestUSBCosmoANSIKbd; break;
case kprodUSBCosmoISOKbd: keyboardID = kgestUSBCosmoISOKbd; break;
case kprodUSBCosmoJISKbd: keyboardID = kgestUSBCosmoJISKbd; break;
case kprodUSBAndyANSIKbd: keyboardID = kgestUSBAndyANSIKbd; break;
case kprodUSBAndyISOKbd: keyboardID = kgestUSBAndyISOKbd; break;
case kprodUSBAndyJISKbd: keyboardID = kgestUSBAndyJISKbd; break;
case kprodQ6ANSIKbd: keyboardID = kgestQ6ANSIKbd;
break;
case kprodQ6ISOKbd: keyboardID = kgestQ6ISOKbd;
break;
case kprodQ6JISKbd: keyboardID = kgestQ6JISKbd;
break;
case kprodQ30ANSIKbd: keyboardID = kgestQ30ANSIKbd;
break;
case kprodQ30ISOKbd: keyboardID = kgestQ30ISOKbd;
break;
case kprodQ30JISKbd: keyboardID = kgestQ30JISKbd;
break;
case kprodFountainANSIKbd: keyboardID = kgestFountainANSIKbd;
break;
case kprodFountainISOKbd: keyboardID = kgestFountainISOKbd;
break;
case kprodFountainJISKbd: keyboardID = kgestFountainJISKbd;
break;
case kprodSantaANSIKbd: keyboardID = kgestSantaANSIKbd;
break;
case kprodSantaISOKbd: keyboardID = kgestSantaISOKbd;
break;
case kprodSantaJISKbd: keyboardID = kgestSantaJISKbd;
break;
default:
keyboardID = kgestUSBCosmoANSIKbd;
break;
}
}
return keyboardID;
}
bool IOHIDKeyboardFilter::isModifiersPressed () {
auto iter = _activeKeys.begin();
for ( ; iter != _activeKeys.end(); ++iter) {
if (iter->first.isModifier()) {
return true;
}
}
return false;
}
#endif