utility.m   [plain text]


//
//  devices.m
//  IOHIDFamily
//
//  Created by YG on 4/14/16.
//
//
#include "hdutil.h"
#import <Foundation/Foundation.h>
#include <stdio.h>
#include <strings.h>
#include <getopt.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDEventSystemClient.h>
#include <IOKit/hid/IOHIDEventSystemKeys.h>
#include "AssertMacros.h"
#include "utility.h"
#include <IOKit/hid/IOHIDUsageTables.h>

static NSArray *matchingKeys(void) {
    return @[ @kIOHIDVendorIDKey, @kIOHIDProductIDKey, @kIOHIDTransportKey,
              @kIOHIDPrimaryUsageKey, @kIOHIDPrimaryUsagePageKey, @kIOHIDLocationIDKey,
              @kIOHIDTransportKey, @kIOHIDProductKey, @kIOHIDManufacturerKey, @kIOClassKey ];
}

NSDictionary *createServiceInfoDictionary(IOHIDServiceClientRef service) {
    NSMutableDictionary *serviceInfo = [[NSMutableDictionary  alloc] init];
    
    if (serviceInfo == NULL) {
        return serviceInfo;
    }
    
    [serviceInfo setValue:(NSNumber *)CFBridgingRelease(IOHIDServiceClientGetRegistryID(service)) forKey: @kIORegistryEntryIDKey];
    
    for (NSString *key in matchingKeys()) {
        [serviceInfo setValue:CFBridgingRelease(IOHIDServiceClientCopyProperty(service, (__bridge CFStringRef)key)) forKey:key];
    }
 
    return serviceInfo;
}

NSDictionary *createDeviceInfoDictionary(IOHIDDeviceRef device) {
    NSMutableDictionary *deviceInfo = [[NSMutableDictionary  alloc] init];
    uint64_t regID;
    
    if (deviceInfo == NULL) {
        return deviceInfo;
    }
    
    IORegistryEntryGetRegistryEntryID(IOHIDDeviceGetService(device), &regID);
    [deviceInfo setValue:@(regID) forKey: @kIORegistryEntryIDKey];
    
    for (NSString *key in matchingKeys()) {
        [deviceInfo setValue:IOHIDDeviceGetProperty(device, (__bridge CFStringRef)key) forKey:key];
    }
    
    return deviceInfo;
}

static NSString *createMatchingString(const char * str) {
    NSError *e = NULL;
    NSMutableString *filterString = [NSMutableString stringWithUTF8String:str];
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"0[xX][0-9a-f]+" options:NSRegularExpressionCaseInsensitive error:&e];
    NSUInteger start = 0;
    NSTextCheckingResult * result = NULL;
    do {
        result = [regex firstMatchInString:filterString options:0 range: NSMakeRange(start, filterString.length - start)];
        if (result) {
            NSScanner *scan = [NSScanner scannerWithString:[filterString substringWithRange:result.range]];
            unsigned long long value = 0;
            [scan scanHexLongLong:&value];
            NSString *valueString = [NSString stringWithFormat:@"%llu",value];
            [filterString replaceCharactersInRange:result.range withString: valueString];
            start = result.range.location + valueString.length;
        }
    } while (result);
    
    return filterString;
}

NSString * createPropertiesString (const char * str) {
    return createMatchingString (str);
}

id createPropertiesDicitonary (NSString *str) {
    NSError      *e = NULL;
    id value = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers  error:&e];
    if (e) {
        NSLog(@"NSJSONSerialization %@", e);
    }
    return value;
}

static NSDictionary *matchingDictionaryForDeviceString(NSString *matching)
{
    NSDictionary *matchingDictionary = nil;
    
    if ([[NSString stringWithUTF8String:"keyboard"] containsString:matching]) {
        matchingDictionary = @{ @(kIOHIDPrimaryUsagePageKey) : @(kHIDPage_GenericDesktop),
                                @(kIOHIDPrimaryUsageKey) : @(kHIDUsage_GD_Keyboard) };
    } else if ([[NSString stringWithUTF8String:"mouse"] containsString:matching]) {
        matchingDictionary = @{ @(kIOHIDPrimaryUsagePageKey) : @(kHIDPage_GenericDesktop),
                                @(kIOHIDPrimaryUsageKey) : @(kHIDUsage_GD_Mouse) };
    } else if ([[NSString stringWithUTF8String:"digitizer"] containsString:matching]) {
        matchingDictionary = @{ @(kIOHIDPrimaryUsagePageKey) : @(kHIDPage_Digitizer) };
    }
    
    return matchingDictionary;
}

bool setClientMatching(IOHIDEventSystemClientRef client, const char *str) {
    bool        result          = false;
    NSError     *error          = nil;
    NSString    *matchString    = nil;
    id          matchingObj     = nil;
    
    matchString = createMatchingString(str);
    
    if ([[matchString substringToIndex:1] isEqual:@"["] ||
        [[matchString substringToIndex:1] isEqual:@"{"]) {
        matchingObj = [NSJSONSerialization JSONObjectWithData:[matchString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&error];
    } else {
        matchingObj = matchingDictionaryForDeviceString(matchString);
    }
   
    if (!client || error || !matchingObj) {
        if (error) {
            NSLog(@"Serialization error: %@", error);
        }
        goto exit;
    }
    
    if ([matchingObj isKindOfClass:[NSDictionary class]]) {
        IOHIDEventSystemClientSetMatching(client, (__bridge CFDictionaryRef)matchingObj);
        result = true;
    } else if ([matchingObj isKindOfClass:[NSArray class]]) {
        IOHIDEventSystemClientSetMatchingMultiple(client, (__bridge CFArrayRef)matchingObj);
        result = true;
    }
    
exit:
    return result;
}

bool setManagerMatching(IOHIDManagerRef manager, const char *str) {
    bool        result          = false;
    NSError     *error          = nil;
    NSString    *matchString    = nil;
    id          matchingObj     = nil;
    
    matchString = createMatchingString(str);
    
    if ([[matchString substringToIndex:1] isEqual:@"["] ||
        [[matchString substringToIndex:1] isEqual:@"{"]) {
        matchingObj = [NSJSONSerialization JSONObjectWithData:[matchString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&error];
    } else {
        matchingObj = matchingDictionaryForDeviceString(matchString);
    }
    
    if (!manager || error || !matchingObj) {
        if (error) {
            NSLog(@"Serialization error: %@", error);
        }
        goto exit;
    }
    
    if ([matchingObj isKindOfClass:[NSDictionary class]]) {
        IOHIDManagerSetDeviceMatching(manager, (__bridge CFDictionaryRef)matchingObj);
        result = true;
    } else if ([matchingObj isKindOfClass:[NSArray class]]) {
        IOHIDManagerSetDeviceMatchingMultiple(manager, (__bridge CFArrayRef)matchingObj);
        result = true;
    }
    
exit:
    return result;
}