IOHIDDeviceTestController.m [plain text]
//
// IOHIDTestController+IOHIDDeviceTestController.m
// IOHIDFamily
//
// Created by YG on 10/24/16.
//
//
#import "IOHIDDeviceTestController.h"
#include "IOHIDUnitTestUtility.h"
static void HIDManagerDeviceAddedCallback ( void * _Nullable context, IOReturn result, void * _Nullable sender, IOHIDDeviceRef device);
static void HIDManagerDeviceRemovedCallback ( void * _Nullable context, IOReturn result, void * _Nullable sender, IOHIDDeviceRef device);
static void HIDDeviceValueCallback (void * _Nullable context, IOReturn result, void * _Nullable sender, IOHIDValueRef value);
@implementation IOHIDDeviceTestController {
CFRunLoopRef runloop;
id deviceUniqueID;
dispatch_semaphore_t sema;
}
-(nullable instancetype) initWithDeviceUniqueID: (nonnull id) deviceID :(nonnull CFRunLoopRef) runLoop {
NSDictionary *matching = @{@kIOHIDPhysicalDeviceUniqueIDKey : deviceID};
return [self initWithMatching: matching :runLoop];
}
-(nullable instancetype) initWithMatching: (nonnull NSDictionary *) matching :(nonnull CFRunLoopRef) runLoop {
self = [super init];
if (!self) {
return self;
}
self->_deviceManager = IOHIDManagerCreate (kCFAllocatorDefault, kIOHIDOptionsTypeNone);
if (!self->_deviceManager) {
return nil;
}
self->runloop = runLoop;
self.values = [[NSMutableArray alloc] init];
self->sema = dispatch_semaphore_create(0);
deviceUniqueID = matching[@kIOHIDPhysicalDeviceUniqueIDKey];
IOHIDManagerSetDeviceMatching(self.deviceManager, (CFDictionaryRef)matching);
IOHIDManagerRegisterDeviceMatchingCallback(self.deviceManager, HIDManagerDeviceAddedCallback, (__bridge void *)(self));
IOHIDManagerRegisterDeviceRemovalCallback(self.deviceManager, HIDManagerDeviceRemovedCallback, (__bridge void *)(self));
if (CFRunLoopGetCurrent() == self->runloop) {
IOHIDManagerScheduleWithRunLoop(self.deviceManager, (CFRunLoopRef)self->runloop , kCFRunLoopDefaultMode);
IOHIDManagerOpen(self.deviceManager, kIOHIDOptionsTypeNone);
CFRunLoopRunInMode (kCFRunLoopDefaultMode, 2.0, false);
} else {
CFRunLoopPerformBlock(self->runloop, kCFRunLoopDefaultMode, ^{
IOHIDManagerScheduleWithRunLoop(self.deviceManager, (CFRunLoopRef)self->runloop , kCFRunLoopDefaultMode);
IOHIDManagerOpen(self.deviceManager, kIOHIDOptionsTypeNone);
});
CFRunLoopWakeUp(self->runloop);
}
if (dispatch_semaphore_wait (self->sema, dispatch_time(DISPATCH_TIME_NOW, kDeviceMatchingTimeout * NSEC_PER_SEC))) {
return nil;
}
return self;
}
-(void)dealloc {
[self invalidate];
if (self.device) {
CFRelease (self.device);
}
if (self.deviceManager) {
CFRelease (self.deviceManager);
}
}
-(void)ManagerDeviceAdded : (IOHIDDeviceRef) device {
CFTypeRef value = IOHIDDeviceGetProperty (device, CFSTR (kIOHIDPhysicalDeviceUniqueIDKey));
if (value && CFEqual(value, (__bridge CFTypeRef)(self->deviceUniqueID))) {
self->_device = (IOHIDDeviceRef) CFRetain(device);
TestLog("ManagerDeviceAdded: IOHIDDeviceRegisterInputValueCallback (self.device, HIDDeviceValueCallback, (__bridge void * _Nullable)(self));
IOHIDDeviceOpen(self.device, 0);
dispatch_semaphore_signal (self->sema);
}
}
-(void)ManagerDeviceRemoved: (IOHIDDeviceRef) device {
if (self.device == device) {
TestLog("ManagerDeviceRemoved: self.device = nil;
}
}
-(void)DeviceValueCallback: (IOHIDValueRef) value {
NSMutableDictionary *valueDict = [[NSMutableDictionary alloc] initWithCapacity:2];
valueDict[@"timestamp"] = @(IOHIDValueGetTimeStamp (value));
CFIndex lenght = IOHIDValueGetLength (value);
const uint8_t *bytes = IOHIDValueGetBytePtr (value);
if (lenght && bytes) {
valueDict[@"data"] = [[NSData alloc] initWithBytes:bytes length:lenght];
}
IOHIDElementRef element = IOHIDValueGetElement(value);
valueDict [@"element"] = (__bridge id _Nullable)(element);
@synchronized (self.values) {
[self.values addObject:valueDict];
}
TestLog("DeviceValueCallback: }
-(void)invalidate {
if (self->runloop) {
if (CFRunLoopGetCurrent() == runloop) {
IOHIDManagerUnscheduleFromRunLoop (self.deviceManager, self->runloop, kCFRunLoopDefaultMode);
IOHIDManagerClose(self.deviceManager, 0);
} else {
dispatch_semaphore_t completionSema = dispatch_semaphore_create(0);
CFRunLoopPerformBlock(self->runloop, kCFRunLoopDefaultMode, ^{
IOHIDManagerUnscheduleFromRunLoop(self.deviceManager, (CFRunLoopRef)self->runloop , kCFRunLoopDefaultMode);
IOHIDManagerClose(self.deviceManager, kIOHIDOptionsTypeNone);
dispatch_semaphore_signal(completionSema);
});
CFRunLoopWakeUp(self->runloop);
dispatch_semaphore_wait (completionSema, DISPATCH_TIME_FOREVER);
}
self->runloop = NULL;
}
}
@end
void HIDManagerDeviceAddedCallback ( void * _Nullable context, IOReturn result, void * _Nullable sender __unused, IOHIDDeviceRef device) {
IOHIDDeviceTestController *self = (__bridge IOHIDDeviceTestController *)context;
if (result) {
TestLog("HIDManagerDeviceAddedCallback: result:0x }
[self ManagerDeviceAdded : device];
}
void HIDManagerDeviceRemovedCallback ( void * _Nullable context, IOReturn result, void * _Nullable sender __unused, IOHIDDeviceRef device) {
IOHIDDeviceTestController *self = (__bridge IOHIDDeviceTestController *)context;
if (result) {
TestLog("HIDManagerDeviceRemovedCallback: result:0x }
[self ManagerDeviceRemoved : device];
}
void HIDDeviceValueCallback (void * _Nullable context, IOReturn result, void * _Nullable sender __unused, IOHIDValueRef value) {
IOHIDDeviceTestController *self = (__bridge IOHIDDeviceTestController *)context;
if (result) {
TestLog("HIDDeviceValueCallback: result:0x }
[self DeviceValueCallback : value];
}