HIDVirtualEventService.m   [plain text]

//
// HIDVirtualEventService.m
// HID
//
// Created by ygoryachok on 12/12/18.
//

#import "HIDVirtualEventService.h"
#import "HIDEventSystemClient.h"
#import "HIDEventSystemClientPrivate.h"
#import <IOKit/hid/IOHIDEventSystemClient.h>
#import "HIDServiceClient.h"
#import "NSError+IOReturn.h"
#import "IOHIDPrivateKeys.h"

@interface HIDVirtualEventService ()

@property HIDEventSystemClient * client;
@property HIDServiceClient * serviceClient;
@property dispatch_queue_t queue;

@end

@implementation HIDVirtualEventService

- (instancetype)init
{
self = [super init];

if (!self) {
return self;
}

self.client = [[HIDEventSystemClient alloc] initWithType: HIDEventSystemClientTypeSimple];

if (!self.client) {
return nil;
}

return self;
}

- (NSString *)description
{
return [self.serviceClient description];
}

- (void)setCancelHandler:(HIDBlock)handler
{
[self.client setCancelHandler:handler];
}

- (void)setDispatchQueue:(dispatch_queue_t)queue
{

[self.client setDispatchQueue:queue];
self.queue = queue;
}

static void __HIDVirtualServiceNotifyCallback ( void * __unused target, void * __unused context, IOHIDServiceClientRef __unused service, uint32_t type, CFDictionaryRef property) {

HIDVirtualEventService * self = (__bridge HIDVirtualEventService *)target;
__strong id <HIDVirtualEventServiceDelegate> delegate = self.delegate;

if (!delegate) {
return;
}

HIDVirtualServiceNotificationType notificationType;
switch (type) {
case kIOHIDVirtualServiceScheduledWithDispatchQueue:
notificationType = HIDVirtualServiceNotificationTypeEnumerated;
break;
case kIOHIDVirtualServiceUnScheduledFromDispatchQueue:
notificationType = HIDVirtualServiceNotificationTypeTerminated;
break;
default:
return;
break;
}

[delegate notification:notificationType withProperty:(__bridge NSDictionary * _Nullable)(property) forService:self];
}

static bool __HIDVirtualServiceSetPropertyCallback (void * target, void * __unused context, IOHIDServiceClientRef __unused service, CFStringRef key, CFTypeRef value) {
HIDVirtualEventService * self = (__bridge HIDVirtualEventService *)target;
__strong id <HIDVirtualEventServiceDelegate> delegate = self.delegate;

if (!delegate) {
return false;
}

return [delegate setProperty:(__bridge id)value forKey:(__bridge NSString *)key forService:self];
}

static CFTypeRef __HIDVirtualServiceCopyPropertyCallback (void * target, void * __unused context, IOHIDServiceClientRef __unused service, CFStringRef key) {
HIDVirtualEventService * self = (__bridge HIDVirtualEventService *)target;
__strong id <HIDVirtualEventServiceDelegate> delegate = self.delegate;

if (!delegate) {
return false;
}

return CFBridgingRetain([delegate propertyForKey:(__bridge NSString * _Nonnull)(key) forService:self]);
}

static IOHIDEventRef __HIDVirtualServiceCopyEvent (void * target, void * __unused context, IOHIDServiceClientRef __unused service, IOHIDEventType type, IOHIDEventRef matching, IOOptionBits options) {
HIDVirtualEventService * self = (__bridge HIDVirtualEventService *)target;
__strong id <HIDVirtualEventServiceDelegate> delegate = self.delegate;

if (!delegate) {
return false;
}

NSMutableDictionary * matchingDict = [[NSMutableDictionary alloc] init];
matchingDict[@(kIOHIDMatchingEventTypeKey)] = @(type);
matchingDict[@(kIOHIDMatchingEventKey)] = (__bridge id)matching;
matchingDict[@(kIOHIDMatchingEventOptionsKey)] = @(options);

return (IOHIDEventRef) CFBridgingRetain([delegate copyEventMatching:matchingDict forService:self]);
}

static IOReturn __HIDVirtualServiceSetOutputEvent (void * target, void * __unused context, IOHIDServiceClientRef __unused service, IOHIDEventRef event) {
HIDVirtualEventService * self = (__bridge HIDVirtualEventService *)target;

__strong id <HIDVirtualEventServiceDelegate> delegate = self.delegate;

if (!delegate) {
return false;
}

return [delegate setOutputEvent:(__bridge HIDEvent *)event forService:self] ? kIOReturnSuccess : kIOReturnError;
}

static IOHIDEventRef _Nullable __HIDVirtualServiceClientCopyMatchingEventCallback (void * target, void * __unused context, IOHIDServiceClientRef __unused service, CFDictionaryRef matching)
{
HIDVirtualEventService * self = (__bridge HIDVirtualEventService *)target;

__strong id <HIDVirtualEventServiceDelegate> delegate = self.delegate;

if (!delegate) {
return false;
}

return (IOHIDEventRef) CFBridgingRetain([delegate copyEventMatching:(__bridge NSDictionary *)matching forService:self]);
}

- (void)activate
{
[self.client activate];

IOHIDVirtualServiceClientCallbacksV2 callbacks = {
kIOHIDVirtualServiceClientCallbacksV2,
{
__HIDVirtualServiceNotifyCallback,
__HIDVirtualServiceSetPropertyCallback,
__HIDVirtualServiceCopyPropertyCallback,
__HIDVirtualServiceCopyEvent,
__HIDVirtualServiceSetOutputEvent
},
__HIDVirtualServiceClientCopyMatchingEventCallback
};

self.serviceClient = (HIDServiceClient *) CFBridgingRelease(IOHIDVirtualServiceClientCreateWithCallbacks (self.client.client, NULL, (IOHIDVirtualServiceClientCallbacks *)&callbacks, (__bridge void * _Nullable)(self), NULL));

__weak HIDVirtualEventService * weakSelf = self;
HIDBlock thandler = ^{
__strong HIDVirtualEventService * self_ = weakSelf;
if (self_) {
[self_.delegate notification:HIDVirtualServiceNotificationTypeTerminated withProperty:nil forService:self_];
}
};

if (self.serviceClient) {
[self.serviceClient setRemovalHandler: thandler];
} else {
dispatch_async(self.queue, thandler);
}
}

- (void)cancel
{
[self.client cancel];
}

- (BOOL) dispatchEvent: (HIDEvent *) event
{
return IOHIDVirtualServiceClientDispatchEvent ((__bridge IOHIDServiceClientRef) self.serviceClient, (IOHIDEventRef) event);
}

- (uint64_t) serviceID
{
NSNumber * senderID = (NSNumber *)IOHIDServiceClientGetRegistryID ((__bridge IOHIDServiceClientRef) self.serviceClient);
return [senderID unsignedLongLongValue];
}

@end