IOHIDNXEventTranslatorServiceFilter.cpp [plain text]
#include "IOHIDNXEventTranslatorServiceFilter.h"
#include <new>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFLogUtilities.h>
#include <IOKit/hid/IOHIDServiceFilterPlugIn.h>
#include <IOKit/hid/IOHIDEventSystemPrivate.h>
#include <IOKit/hid/IOHIDEventTypes.h>
#include <IOKit/hid/IOHIDSession.h>
#include <IOKit/hid/IOHIDServiceClient.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 "IOHIDParameter.h"
#include "IOHIDEventData.h"
#include <notify.h>
#include <pthread.h>
#include <asl.h>
#include <fcntl.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include "IOHIDDebug.h"
#include "CF.h"
#include "IOHIDevicePrivateKeys.h"
#define kIOHIDNXEventTranslatorServiceFilterFactory CFUUIDGetConstantUUIDWithBytes(kCFAllocatorSystemDefault, 0x84, 0x10, 0xC8, 0xC6, 0xD0, 0x24, 0x47, 0x08, 0xAF, 0x6E, 0x7C, 0xC3, 0xD1, 0xFD, 0x7E, 0x12)
extern "C" void * IOHIDNXEventTranslatorServiceFilterFactory(CFAllocatorRef allocator, CFUUIDRef typeUUID);
void *IOHIDNXEventTranslatorServiceFilterFactory(CFAllocatorRef allocator __unused, CFUUIDRef typeUUID)
{
if (CFEqual(typeUUID, kIOHIDServiceFilterPlugInTypeID)) {
void *p = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(IOHIDNXEventTranslatorServiceFilter), 0);
return new(p) IOHIDNXEventTranslatorServiceFilter(kIOHIDNXEventTranslatorServiceFilterFactory);
}
return NULL;
}
IOHIDServiceFilterPlugInInterface IOHIDNXEventTranslatorServiceFilter::sIOHIDNXEventTranslatorServiceFilterFtbl =
{
NULL,
IOHIDNXEventTranslatorServiceFilter::QueryInterface,
IOHIDNXEventTranslatorServiceFilter::AddRef,
IOHIDNXEventTranslatorServiceFilter::Release,
IOHIDNXEventTranslatorServiceFilter::match,
IOHIDNXEventTranslatorServiceFilter::filter,
NULL,
IOHIDNXEventTranslatorServiceFilter::open,
IOHIDNXEventTranslatorServiceFilter::close,
IOHIDNXEventTranslatorServiceFilter::scheduleWithDispatchQueue,
IOHIDNXEventTranslatorServiceFilter::unscheduleFromDispatchQueue,
IOHIDNXEventTranslatorServiceFilter::copyPropertyForClient,
IOHIDNXEventTranslatorServiceFilter::setPropertyForClient,
NULL,
IOHIDNXEventTranslatorServiceFilter::setEventCallback,
};
IOHIDNXEventTranslatorServiceFilter::IOHIDNXEventTranslatorServiceFilter(CFUUIDRef factoryID):
_serviceInterface(&sIOHIDNXEventTranslatorServiceFilterFtbl),
_factoryID(NULL),
_refCount(1),
_matchScore(0),
_eventCallback(NULL),
_eventTarget(NULL),
_eventContext(NULL),
_queue(NULL),
_service(NULL),
_translator(NULL),
_isTranslationEnabled(true)
{
HIDLogDebug("(%p)", this);
if (factoryID) {
_factoryID = static_cast<CFUUIDRef>(CFRetain(factoryID));
CFPlugInAddInstanceForFactory( factoryID );
}
}
IOHIDNXEventTranslatorServiceFilter::~IOHIDNXEventTranslatorServiceFilter()
{
if (_factoryID) {
CFPlugInRemoveInstanceForFactory( _factoryID );
CFRelease( _factoryID );
}
}
HRESULT IOHIDNXEventTranslatorServiceFilter::QueryInterface( void *self, REFIID iid, LPVOID *ppv )
{
return static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->QueryInterface(iid, ppv);
}
HRESULT IOHIDNXEventTranslatorServiceFilter::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 IOHIDNXEventTranslatorServiceFilter::AddRef( void *self )
{
return static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->AddRef();
}
ULONG IOHIDNXEventTranslatorServiceFilter::AddRef()
{
_refCount += 1;
return _refCount;
}
ULONG IOHIDNXEventTranslatorServiceFilter::Release( void *self )
{
return static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->Release();
}
ULONG IOHIDNXEventTranslatorServiceFilter::Release()
{
_refCount -= 1;
if (_refCount == 0) {
delete this;
return 0;
}
return _refCount;
}
void IOHIDNXEventTranslatorServiceFilter::open(void * self, IOHIDServiceRef service, IOOptionBits options)
{
static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->open(service, options);
}
void IOHIDNXEventTranslatorServiceFilter::open(IOHIDServiceRef service, IOOptionBits options)
{
HIDLogDebug("(%p)", this);
(void)options;
_service = service;
_translator = IOHIDKeyboardEventTranslatorCreateWithService (CFGetAllocator(_service), _service);
}
void IOHIDNXEventTranslatorServiceFilter::close(void * self, IOHIDServiceRef service, IOOptionBits options)
{
static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->close(service, options);
}
void IOHIDNXEventTranslatorServiceFilter::close(IOHIDServiceRef service, IOOptionBits options)
{
HIDLogDebug("(%p)", this);
(void) options;
(void) service;
_service = NULL;
if (_translator) {
CFRelease(_translator);
_translator = NULL;
}
}
void IOHIDNXEventTranslatorServiceFilter::scheduleWithDispatchQueue(void * self, dispatch_queue_t queue)
{
static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->scheduleWithDispatchQueue(queue);
}
void IOHIDNXEventTranslatorServiceFilter::scheduleWithDispatchQueue(dispatch_queue_t queue)
{
_queue = queue;
}
void IOHIDNXEventTranslatorServiceFilter::unscheduleFromDispatchQueue(void * self, dispatch_queue_t queue)
{
static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->unscheduleFromDispatchQueue(queue);
}
void IOHIDNXEventTranslatorServiceFilter::unscheduleFromDispatchQueue(dispatch_queue_t queue __unused)
{
_queue = NULL;
}
void IOHIDNXEventTranslatorServiceFilter::setEventCallback(void * self, IOHIDServiceEventCallback callback, void * target, void * refcon)
{
static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->setEventCallback(callback, target, refcon);
}
void IOHIDNXEventTranslatorServiceFilter::setEventCallback(IOHIDServiceEventCallback callback, void * target, void * refcon)
{
_eventCallback = callback;
_eventTarget = target;
_eventContext = refcon;
}
CFTypeRef IOHIDNXEventTranslatorServiceFilter::copyPropertyForClient(void * self, CFStringRef key, CFTypeRef client)
{
return static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->copyPropertyForClient(key, client);
}
CFTypeRef IOHIDNXEventTranslatorServiceFilter::copyPropertyForClient(CFStringRef key, CFTypeRef client __unused)
{
CFTypeRef result = NULL;
if (CFEqual (key, CFSTR(kHIDEventTranslationModifierFlags))) {
uint32_t serviceModifiers = IOHIDKeyboardEventTranslatorGetModifierFlags (_translator);
result = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &serviceModifiers);
} else if (CFEqual(key, CFSTR(kIOHIDServiceFilterDebugKey))) {
CFMutableDictionaryRefWrap serializer;
if (serializer) {
serialize(serializer);
result = CFRetain(serializer.Reference());
}
} else if (CFEqual(key, CFSTR(kIOHIDNXEventTranslation))) {
result = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt8Type, &_isTranslationEnabled);
}
return result;
}
void IOHIDNXEventTranslatorServiceFilter::setPropertyForClient(void * self,CFStringRef key,CFTypeRef property,CFTypeRef client)
{
static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->setPropertyForClient(key, property, client);
}
void IOHIDNXEventTranslatorServiceFilter::setPropertyForClient(CFStringRef key,CFTypeRef property __unused, CFTypeRef client __unused)
{
if (CFEqual(key, CFSTR(kIOHIDServiceCapsLockStateKey))) {
if (_queue && _translator) {
__block IOHIDServiceRef service = (IOHIDServiceRef)CFRetain(_service);
dispatch_async(_queue, ^(){
CFBooleanRefWrap capsLockState ((CFBooleanRef)IOHIDServiceCopyProperty(service, CFSTR(kIOHIDServiceCapsLockStateKey)), true);
uint32_t translationFlags = capsLockState ? kTranslationFlagCapsLockOn : 0;
if ((IOHIDKeyboardEventTranslatorGetModifierFlags (_translator) & NX_ALPHASHIFTMASK) != (capsLockState ? NX_ALPHASHIFTMASK : 0)) {
CFRefWrap <IOHIDEventRef> dummyEvent (IOHIDEventCreate(kCFAllocatorDefault, kIOHIDEventTypeKeyboard, 0, 0), true);
CFArrayRef collection = IOHIDKeyboardEventTranslatorCreateEventCollection (_translator, dummyEvent, translationFlags);
if (collection) {
for (CFIndex index = 0; index < CFArrayGetCount(collection); index++) {
IOHIDEventRef translatedEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(collection, index);
if (translatedEvent && (_isTranslationEnabled || IOHIDEventGetIntegerValue (translatedEvent, kIOHIDEventFieldVendorDefinedUsage) != kHIDUsage_AppleVendor_NXEvent_Translated)) {
_eventCallback(_eventTarget, _eventContext, &_serviceInterface, translatedEvent, 0);
}
}
CFRelease(collection);
}
}
CFRelease(service);
});
}
} else if (CFEqual(key,CFSTR(kIOHIDNXEventTranslation))) {
if (CFNumberGetTypeID() == CFGetTypeID(property)) {
uint8_t tmp = 0;
CFNumberGetValue((CFNumberRef)property, kCFNumberCharType, &tmp);
_isTranslationEnabled = tmp ? true : false;
}
}
if (_translator) {
IOHIDKeyboardEventTranslatorSetProperty (_translator, key, property);
}
return;
}
SInt32 IOHIDNXEventTranslatorServiceFilter::match(void * self, IOHIDServiceRef service, IOOptionBits options)
{
return static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->match(service, options);
}
SInt32 IOHIDNXEventTranslatorServiceFilter::match(IOHIDServiceRef service, IOOptionBits options)
{
(void) options;
_matchScore = (IOHIDServiceConformsTo(service, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard) ||
IOHIDServiceConformsTo(service, kHIDPage_Consumer, kHIDUsage_Csmr_ConsumerControl)) ? 100 : 0;
HIDLogDebug("(%p) for ServiceID %@ with score %d", this, IOHIDServiceGetRegistryID(service), (int)_matchScore);
return _matchScore;
}
IOHIDEventRef IOHIDNXEventTranslatorServiceFilter::filter(void * self, IOHIDEventRef event)
{
return static_cast<IOHIDNXEventTranslatorServiceFilter *>(self)->filter(event);
}
IOHIDEventRef IOHIDNXEventTranslatorServiceFilter::filter(IOHIDEventRef event)
{
if (!event || !_translator || _isTranslationEnabled == false) {
return event;
}
CFArrayRef collection;
if (IOHIDEventConformsTo (event, kIOHIDEventTypeKeyboard)) {
CFBooleanRefWrap capsLockState ((CFBooleanRef)IOHIDServiceCopyProperty(_service, CFSTR(kIOHIDServiceCapsLockStateKey)), true);
uint32_t translationFlags = capsLockState ? kTranslationFlagCapsLockOn : 0;
collection = IOHIDKeyboardEventTranslatorCreateEventCollection (_translator, event, translationFlags);
if (collection) {
for (CFIndex index = 0; index < CFArrayGetCount(collection); index++) {
IOHIDEventRef translatedEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(collection, index);
if (translatedEvent) {
IOHIDEventAppendEvent(event, translatedEvent, 0);
}
}
CFRelease(collection);
}
}
return event;
}
void IOHIDNXEventTranslatorServiceFilter::serialize (CFMutableDictionaryRef dict) const {
CFMutableDictionaryRefWrap serializer (dict);
serializer.SetValueForKey(CFSTR("Class"), CFSTR("IOHIDNXEventTranslatorServiceFilter"));
serializer.SetValueForKey(CFSTR("MatchScore"), (uint64_t)_matchScore);
serializer.SetValueForKey(CFSTR(kIOHIDNXEventTranslation), _isTranslationEnabled);
}