IOHIDDisplaySessionFilter.m [plain text]
//
// HIDDisplaySessionFilter.m
// HIDDisplaySessionFilter
//
// Created by AB on 1/25/19.
//
#import "IOHIDDisplaySessionFilter.h"
#import <os/log.h>
#import <HIDDisplay/HIDDisplay.h>
#import <HIDDisplay/HIDDisplayDevicePrivate.h>
#import <AssertMacros.h>
#import <IOKit/hid/AppleHIDUsageTables.h>
#import <IOKit/hid/IOHIDKeys.h>
#import <IOKit/hid/IOHIDManager.h>
#import <IOKit/hid/IOHIDDevice.h>
static os_log_t HIDDisplaySessionFilterLog (void);
@interface HIDDisplaySessionFilter (HIDDisplaySessionFilterPrivate)
-(void) handleDeviceMatchCallback:(IOHIDDeviceRef) device;
-(void) handleDeviceRemovalCallback:(IOHIDDeviceRef) device;
@end
os_log_t HIDDisplaySessionFilterLog (void)
{
static os_log_t slog = NULL;
static dispatch_once_t sonceToken;
dispatch_once(&sonceToken, ^{
slog = os_log_create("com.apple.HIDDisplaySessionFilter", "default");
});
return slog;
}
static void __deviceMatchingCallback(void * _Nullable context, IOReturn result, void * _Nullable __unused sender, IOHIDDeviceRef device)
{
HIDDisplaySessionFilter *selfRef = (__bridge HIDDisplaySessionFilter*)context;
if (!selfRef || result != kIOReturnSuccess) {
return;
}
[selfRef handleDeviceMatchCallback:device];
}
static void __deviceRemovalCallback(void * _Nullable context, IOReturn result, void * _Nullable __unused sender, IOHIDDeviceRef device)
{
HIDDisplaySessionFilter *selfRef = (__bridge HIDDisplaySessionFilter*)context;
if (!selfRef || result != kIOReturnSuccess) {
return;
}
[selfRef handleDeviceRemovalCallback:device];
}
@implementation HIDDisplaySessionFilter
{
IOHIDManagerRef _manager;
NSDictionary *_matching;
NSMutableArray *_debugInfo;
NSThread *_thread;
CFRunLoopRef _runLoop;
}
-(nullable instancetype) init
{
self = [super init];
require(self, exit);
_manager = IOHIDManagerCreate (kCFAllocatorDefault, kIOHIDOptionsTypeNone);
require(_manager, exit);
_matching = @{@kIOHIDPrimaryUsagePageKey : @(kHIDPage_AppleVendor), @kIOHIDPrimaryUsageKey : @(kHIDUsage_AppleVendor_Display)};
_debugInfo = [[NSMutableArray alloc] init];
IOHIDManagerSetDeviceMatching(_manager, (__bridge CFDictionaryRef)_matching);
IOHIDManagerRegisterDeviceMatchingCallback(_manager, __deviceMatchingCallback, (__bridge void*)self);
IOHIDManagerRegisterDeviceRemovalCallback(_manager, __deviceRemovalCallback, (__bridge void*)self);
exit:
return self; // nil can cause unwanted condition in plugin
}
-(BOOL) open
{
__weak HIDDisplaySessionFilter *weakSelf = self;
if (!_manager) {
return NO;
}
[NSThread detachNewThreadWithBlock:^{
__strong HIDDisplaySessionFilter *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
strongSelf->_runLoop = CFRunLoopGetCurrent();
IOHIDManagerScheduleWithRunLoop(strongSelf->_manager, strongSelf->_runLoop, kCFRunLoopDefaultMode);
IOHIDManagerOpen(strongSelf->_manager, kIOHIDOptionsTypeNone);
CFRunLoopRun();
}];
return YES;
}
-(void) handleDeviceMatchCallback:(IOHIDDeviceRef) device
{
NSNumber *uniqueID = nil;
HIDDisplayDevice *displayDevice = [[HIDDisplayDevice alloc] initWithMatching:_matching];
uniqueID = (__bridge NSNumber*)IOHIDDeviceGetProperty( device, CFSTR(kIOHIDUniqueIDKey) );
// shouldn't expect failure here, but just for case, deviceMatchCallback is called and the device disappear and is not detected by framework
require_action(displayDevice, exit, os_log_error(HIDDisplaySessionFilterLog(), "No valid HID device for matching
[self setPresetIndexForDevice:displayDevice uniqueID:uniqueID];
exit:
return;
}
-(void) handleDeviceRemovalCallback:(IOHIDDeviceRef) device
{
NSNumber *uniqueID = (__bridge NSNumber*)IOHIDDeviceGetProperty( device, CFSTR(kIOHIDUniqueIDKey) );
@synchronized (self) {
[_debugInfo removeObject:uniqueID];
}
}
-(BOOL) setPresetIndexForDevice:(HIDDisplayDevice*) device uniqueID:(NSNumber*) uniqueID
{
NSError *err = nil;
NSInteger factoryDefaultPresetIndex = -1;
NSInteger activePresetIndex = -1;
BOOL ret = NO;
factoryDefaultPresetIndex = [device getFactoryDefaultPresetIndex:&err];
require_action(factoryDefaultPresetIndex != -1, exit, os_log_error(HIDDisplaySessionFilterLog(), "Failed to get factory default preset index with
activePresetIndex = [device getActivePresetIndex:&err];
require_action(activePresetIndex != -1, exit, os_log_error(HIDDisplaySessionFilterLog(), "Failed to get active preset index with
if (factoryDefaultPresetIndex != activePresetIndex) {
os_log(HIDDisplaySessionFilterLog(),"Setting active preset index (
require_action([device setActivePresetIndex:factoryDefaultPresetIndex error:&err], exit, os_log_error(HIDDisplaySessionFilterLog(), "Failed to set active preset to factory default preset with error }
ret = YES;
exit:
@synchronized (self) {
if (ret) {
[_debugInfo addObject:uniqueID];
}
}
return ret;
}
-(void) close
{
if (!_runLoop) return;
CFRunLoopPerformBlock(_runLoop, kCFRunLoopDefaultMode, ^{
if (_manager) {
IOHIDManagerUnscheduleFromRunLoop(_manager, _runLoop, kCFRunLoopDefaultMode);
IOHIDManagerClose(_manager, kIOHIDOptionsTypeNone);
}
CFRunLoopStop(_runLoop);
});
}
-(NSDictionary*) serialize
{
NSMutableDictionary *ret = [[NSMutableDictionary alloc] init];
@synchronized (self) {
ret[@"Class"] = @"HIDDisplaySessionFilter";
ret[@"Status"] = self->_debugInfo;
}
return ret;
}
-(CFDictionaryRef) getProperty
{
return (__bridge CFDictionaryRef)[self serialize];
}
-(void) dealloc
{
if (_manager) {
CFRelease(_manager);
}
}
@end