IOHIDAccessibilityFilter.cpp [plain text]
#include "IOHIDAccessibilityFilter.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/hid/IOHIDServiceKeys.h>
#include <notify.h>
#include <pthread.h>
#include <asl.h>
#include <fcntl.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <syslog.h>
#define DEBUG_LOGGING 0
#if DEBUG_LOGGING
#define DEBUG_LOG(fmt, ...) syslog(LOG_ERR, "IOHIDAccessibilityFilter: " #fmt "\n", ## __VA_ARGS__)
#else
#define DEBUG_LOG(...)
#endif
#define kIOHIDAccessibilityFilterFactory CFUUIDGetConstantUUIDWithBytes(kCFAllocatorSystemDefault, 0x55, 0x12, 0x66, 0x8E, 0xFF, 0x47, 0x4E, 0x70, 0xB3, 0x3E, 0xE1, 0xFF, 0xFA, 0xEF, 0x01, 0xA8)
extern "C" void * IOHIDAccessibilityFilterFactory(CFAllocatorRef allocator, CFUUIDRef typeUUID);
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);
void *IOHIDAccessibilityFilterFactory(CFAllocatorRef allocator __unused, CFUUIDRef typeUUID)
{
if (CFEqual(typeUUID, kIOHIDServiceFilterPlugInTypeID)) {
void *p = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(IOHIDAccessibilityFilter), 0);
return new(p) IOHIDAccessibilityFilter(kIOHIDAccessibilityFilterFactory);
}
return NULL;
}
IOHIDServiceFilterPlugInInterface IOHIDAccessibilityFilter::sIOHIDAccessibilityFilterFtbl =
{
NULL,
IOHIDAccessibilityFilter::QueryInterface,
IOHIDAccessibilityFilter::AddRef,
IOHIDAccessibilityFilter::Release,
IOHIDAccessibilityFilter::match,
IOHIDAccessibilityFilter::filter,
NULL,
IOHIDAccessibilityFilter::open,
IOHIDAccessibilityFilter::close,
IOHIDAccessibilityFilter::scheduleWithDispatchQueue,
IOHIDAccessibilityFilter::unscheduleFromDispatchQueue,
IOHIDAccessibilityFilter::copyPropertyForClient,
IOHIDAccessibilityFilter::setPropertyForClient,
NULL,
IOHIDAccessibilityFilter::setEventCallback,
};
IOHIDAccessibilityFilter::IOHIDAccessibilityFilter(CFUUIDRef factoryID)
:
_serviceInterface(&sIOHIDAccessibilityFilterFtbl),
_factoryID( static_cast<CFUUIDRef>( CFRetain(factoryID) ) ),
_refCount(1),
_stickyKeysFeatureEnabled(false),
_stickyKeysShiftKeyToggles(false),
_stickyKeysShiftKeyCount(0),
_stickyKeysOn(false),
_stickyKeysShiftResetTimer(0),
_eventCallback(0),
_eventTarget(0),
_eventContext(0),
_queue(0),
_slowKeysDelay(0),
_slowKeysTimer(0),
_slowKeysInProgress(0),
_slowKeysCurrentUsage(0),
_slowKeysCurrentUsagePage(0),
_slowKeysSlowEvent(0)
{
CFPlugInAddInstanceForFactory( factoryID );
for (int i = 0; i < MAX_STICKY_KEYS; i++)
_stickyKeyState[i] = kStickyKeyState_Reset;
}
IOHIDAccessibilityFilter::~IOHIDAccessibilityFilter()
{
CFPlugInRemoveInstanceForFactory( _factoryID );
CFRelease( _factoryID );
}
HRESULT IOHIDAccessibilityFilter::QueryInterface( void *self, REFIID iid, LPVOID *ppv )
{
return static_cast<IOHIDAccessibilityFilter *>(self)->QueryInterface(iid, ppv);
}
HRESULT IOHIDAccessibilityFilter::QueryInterface( REFIID iid, LPVOID *ppv )
{
CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes( NULL, iid );
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
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 IOHIDAccessibilityFilter::AddRef( void *self )
{
return static_cast<IOHIDAccessibilityFilter *>(self)->AddRef();
}
ULONG IOHIDAccessibilityFilter::AddRef()
{
_refCount += 1;
return _refCount;
}
ULONG IOHIDAccessibilityFilter::Release( void *self )
{
return static_cast<IOHIDAccessibilityFilter *>(self)->Release();
}
ULONG IOHIDAccessibilityFilter::Release()
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
_refCount -= 1;
if (_refCount == 0) {
delete this;
return 0;
}
return _refCount;
}
void IOHIDAccessibilityFilter::open(void * self, IOHIDServiceRef service, IOOptionBits options)
{
static_cast<IOHIDAccessibilityFilter *>(self)->open(service, options);
}
void IOHIDAccessibilityFilter::open(IOHIDServiceRef service, IOOptionBits options)
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
(void)service;
(void)options;
}
void IOHIDAccessibilityFilter::close(void * self, IOHIDServiceRef service, IOOptionBits options)
{
static_cast<IOHIDAccessibilityFilter *>(self)->close(service, options);
}
void IOHIDAccessibilityFilter::close(IOHIDServiceRef service, IOOptionBits options)
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
(void) service;
(void) options;
}
void IOHIDAccessibilityFilter::scheduleWithDispatchQueue(void * self, dispatch_queue_t queue)
{
static_cast<IOHIDAccessibilityFilter *>(self)->scheduleWithDispatchQueue(queue);
}
void IOHIDAccessibilityFilter::scheduleWithDispatchQueue(dispatch_queue_t queue)
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
_queue = queue;
_stickyKeysShiftResetTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
dispatch_set_context(_stickyKeysShiftResetTimer, this);
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);
dispatch_set_context(_slowKeysTimer, this);
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);
}
void IOHIDAccessibilityFilter::unscheduleFromDispatchQueue(void * self, dispatch_queue_t queue)
{
static_cast<IOHIDAccessibilityFilter *>(self)->unscheduleFromDispatchQueue(queue);
}
void IOHIDAccessibilityFilter::unscheduleFromDispatchQueue(dispatch_queue_t queue)
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
if (_stickyKeysOn)
dispatchStickyKeys(kStickyKeyState_Down | kStickyKeyState_Locked);
if (_slowKeysDelay)
dispatchSlowKey();
_queue = NULL;
if (_stickyKeysShiftResetTimer) {
dispatch_source_cancel(_stickyKeysShiftResetTimer);
_stickyKeysShiftResetTimer = NULL;
}
if (_slowKeysTimer) {
dispatch_source_cancel(_slowKeysTimer);
_slowKeysTimer = NULL;
}
}
void IOHIDAccessibilityFilter::setEventCallback(void * self, IOHIDServiceEventCallback callback, void * target, void * refcon)
{
static_cast<IOHIDAccessibilityFilter *>(self)->setEventCallback(callback, target, refcon);
}
void IOHIDAccessibilityFilter::setEventCallback(IOHIDServiceEventCallback callback, void * target, void * refcon)
{
DEBUG_LOG("%s callback = %p target = %p refcon = %p", __PRETTY_FUNCTION__, callback, target, refcon);
_eventCallback = callback;
_eventTarget = target;
_eventContext = refcon;
}
CFTypeRef IOHIDAccessibilityFilter::copyPropertyForClient(void * self, CFStringRef key, CFTypeRef client)
{
return static_cast<IOHIDAccessibilityFilter *>(self)->copyPropertyForClient(key, client);
}
CFTypeRef IOHIDAccessibilityFilter::copyPropertyForClient(CFStringRef key, CFTypeRef client __unused)
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
CFTypeRef result = NULL;
if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysDisabledKey), kNilOptions) == kCFCompareEqualTo) {
result = _stickyKeysFeatureEnabled ? kCFBooleanTrue : kCFBooleanFalse;
}
else if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysOnKey), kNilOptions) == kCFCompareEqualTo) {
result = _stickyKeysOn ? kCFBooleanTrue : kCFBooleanFalse;
}
else if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysShiftTogglesKey), kNilOptions) == kCFCompareEqualTo) {
result = _stickyKeysShiftKeyToggles ? kCFBooleanTrue : kCFBooleanFalse;
}
else if (CFStringCompare(key, CFSTR(kIOHIDServiceSlowKeysDelayKey), kNilOptions) == kCFCompareEqualTo) {
result = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &_slowKeysDelay);
}
return result;
}
void IOHIDAccessibilityFilter::setPropertyForClient(void * self,CFStringRef key,CFTypeRef property,CFTypeRef client)
{
static_cast<IOHIDAccessibilityFilter *>(self)->setPropertyForClient(key, property, client);
}
void IOHIDAccessibilityFilter::setPropertyForClient(CFStringRef key,CFTypeRef property,CFTypeRef client __unused)
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
CFBooleanRef boolProp = (CFBooleanRef)property;
if (!key)
goto exit;
if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysDisabledKey), kNilOptions) == kCFCompareEqualTo) {
_stickyKeysFeatureEnabled = !(boolProp == kCFBooleanTrue);
if (!_stickyKeysFeatureEnabled) {
_stickyKeysOn = false;
dispatchStickyKeys(kStickyKeyState_Down | kStickyKeyState_Locked);
}
DEBUG_LOG("_stickyKeysFeatureEnabled = %d", _stickyKeysFeatureEnabled);
}
else if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysOnKey), kNilOptions) == kCFCompareEqualTo) {
_stickyKeysOn = (boolProp == kCFBooleanTrue);
if (!_stickyKeysOn)
dispatchStickyKeys(kStickyKeyState_Down | kStickyKeyState_Locked);
DEBUG_LOG("_stickyKeysOn = %d\n", _stickyKeysOn);
}
else if (CFStringCompare(key, CFSTR(kIOHIDServiceStickyKeysShiftTogglesKey), kNilOptions) == kCFCompareEqualTo) {
_stickyKeysShiftKeyToggles = (boolProp == kCFBooleanTrue);
if (!_stickyKeysShiftKeyToggles)
_stickyKeysShiftKeyCount = 0;
DEBUG_LOG("_stickyKeysShiftKeyToggles = %d\n", _stickyKeysShiftKeyToggles);
}
else if (CFStringCompare(key, CFSTR(kIOHIDServiceSlowKeysDelayKey), kNilOptions) == kCFCompareEqualTo) {
CFNumberRef numProp = (CFNumberRef)property;
CFNumberGetValue(numProp, kCFNumberSInt32Type, &_slowKeysDelay);
DEBUG_LOG("_slowKeysDelay = %d\n", (int)_slowKeysDelay);
}
exit:
return;
}
SInt32 IOHIDAccessibilityFilter::match(void * self, IOHIDServiceRef service, IOOptionBits options)
{
return static_cast<IOHIDAccessibilityFilter *>(self)->match(service, options);
}
SInt32 IOHIDAccessibilityFilter::match(IOHIDServiceRef service, IOOptionBits options)
{
SInt32 score = 0;
(void) options;
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
score += IOHIDServiceConformsTo(service, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
score += IOHIDServiceConformsTo(service, kHIDPage_Consumer, kHIDUsage_Csmr_ConsumerControl);
return score;
}
IOHIDEventRef IOHIDAccessibilityFilter::filter(void * self, IOHIDEventRef event)
{
return static_cast<IOHIDAccessibilityFilter *>(self)->filter(event);
}
IOHIDEventRef IOHIDAccessibilityFilter::filter(IOHIDEventRef event)
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
do
{
if (!event)
break;
if (IOHIDEventGetType(event) != kIOHIDEventTypeKeyboard)
break;
if (_slowKeysDelay)
event = processSlowKeys(event);
if (_stickyKeysFeatureEnabled)
event = processStickyKeys(event);
} while (0);
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 isShiftKey(UInt32 usagePage, UInt32 usage)
{
if (usagePage == kHIDPage_KeyboardOrKeypad)
return ((usage == kHIDUsage_KeyboardLeftShift) || (usage == kHIDUsage_KeyboardRightShift));
else
return false;
}
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;
}
}
StickyKeyState IOHIDAccessibilityFilter::getStickyKeyState(UInt32 usagePage, UInt32 usage)
{
return _stickyKeyState[getModifierIndex(usagePage, usage)];
}
void IOHIDAccessibilityFilter::setStickyKeyState(UInt32 usagePage, UInt32 usage, StickyKeyState state)
{
_stickyKeyState[getModifierIndex(usagePage, usage)] = state;
}
bool IOHIDAccessibilityFilter::processStickyKeyDown(UInt32 usagePage, UInt32 usage, UInt32& flags)
{
StickyKeyState state = getStickyKeyState(usagePage, usage);
bool needsDispatch = false;
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
switch (state) {
case kStickyKeyState_Reset:
needsDispatch = true;
flags = kIOHIDKeyboardStickyKeyDown;
setStickyKeyState(usagePage, usage, kStickyKeyState_Down);
break;
case kStickyKeyState_Down:
needsDispatch = true;
flags = kIOHIDKeyboardStickyKeyLocked;
setStickyKeyState(usagePage, usage, kStickyKeyState_Locked);
break;
case kStickyKeyState_Locked:
needsDispatch = false;
setStickyKeyState(usagePage, usage, kStickyKeyState_Reset);
break;
default:
syslog(LOG_ERR, "IOHIDAccessibilityFilter: Sticky key down in bad state for usage %d\n", (int)usage);
}
return needsDispatch;
}
bool IOHIDAccessibilityFilter::processStickyKeyUp(UInt32 usagePage, UInt32 usage, UInt32& flags)
{
StickyKeyState state = getStickyKeyState(usagePage, usage);
bool needsDispatch = false;
DEBUG_LOG("%s usage = %d state = %d", __PRETTY_FUNCTION__, (int)usage, (int)state);
switch (state) {
case kStickyKeyState_Reset:
needsDispatch = true;
flags = kIOHIDKeyboardStickyKeyUp;
break;
case kStickyKeyState_Down:
break;
case kStickyKeyState_Locked:
break;
default:
syslog(LOG_ERR, "Sticky key up in bad state for usage %d\n", (int)usage);
}
return needsDispatch;
}
void IOHIDAccessibilityFilter::dispatchStickyKeys(int stateMask)
{
int i = 0;
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
for (;i < MAX_STICKY_KEYS; i++) {
UInt32 usage = 0;
UInt32 usagePage = 0;
getUsageForIndex(i, usagePage, usage);
DEBUG_LOG("index = %d, usage = %d", (int)i, (int)usage);
if ((getStickyKeyState(usagePage, usage) & stateMask) == 0)
continue;
IOHIDEventRef event = IOHIDEventCreate(kCFAllocatorDefault, kIOHIDEventTypeKeyboard, mach_absolute_time(), 0);
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage, kHIDPage_KeyboardOrKeypad);
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardUsage, usage);
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardDown, 0);
IOHIDEventSetEventFlags(event, IOHIDEventGetEventFlags(event) | kIOHIDKeyboardStickyKeyUp);
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, event, 0);
DEBUG_LOG("dispatched usage %d", (int)usage);
setStickyKeyState(usagePage, usage, kStickyKeyState_Reset);
}
}
void IOHIDAccessibilityFilter::processShiftKey(void)
{
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
if (_stickyKeysShiftKeyToggles == 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;
_stickyKeysOn = !_stickyKeysOn;
}
if (!_stickyKeysOn)
dispatch_async(_queue, ^{dispatchStickyKeys(kStickyKeyState_Down | kStickyKeyState_Locked);});
}
IOHIDEventRef IOHIDAccessibilityFilter::processStickyKeys(IOHIDEventRef event)
{
UInt32 usage = 0;
UInt32 usagePage = 0;
UInt32 flags = 0;
UInt32 oldFlags = 0;
bool keyDown = 0;
bool needsDispatch = 0;
bool stickyKeysOldState = _stickyKeysOn;
bool stickyKeysToggled = false;
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
if (!event)
goto exit;
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
oldFlags = (UInt32)IOHIDEventGetEventFlags(event);
if ((oldFlags & kIOHIDKeyboardStickyKeyUp) != 0)
goto exit;
if (keyDown && isShiftKey(usagePage, usage))
processShiftKey();
stickyKeysToggled = (stickyKeysOldState != _stickyKeysOn);
if (!_stickyKeysOn) {
if (stickyKeysToggled)
IOHIDEventSetEventFlags(event, IOHIDEventGetEventFlags(event) | kIOHIDKeyboardStickyKeysOff);
goto exit;
}
if (!isModifier(usagePage, usage)) {
if (_stickyKeysShiftKeyCount)
dispatch_source_set_timer(_stickyKeysShiftResetTimer, DISPATCH_TIME_FOREVER, 0, 0);
if (!keyDown){
dispatch_async(_queue, ^{
dispatchStickyKeys(kStickyKeyState_Down);
});
_stickyKeysShiftKeyCount = 0;
}
}
else {
if (stickyKeysToggled) {
IOHIDEventSetEventFlags(event, IOHIDEventGetEventFlags(event) | kIOHIDKeyboardStickyKeysOn);
goto exit;
}
if (keyDown)
needsDispatch = processStickyKeyDown(usagePage, usage, flags);
else
needsDispatch = processStickyKeyUp(usagePage, usage, flags);
if (!needsDispatch) {
event = NULL;
}
else {
usage = makeModifierLeftHanded(usage);
IOHIDEventSetIntegerValue(event, kIOHIDEventFieldKeyboardUsage, usage);
IOHIDEventSetEventFlags(event, IOHIDEventGetEventFlags(event) | flags);
}
}
exit:
return event;
}
void IOHIDAccessibilityFilter::resetSlowKey(void)
{
_slowKeysCurrentUsage = 0;
_slowKeysCurrentUsagePage = 0;
_slowKeysInProgress = false;
if (_slowKeysSlowEvent) {
CFRelease(_slowKeysSlowEvent);
_slowKeysSlowEvent = NULL;
}
}
void IOHIDAccessibilityFilter::dispatchSlowKey(void)
{
IOHIDEventRef event = _slowKeysSlowEvent;
DEBUG_LOG("%s", __PRETTY_FUNCTION__);
if (!event)
return;
IOHIDEventSetEventFlags(event, IOHIDEventGetEventFlags(event) | kIOHIDKeyboardSlowKey);
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, event, 0);
resetSlowKey();
}
IOHIDEventRef IOHIDAccessibilityFilter::processSlowKeys(IOHIDEventRef event)
{
UInt32 usage = 0;
UInt32 usagePage = 0;
bool keyDown = 0;
DEBUG_LOG("%s event = %p flags = 0x%x", __PRETTY_FUNCTION__, event, event ? IOHIDEventGetEventFlags(event) : 0);
if (!event)
goto exit;
if ((IOHIDEventGetEventFlags(event) & kIOHIDKeyboardSlowKey) != 0)
goto exit;
usage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage);
usagePage = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsagePage);
keyDown = (UInt32)IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown);
DEBUG_LOG("keyDown = %d usage = %d page = %d _slowKeysInProgress = %d",
(int)keyDown, (int)usage, (int)usagePage, (int)_slowKeysInProgress);
if (keyDown) {
dispatch_source_set_timer(_slowKeysTimer,
dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * _slowKeysDelay),
DISPATCH_TIME_FOREVER, 0);
_slowKeysCurrentUsage = usage;
_slowKeysCurrentUsagePage = usagePage;
_slowKeysSlowEvent = event;
_slowKeysInProgress = true;
CFRetain(event);
event = NULL;
}
else {
if (_slowKeysInProgress) {
if ((_slowKeysCurrentUsage == usage) && (_slowKeysCurrentUsagePage == usagePage)) {
dispatch_source_set_timer(_slowKeysTimer, DISPATCH_TIME_FOREVER, 0, 0);
resetSlowKey();
event = NULL;
}
}
}
exit:
return event;
}