IOHIDNXEventTranslatorSessionFilter.cpp [plain text]
#include <new>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDSessionFilterPlugIn.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/pwr_mgt/IOPM.h>
#include "IOHIDNXEventTranslatorServiceFilter.h"
#include "IOHIDDebug.h"
#include "IOHIDNXEventTranslatorSessionFilter.h"
#include <IOKit/hidsystem/IOHIDParameter.h>
#include "IOHIDEventTranslation.h"
#include <IOKit/IOMessage.h>
#include <sstream>
#include <iomanip>
#include <sys/time.h>
enum {
kIOHIDGlobalModifersReportToService = 0x1,
kIOHIDGlobalModifersUse = 0x2
};
#define kIOHIDNXEventTranslatorSessionFilterFactory CFUUIDGetConstantUUIDWithBytes(kCFAllocatorSystemDefault, 0xAC, 0x65, 0x8E, 0x84, 0x3D, 0xFE, 0x43, 0xDE, 0x94, 0x5D, 0x6E, 0x79, 0x19, 0xFB, 0xED, 0xE1)
extern "C" void * IOHIDNXEventTranslatorSessionFilterFactory(CFAllocatorRef allocator, CFUUIDRef typeUUID);
static boolean_t IsCompanionService(IOHIDServiceRef s1, IOHIDServiceRef s2);
static NXEventHandle openHIDSystem(void);
enum {
kPMDisplayOff = 0,
kPMDisplayDim = 1,
kPMDisplayOn = 2
};
static const int64_t one_mil = 1000*1000;
#define to_ns(ticks) ((ticks * tb_info.numer) / (tb_info.denom))
#define to_ms(ticks) (to_ns(ticks)/one_mil)
#define ns_to_absolute_time(x) x / (tb_info.numer / tb_info.denom);
static mach_timebase_info_data_t tb_info;
void *IOHIDNXEventTranslatorSessionFilterFactory(CFAllocatorRef allocator __unused, CFUUIDRef typeUUID)
{
if (CFEqual(typeUUID, kIOHIDSessionFilterPlugInTypeID)) {
void *p = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(IOHIDNXEventTranslatorSessionFilter), 0);
return new(p) IOHIDNXEventTranslatorSessionFilter(kIOHIDNXEventTranslatorSessionFilterFactory);
}
return NULL;
}
IOHIDSessionFilterPlugInInterface IOHIDNXEventTranslatorSessionFilter::sIOHIDNXEventTranslatorSessionFilterFtbl =
{
NULL,
IOHIDNXEventTranslatorSessionFilter::QueryInterface,
IOHIDNXEventTranslatorSessionFilter::AddRef,
IOHIDNXEventTranslatorSessionFilter::Release,
IOHIDNXEventTranslatorSessionFilter::filter,
NULL,
NULL,
IOHIDNXEventTranslatorSessionFilter::open,
IOHIDNXEventTranslatorSessionFilter::close,
NULL,
NULL,
IOHIDNXEventTranslatorSessionFilter::registerService,
IOHIDNXEventTranslatorSessionFilter::unregisterService,
IOHIDNXEventTranslatorSessionFilter::scheduleWithDispatchQueue,
IOHIDNXEventTranslatorSessionFilter::unscheduleFromDispatchQueue,
IOHIDNXEventTranslatorSessionFilter::getPropertyForClient,
IOHIDNXEventTranslatorSessionFilter::setPropertyForClient,
};
IOHIDNXEventTranslatorSessionFilter::IOHIDNXEventTranslatorSessionFilter(CFUUIDRef factoryID):
_sessionInterface(&sIOHIDNXEventTranslatorSessionFilterFtbl),
_factoryID( static_cast<CFUUIDRef>( CFRetain(factoryID) ) ),
_refCount(1),
_globalModifiers (0),
_hidSystem(MACH_PORT_NULL),
_translator (IOHIDPointerEventTranslatorCreate (kCFAllocatorDefault, 0)),
_powerConnect (MACH_PORT_NULL),
_powerNotifier(MACH_PORT_NULL),
_powerPort (NULL),
_powerState (0),
_powerOnThresholdEventCount (0),
_powerOnThreshold (kIOHIDPowerOnThresholdMS),
_port (NULL),
_wranglerNotifier (MACH_PORT_NULL),
_displayState (kPMDisplayOn),
_displaySleepAbortThreshold (kIOHIDDisplaySleepAbortThresholdMS),
_displayWakeAbortThreshold (kIOHIDDisplayWakeAbortThresholdMS),
_previousEventTime (0),
_declareActivityThreshold (0),
_powerStateChangeTime (0),
_displayStateChangeTime (0),
_displayLog(NULL)
{
CFPlugInAddInstanceForFactory( factoryID );
if (tb_info.denom == 0) {
mach_timebase_info(&tb_info);
}
_declareActivityThreshold = ns_to_absolute_time(kIOHIDDeclareActivityThresholdMS * NSEC_PER_MSEC);
}
IOHIDNXEventTranslatorSessionFilter::~IOHIDNXEventTranslatorSessionFilter()
{
CFPlugInRemoveInstanceForFactory( _factoryID );
CFRelease( _factoryID );
}
HRESULT IOHIDNXEventTranslatorSessionFilter::QueryInterface( void *self, REFIID iid, LPVOID *ppv )
{
return static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->QueryInterface(iid, ppv);
}
HRESULT IOHIDNXEventTranslatorSessionFilter::QueryInterface( REFIID iid, LPVOID *ppv )
{
CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes( NULL, iid );
if (CFEqual(interfaceID, kIOHIDSimpleSessionFilterPlugInInterfaceID) || CFEqual(interfaceID, kIOHIDSessionFilterPlugInInterfaceID)) {
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 IOHIDNXEventTranslatorSessionFilter::AddRef( void *self )
{
return static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->AddRef();
}
ULONG IOHIDNXEventTranslatorSessionFilter::AddRef()
{
_refCount += 1;
return _refCount;
}
ULONG IOHIDNXEventTranslatorSessionFilter::Release( void *self )
{
return static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->Release();
}
ULONG IOHIDNXEventTranslatorSessionFilter::Release()
{
_refCount -= 1;
if (_refCount == 0) {
delete this;
return 0;
}
return _refCount;
}
boolean_t IOHIDNXEventTranslatorSessionFilter::open(void * self, IOHIDSessionRef session, IOOptionBits options)
{
return static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->open(session, options);
}
boolean_t IOHIDNXEventTranslatorSessionFilter::open(IOHIDSessionRef session __unused, IOOptionBits options __unused)
{
_powerConnect = IORegisterForSystemPower (this, &_powerPort, powerNotificationCallback, &_powerNotifier);
return _keyboards && _companions && _modifiers;
}
void IOHIDNXEventTranslatorSessionFilter::close(void * self, IOHIDSessionRef session, IOOptionBits options)
{
static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->close(session, options);
}
void IOHIDNXEventTranslatorSessionFilter::close(IOHIDSessionRef session __unused, IOOptionBits options __unused)
{
if (_powerConnect) {
IOServiceClose(_powerConnect);
}
if (_hidSystem != MACH_PORT_NULL) {
IOServiceClose(_hidSystem);
_hidSystem = MACH_PORT_NULL;
}
if (_displayLog) {
CFRelease(_displayLog);
}
}
void IOHIDNXEventTranslatorSessionFilter::registerService(void * self, IOHIDServiceRef service)
{
static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->registerService(service);
}
void IOHIDNXEventTranslatorSessionFilter::registerService(IOHIDServiceRef service)
{
if (_keyboards &&
( IOHIDServiceConformsTo(service, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard) ||
IOHIDServiceConformsTo(service, kHIDPage_Consumer, kHIDUsage_Csmr_ConsumerControl))) {
_keyboards.SetValue(service);
}
if (IOHIDServiceConformsTo(service, kHIDPage_AppleVendor, kHIDUsage_AppleVendor_DFR)) {
_dfr = service;
}
if (_modifiers) {
CFNumberRefWrap modifiersMask (IOHIDServiceCopyProperty (service, CFSTR(kHIDEventTranslationModifierFlags)), true);
if (modifiersMask) {
_modifiers.SetValueForKey ((CFTypeRef)service, modifiersMask.Reference());
if (_companions) {
IOHIDServiceRef companion = getCompanionService (service);
if (companion) {
_companions.SetValueForKey ((CFTypeRef)service, (CFTypeRef)companion);
_companions.SetValueForKey ((CFTypeRef)companion, (CFTypeRef)service);
}
}
updateModifiers();
}
}
CFNumberRefWrap value (IOHIDServiceCopyProperty(service, CFSTR(kIOHIDServiceGlobalModifiersUsageKey)), true);
if (value) {
HIDLogError("kIOHIDServiceGlobalModifiersUsageKey = %d", ((int)value));
if (((SInt32)value) & kIOHIDGlobalModifersReportToService) {
_reportModifiers.SetValue(service);
}
if (((SInt32)value) & kIOHIDGlobalModifersUse) {
_updateModifiers.SetValue(service);
}
}
if (_translator &&
(IOHIDServiceConformsTo(service, kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse) ||
IOHIDServiceConformsTo(service, kHIDPage_GenericDesktop, kHIDUsage_GD_Pointer) ||
IOHIDServiceConformsTo(service, kHIDPage_AppleVendor, kHIDUsage_GD_Pointer) ||
IOHIDServiceConformsTo(service, kHIDPage_AppleVendor, kHIDUsage_AppleVendor_NXEvent))
){
IOHIDPointerEventTranslatorRegisterService (_translator, service);
}
}
void IOHIDNXEventTranslatorSessionFilter::unregisterService(void * self, IOHIDServiceRef service)
{
static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->unregisterService(service);
}
void IOHIDNXEventTranslatorSessionFilter::unregisterService(IOHIDServiceRef service)
{
if (_keyboards) {
_keyboards.RemoveValue(service);
}
if (service == _dfr) {
_dfr = NULL;
}
if (_updateModifiers) {
_updateModifiers.RemoveValue(service);
}
if (_reportModifiers) {
_reportModifiers.RemoveValue(service);
}
if (_companions) {
CFRefWrap<CFTypeRef> companion (_companions[service]);
if (companion) {
_companions.Remove(service);
_companions.Remove(companion);
}
}
if (_modifiers && _modifiers.ContainKey(service)) {
_modifiers.Remove(service);
updateModifiers();
}
if (_translator) {
IOHIDPointerEventTranslatorUnRegisterService (_translator, service);
}
}
void IOHIDNXEventTranslatorSessionFilter::scheduleWithDispatchQueue(void * self, dispatch_queue_t queue)
{
static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->scheduleWithDispatchQueue(queue);
}
void IOHIDNXEventTranslatorSessionFilter::scheduleWithDispatchQueue(dispatch_queue_t queue)
{
_dispatch_queue = queue;
if (_powerPort) {
IONotificationPortSetDispatchQueue (_powerPort, _dispatch_queue);
}
_port = IONotificationPortCreate(kIOMasterPortDefault);
if (_port) {
IONotificationPortSetDispatchQueue (_port, _dispatch_queue);
kern_return_t status = IOServiceAddMatchingNotification(
_port,
kIOFirstPublishNotification,
IOServiceMatching ("IODisplayWrangler"),
displayMatchNotificationCallback,
(void*) this,
&_wranglerNotifier
);
if (status) {
HIDLogError("IOServiceAddMatchingNotification with status 0x%x\n", status);
}
if (_wranglerNotifier) {
displayMatchNotificationCallback (_wranglerNotifier);
}
}
}
void IOHIDNXEventTranslatorSessionFilter::unscheduleFromDispatchQueue(void * self, dispatch_queue_t queue)
{
static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->unscheduleFromDispatchQueue(queue);
}
void IOHIDNXEventTranslatorSessionFilter::unscheduleFromDispatchQueue(dispatch_queue_t queue __unused)
{
if (_powerNotifier) {
IODeregisterForSystemPower (&_powerNotifier);
_powerNotifier = MACH_PORT_NULL;
}
if ( _powerPort ) {
IONotificationPortDestroy(_powerPort);
_powerPort = NULL;
}
if (_wranglerNotifier) {
IOObjectRelease(_wranglerNotifier);
_wranglerNotifier = MACH_PORT_NULL;
}
if ( _port ) {
IONotificationPortDestroy(_port);
_port = NULL;
}
if (_wrangler) {
IOObjectRelease(_wrangler);
_wrangler = MACH_PORT_NULL;
}
}
IOHIDEventRef IOHIDNXEventTranslatorSessionFilter::filter(void * self, IOHIDServiceRef sender, IOHIDEventRef event)
{
return static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->filter(sender, event);
}
IOHIDEventRef IOHIDNXEventTranslatorSessionFilter::filter(IOHIDServiceRef sender, IOHIDEventRef event)
{
if (!event) {
return event;
}
if (!displayStateFilter (sender, event)) {
CFRelease(event);
return NULL;
}
if (!powerStateFilter (event)) {
CFRelease(event);
return NULL;
}
if (!sender) {
return event;
}
bool doUpdateModifiers = false;
if (IOHIDEventConformsTo(event, kIOHIDEventTypeVendorDefined)) {
IOHIDEventRef ev = IOHIDEventGetEvent(event, kIOHIDEventTypeVendorDefined);
if (IOHIDEventGetIntegerValue(ev, kIOHIDEventFieldVendorDefinedUsage) == kHIDUsage_AppleVendor_NXEvent_Translated) {
NXEventExt *nxEvent = NULL;
CFIndex nxEventLength = 0;
IOHIDEventGetVendorDefinedData(ev, (uint8_t**)&nxEvent, &nxEventLength);
if (nxEvent && nxEventLength >= sizeof (NXEventExt) && nxEvent->payload.type == NX_FLAGSCHANGED) {
doUpdateModifiers = true;
}
}
}
if (IOHIDEventConformsTo(event, kIOHIDEventTypeKeyboard) && _modifiers.ContainKey(sender)) {
CFArrayRefWrap childrens (IOHIDEventGetChildren(event));
if (childrens.Reference()) {
for (CFIndex index = 0; index < childrens.Count(); index++) {
IOHIDEventRef childEvent = (IOHIDEventRef)childrens[index];
if (childEvent && IOHIDEventGetType(childEvent) == kIOHIDEventTypeVendorDefined &&
IOHIDEventGetIntegerValue(childEvent, kIOHIDEventFieldVendorDefinedUsagePage) == kHIDPage_AppleVendor &&
IOHIDEventGetIntegerValue(childEvent, kIOHIDEventFieldVendorDefinedUsage) == kHIDUsage_AppleVendor_NXEvent_Translated) {
if (_companions) {
IOHIDServiceRef companion = (IOHIDServiceRef)_companions[sender];
if (companion) {
CFNumberRefWrap companionServiceModifiers ((CFNumberRef)_modifiers[companion]);
if (companionServiceModifiers) {
IOHIDKeyboardEventTranslatorUpdateWithCompanionModifiers(childEvent, (uint32_t) companionServiceModifiers);
}
}
}
if (_updateModifiers.ContainValue(sender)) {
IOHIDKeyboardEventTranslatorUpdateWithCompanionModifiers (childEvent, _globalModifiers);
}
}
}
}
doUpdateModifiers = true;
}
if (doUpdateModifiers) {
CFNumberRefWrap currentServiceModifiers ((CFNumberRef)IOHIDServiceCopyProperty (sender, CFSTR(kHIDEventTranslationModifierFlags)), true);
CFNumberRefWrap cachedServiceModifiers ((CFNumberRef)_modifiers[sender]);
if (currentServiceModifiers && cachedServiceModifiers && currentServiceModifiers != cachedServiceModifiers) {
_modifiers.SetValueForKey(sender, currentServiceModifiers.Reference());
updateModifiers();
}
}
if (_translator &&
(IOHIDEventGetType(event) == kIOHIDEventTypePointer ||
IOHIDEventGetType(event) == kIOHIDEventTypeScroll ||
(IOHIDEventGetType(event) == kIOHIDEventTypeVendorDefined &&
IOHIDEventGetIntegerValue(event, kIOHIDEventFieldVendorDefinedUsage) == kHIDUsage_AppleVendor_NXEvent
))) {
CFArrayRef collection = IOHIDPointerEventTranslatorCreateEventCollection (_translator, event, sender, _globalModifiers, 0);
if (collection) {
for (CFIndex index = 0; index < CFArrayGetCount(collection); index++) {
IOHIDEventRef translatedEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(collection, index);
if (translatedEvent) {
IOHIDEventAppendEvent(event, translatedEvent, 0);
}
}
CFRelease(collection);
}
}
if (resetStickyKeys(event)) {
dispatch_async(_dispatch_queue, ^() {
_keyboards.Apply ([sender](const void *value) {
if (sender != (IOHIDServiceRef)value) {
IOHIDServiceSetProperty((IOHIDServiceRef)value, CFSTR(kIOHIDResetStickyKeyNotification), kCFBooleanTrue);
}
});
});
}
return event;
}
boolean_t IOHIDNXEventTranslatorSessionFilter::resetStickyKeys(IOHIDEventRef event) {
if (IOHIDEventConformsTo(event, kIOHIDEventTypeDigitizer)) {
return false;
}
if (IOHIDEventConformsTo(event, kIOHIDEventTypeButton)) {
if (_translator) {
if (!IOHIDPointerEventTranslatorGetGlobalButtonState(_translator)) {
return true;
}
}
}
if (IOHIDEventConformsTo(event, kIOHIDEventTypeVendorDefined)) {
IOHIDEventRef ev = IOHIDEventGetEvent(event, kIOHIDEventTypeVendorDefined);
if (IOHIDEventGetIntegerValue(ev, kIOHIDEventFieldVendorDefinedUsage) == kHIDUsage_AppleVendor_NXEvent) {
NXEventExt *nxEvent = NULL;
CFIndex eventLength = 0;
IOHIDEventGetVendorDefinedData(ev, (uint8_t**)&nxEvent, &eventLength);
if (nxEvent) {
if (nxEvent->payload.type == NX_LMOUSEDOWN ||
nxEvent->payload.type == NX_RMOUSEDOWN ||
nxEvent->payload.type == NX_OMOUSEDOWN) {
return true;
}
}
}
}
if (IOHIDEventConformsTo(event, kIOHIDEventTypeKeyboard)) {
IOHIDEventRef ev = IOHIDEventGetEvent(event, kIOHIDEventTypeKeyboard);
if (IOHIDEventGetIntegerValue(ev, kIOHIDEventFieldKeyboardDown)) {
return true;
}
}
return false;
}
CFTypeRef IOHIDNXEventTranslatorSessionFilter::getPropertyForClient (void * self, CFStringRef key, CFTypeRef client) {
return static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->getPropertyForClient(key,client);
}
CFTypeRef IOHIDNXEventTranslatorSessionFilter::getPropertyForClient (CFStringRef key, CFTypeRef client __unused) {
CFTypeRef result = NULL;
if (CFEqual(key, CFSTR(kIOHIDSessionFilterDebugKey))) {
CFMutableDictionaryRefWrap serializer;
serialize(serializer);
if (serializer) {
result = CFRetain(serializer.Reference());
}
}
return result;
}
void IOHIDNXEventTranslatorSessionFilter::setPropertyForClient (void * self, CFStringRef key, CFTypeRef property, CFTypeRef client) {
static_cast<IOHIDNXEventTranslatorSessionFilter *>(self)->setPropertyForClient(key, property, client);
}
void IOHIDNXEventTranslatorSessionFilter::setPropertyForClient (CFStringRef key, CFTypeRef property, CFTypeRef client __unused) {
if (CFEqual(key, CFSTR(kIOHIDActivityStateKey))) {
updateActivity(CFBooleanGetValue((CFBooleanRef)key));
} else if (CFEqual(key, CFSTR(kIOHIDDeclareActivityThreshold))) {
if (property && CFGetTypeID(property) == CFNumberGetTypeID()) {
uint32_t val;
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, &val);
_declareActivityThreshold = ns_to_absolute_time(val * NSEC_PER_MSEC);
}
}
if (_translator) {
IOHIDPointerEventTranslatorSetProperty (_translator, key, property);
}
}
void IOHIDNXEventTranslatorSessionFilter::updateModifiers() {
uint32_t globalModifiers = 0;
_modifiers.Apply ([&globalModifiers](const void *key __unused, const void *value) {
if (value ) {
globalModifiers |= (uint32_t) CFNumberRefWrap((CFTypeRef)value);
}
});
if (_globalModifiers != globalModifiers) {
HIDLogDebug("Update modifier flags: 0x%x", globalModifiers);
_globalModifiers = globalModifiers;
_reportModifiers.Apply([this, &globalModifiers](const void * value) {
IOHIDServiceSetProperty((IOHIDServiceRef)value, CFSTR(kIOHIDKeyboardGlobalModifiersKey), CFNumberRefWrap(globalModifiers));
});
}
}
IOHIDServiceRef IOHIDNXEventTranslatorSessionFilter::getCompanionService(IOHIDServiceRef service) {
IOHIDServiceRef companion = NULL;
CFIndex count = _modifiers.Count();
if (count == 0 || service == NULL) {
return companion;
}
IOHIDServiceRef *list = (IOHIDServiceRef*)malloc(count * sizeof (IOHIDServiceRef));
if (list != NULL) {
CFDictionaryGetKeysAndValues(_modifiers.Reference(), (const void **)list, NULL);
for (CFIndex index = 0; index < count; ++index) {
if (IsCompanionService(service, list[index])) {
companion = list[index];
break;
}
}
free(list);
}
return companion;
}
void IOHIDNXEventTranslatorSessionFilter::displayMatchNotificationCallback (void * refcon, io_iterator_t iterator) {
static_cast<IOHIDNXEventTranslatorSessionFilter *>(refcon)->displayMatchNotificationCallback(iterator);
}
void IOHIDNXEventTranslatorSessionFilter::displayMatchNotificationCallback (io_iterator_t iterator) {
_wrangler = IOIteratorNext (iterator);
if (_wrangler == IO_OBJECT_NULL) {
return;
}
IOObjectRelease (_wranglerNotifier);
_wranglerNotifier = NULL;
kern_return_t status = IOServiceAddInterestNotification (_port,
_wrangler,
kIOGeneralInterest,
displayNotificationCallback,
this,
&_wranglerNotifier
);
if (status) {
HIDLogError("IOServiceAddInterestNotification with status 0x%x\n", status);
}
}
void IOHIDNXEventTranslatorSessionFilter::displayNotificationCallback (void * refcon, io_service_t service, uint32_t messageType, void * messageArgument) {
static_cast<IOHIDNXEventTranslatorSessionFilter *>(refcon)->displayNotificationCallback(service, messageType, messageArgument);
}
void IOHIDNXEventTranslatorSessionFilter::displayNotificationCallback (io_service_t service __unused, uint32_t messageType, void * messageArgument) {
IOPowerStateChangeNotification *params = (IOPowerStateChangeNotification*)messageArgument;
HIDLogDebug ("displayNotificationCallback : message: 0x%x powerState: 0x%lx", messageType, params->stateNumber);
uint32_t currentDisplayState;
switch (params->stateNumber) {
case 4:
currentDisplayState = kPMDisplayOn;
break;
case 3:
currentDisplayState = kPMDisplayDim;
break;
default:
currentDisplayState = kPMDisplayOff;
}
if (_displayState != currentDisplayState) {
_displayStateChangeTime = mach_continuous_time();
HIDLogDebug ("displayNotificationCallback : displayState change 0x%x -> 0x%x", _displayState, currentDisplayState);
_displayState = currentDisplayState;
}
}
IOPMAssertionID IOHIDNXEventTranslatorSessionFilter::_AssertionID = 0;
IOHIDEventRef IOHIDNXEventTranslatorSessionFilter::displayStateFilter (IOHIDServiceRef sender, IOHIDEventRef event)
{
IOHIDEventPolicyValue policy = IOHIDEventGetPolicy(event, kIOHIDEventPowerPolicy);
IOHIDEventSenderID senderID = IOHIDEventGetSenderID(event);
IOHIDEventType eventType = IOHIDEventGetType(event);
uint64_t eventTime = IOHIDEventGetTimeStamp(event);
IOHIDEventRef result = event;
uint64_t deltaMS = to_ms(mach_continuous_time()) - to_ms(_displayStateChangeTime);
uint64_t eventDelta = eventTime - _previousEventTime;
bool declareActivity = true;
if ((_displayState < kPMDisplayDim || (_displayState > kPMDisplayDim && deltaMS < _displayWakeAbortThreshold)) &&
shouldCancelEvent(event)) {
HIDLogDebug ("displayStateFilter: Cancel event (type:%d)", IOHIDEventGetType(event));
result = NULL;
}
if (policy == kIOHIDEventPowerPolicyMaintainSystem &&
_displayState < kPMDisplayDim &&
deltaMS > _displaySleepAbortThreshold &&
sender != _dfr) {
declareActivity = false;
}
if (policy == kIOHIDEventNoPolicy) {
declareActivity = false;
}
if (policy == kIOHIDEventPowerPolicyMaintainSystem &&
eventDelta < _declareActivityThreshold) {
declareActivity = false;
} else {
_previousEventTime = eventTime;
}
if (declareActivity) {
if (_displayState < kPMDisplayDim) {
updateDisplayLog(senderID, policy, eventType, eventTime);
}
dispatch_async(dispatch_get_main_queue(), ^() {
CFStringRefWrap activityString (std::string(kIOHIDEventSystemServerName) + std::string(".queue.tickle.") +
std::to_string(senderID) + std::string(".") + std::to_string(eventType));
IOReturn status = IOPMAssertionDeclareUserActivity(activityString.Reference(),
kIOPMUserActiveLocal,
&_AssertionID);
if (status) {
HIDLogError ("IOPMAssertionDeclareUserActivity status:0x%x", status);
}
updateActivity(true);
});
}
return result;
}
boolean_t IOHIDNXEventTranslatorSessionFilter::shouldCancelEvent (IOHIDEventRef event) {
IOHIDEventRef testEvent;
testEvent = IOHIDEventGetEvent (event, kIOHIDEventTypeKeyboard);
if (testEvent && IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown)) {
return true;
}
testEvent = IOHIDEventGetEvent (event, kIOHIDEventTypePointer);
if (testEvent && IOHIDEventGetIntegerValue (event, kIOHIDEventFieldPointerButtonMask)) {
return true;
}
testEvent = IOHIDEventGetEvent (event, kIOHIDEventTypeButton);
if (testEvent && IOHIDEventGetIntegerValue (event, kIOHIDEventFieldButtonState)) {
return true;
}
return false;
}
void IOHIDNXEventTranslatorSessionFilter::updateDisplayLog (IOHIDEventSenderID serviceID, IOHIDEventPolicyValue policy, IOHIDEventType eventType, uint64_t timestamp) {
LogEntry entry;
gettimeofday(&entry.time, NULL);
entry.serviceID = serviceID;
entry.policy = policy;
entry.eventType = eventType;
entry.timestamp = timestamp;
if (_displayLog == NULL) {
_displayLog = _IOHIDSimpleQueueCreate(kCFAllocatorDefault, sizeof(LogEntry), LOG_MAX_ENTRIES);
}
if (_displayLog) {
_IOHIDSimpleQueueEnqueue(_displayLog, &entry, true);
}
}
void IOHIDNXEventTranslatorSessionFilter::updateActivity (bool active) {
if (_hidSystem == MACH_PORT_NULL) {
_hidSystem = openHIDSystem();
}
kern_return_t status = IOHIDSetStateForSelector(_hidSystem, kIOHIDActivityUserIdle, active ? 0 : 1);
if (status) {
HIDLogError ("updateActivity: IOHIDSetStateForSelector status:0x%x", status);
}
}
void IOHIDNXEventTranslatorSessionFilter::powerNotificationCallback (void * refcon, io_service_t service, uint32_t messageType, void * messageArgument) {
static_cast<IOHIDNXEventTranslatorSessionFilter *>(refcon)->powerNotificationCallback(service, messageType, messageArgument);
}
void IOHIDNXEventTranslatorSessionFilter::powerNotificationCallback (io_service_t service __unused, uint32_t messageType, void * messageArgument) {
switch (messageType) {
case kIOMessageCanSystemSleep:
case kIOMessageSystemWillSleep:
IOAllowPowerChange (_powerConnect, (long)messageArgument);
default:
_powerStateChangeTime = mach_continuous_time();
_powerState = messageType;
break;
}
HIDLogDebug ("powerNotificationCallback message: 0x%x \n", messageType);
}
IOHIDEventRef IOHIDNXEventTranslatorSessionFilter::powerStateFilter (IOHIDEventRef event) {
uint64_t deltaMS = to_ms(mach_continuous_time()) - to_ms(_powerStateChangeTime);
if (_powerState == kIOMessageSystemWillPowerOn && deltaMS < _powerOnThreshold && shouldCancelEvent (event)) {
++_powerOnThresholdEventCount;
HIDLogDebug ("powerStateFilter : Cancel event (type:%d power state:0x%x changed %lldms ago (threshold:%dms)\n",
IOHIDEventGetType(event),
_powerState,
deltaMS,
_powerOnThreshold
);
return NULL;
}
_powerOnThresholdEventCount = 0;
return event;
}
void IOHIDNXEventTranslatorSessionFilter::serialize (CFMutableDictionaryRef dict) const {
CFMutableDictionaryRefWrap serializer (dict);
serializer.SetValueForKey(CFSTR("Class"), CFSTR("IOHIDNXEventTranslatorSessionFilter"));
serializer.SetValueForKey(CFSTR("CanceledEventCount"), CFNumberRefWrap(_powerOnThresholdEventCount));
serializer.SetValueForKey(CFSTR("DisplayPowerStateChangeTime"), CFNumberRefWrap(to_ms(mach_continuous_time()) - to_ms(_displayStateChangeTime)));
serializer.SetValueForKey(CFSTR("DisplayPowerState"), CFNumberRefWrap(_displayState));
serializer.SetValueForKey(CFSTR("SystemPowerStateChangeTime"), CFNumberRefWrap(to_ms(mach_continuous_time()) - to_ms(_powerStateChangeTime)));
serializer.SetValueForKey(CFSTR("SystemPowerState"), CFNumberRefWrap(_powerState));
serializer.SetValueForKey(CFSTR("DisplayWrangelServiceObject"), CFNumberRefWrap(_wrangler));
serializer.SetValueForKey(CFSTR("DeclareActivityThreshold"), CFNumberRefWrap(_declareActivityThreshold));
CFMutableArrayRefWrap companions;
_companions.Apply ([&companions](const void *key, const void *value) {
CFMutableDictionaryRefWrap pair;
pair.SetValueForKey(CFSTR("ServiceId0"), IOHIDServiceGetRegistryID((IOHIDServiceRef)key));
pair.SetValueForKey(CFSTR("ServiceId1"), IOHIDServiceGetRegistryID((IOHIDServiceRef)value));
companions.Append(pair);
});
serializer.SetValueForKey(CFSTR("CompanionMap"), companions);
CFMutableArrayRefWrap reportModifiers;
_reportModifiers.Apply([&reportModifiers](const void * value) {
reportModifiers.Append(IOHIDServiceGetRegistryID((IOHIDServiceRef)value));
});
serializer.SetValueForKey(CFSTR("ReportGlobalModifiers"), reportModifiers);
CFMutableArrayRefWrap updateModifiers;
_updateModifiers.Apply([&updateModifiers](const void * value) {
updateModifiers.Append(IOHIDServiceGetRegistryID((IOHIDServiceRef)value));
});
serializer.SetValueForKey(CFSTR("TranslateWithGlobalModifiers"), updateModifiers);
if (_displayLog) {
CFMutableArrayRef displayLog = CFArrayCreateMutable(kCFAllocatorDefault, LOG_MAX_ENTRIES, &kCFTypeArrayCallBacks);
if (displayLog) {
_IOHIDSimpleQueueApplyBlock (_displayLog, ^(void * entry, void * ctx) {
CFMutableArrayRef log = (CFMutableArrayRef)ctx;
LogEntry * entryData = (LogEntry *)entry;
CFMutableDictionaryRef entryDict = CFDictionaryCreateMutable(CFGetAllocator(log), 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!entryDict) {
return;
}
_IOHIDDictionaryAddSInt64(entryDict, CFSTR("ServiceID"), entryData->serviceID);
_IOHIDDictionaryAddSInt64(entryDict, CFSTR("Policy"), entryData->policy);
_IOHIDDictionaryAddSInt32(entryDict, CFSTR("EventType"), entryData->eventType);
_IOHIDDictionaryAddSInt64(entryDict, CFSTR("timestamp"), entryData->timestamp);
CFStringRef time = _IOHIDCreateTimeString(kCFAllocatorDefault, &entryData->time);
if (time) {
CFDictionaryAddValue(entryDict, CFSTR("Time"), time);
CFRelease(time);
}
CFArrayAppendValue(log, entryDict);
CFRelease(entryDict);
}, (void *)displayLog);
serializer.SetValueForKey(CFSTR("DisplayWakeLog"), displayLog);
CFRelease(displayLog);
}
}
}
static const CFStringRef propertyTable [] = {
CFSTR(kIOHIDVendorIDKey),
CFSTR(kIOHIDProductIDKey),
CFSTR(kIOHIDLocationIDKey),
CFSTR(kIOHIDTransportKey)
};
static boolean_t IsAltSender(IOHIDServiceRef s1, IOHIDServiceRef s2) {
CFTypeRef altSenderID = IOHIDServiceGetProperty (s1, CFSTR(kIOHIDAltSenderIdKey));
if (altSenderID) {
CFTypeRef registryID = IOHIDServiceGetRegistryID(s2);
if (altSenderID && registryID && CFEqual(altSenderID, registryID)) {
return true;
}
}
return false;
}
static boolean_t IsCompanionService(IOHIDServiceRef s1, IOHIDServiceRef s2) {
CFTypeRef s1Property;
CFTypeRef s2Property;
if (CFEqual(IOHIDServiceGetRegistryID(s1), IOHIDServiceGetRegistryID(s2))) {
return false;
}
if (IsAltSender(s1,s2) || IsAltSender(s2,s1)) {
return true;
}
for (size_t index = 0; index < sizeof(propertyTable)/sizeof(propertyTable[0]); ++index) {
s1Property = IOHIDServiceGetProperty(s1, propertyTable[index]);
s2Property = IOHIDServiceGetProperty(s2, propertyTable[index]);
if (s1Property == NULL || s2Property == NULL || !CFEqual(s1Property, s2Property)) {
return false;
}
}
return true;
}
static NXEventHandle openHIDSystem(void)
{
kern_return_t kr;
io_service_t service = MACH_PORT_NULL;
NXEventHandle handle = MACH_PORT_NULL;
mach_port_t masterPort;
kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
if(kr == KERN_SUCCESS) {
service = IORegistryEntryFromPath(masterPort, kIOServicePlane ":/IOResources/IOHIDSystem");
if (service) {
IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &handle);
IOObjectRelease(service);
}
}
return handle;
}