list.m   [plain text]


//
//  list.m
//  IOHIDFamily
//
//  Created by dekom on 8/15/17.
//

#import <Foundation/Foundation.h>
#include <IOKit/hid/IOHIDLib.h>
#include <getopt.h>
#include "utility.h"

int list(int argc, const char * argv[]);

static const char listOptionShort[] = "hm:";
static const struct option listOptionLong[] =
{
    { "help",       0,                  NULL,   'h' },
    { "matching",   required_argument,  NULL,   'm' },
    { NULL,         0,                  NULL,    0  }
};

const char listUsage[] =
"\nList HID Event System services and devices\n"
"\nUsage:\n\n"
"  hidutil list [ --matching <matching> ]\n"
"\nFlags:\n\n"
MATCHING_HELP
"\nExamples:\n\n"
"  hidutil list\n"
"  hidutil list --matching '{\"ProductID\":0x54c}'\n"
"  hidutil list --matching '{\"ProductID\":0x54c,\"VendorID\":746}'\n";


struct fields {
    NSString * key;
    NSUInteger integerBase;
    NSUInteger width;
    NSString * name;
} serviceFields [] = {
    {@kIOHIDVendorIDKey,            16, 0, @kIOHIDVendorIDKey},
    {@kIOHIDProductIDKey,           16, 0, @kIOHIDProductIDKey},
    {@kIOHIDLocationIDKey,          16, 0, @kIOHIDLocationIDKey },
    {@kIOHIDPrimaryUsagePageKey,    10, 0, @"UsagePage"},
    {@kIOHIDPrimaryUsageKey,        10, 0, @"Usage"},
    {@kIORegistryEntryIDKey,        16, 0, @"RegistryID"},
    {@kIOHIDTransportKey,            0, 0, @kIOHIDTransportKey},
    {@kIOClassKey,                   0, 0, @"Class"},
    {@kIOHIDProductKey,              0, 0, @"Product"},
};


static void listPrint(NSArray * infoArray) {
    NSDictionary * info;

    for (NSInteger index = 0; index < sizeof(serviceFields) / sizeof(serviceFields[0]); index++) {
        serviceFields[index].width = serviceFields[index].name.length;
    }
    
    for (info in infoArray) {
        for (NSInteger index = 0; index < sizeof(serviceFields) / sizeof(serviceFields[0]); index++) {
            NSUInteger width = formatPropertyValue (info[serviceFields[index].key], serviceFields[index].integerBase).length;
            if (width > serviceFields[index].width) {
                serviceFields[index].width = width;
            }
        }
    }
    for (NSInteger index = 0; index < sizeof(serviceFields) / sizeof(serviceFields[0]); index++) {
        printf("%-*s", (int)serviceFields[index].width + 1, [serviceFields[index].name cStringUsingEncoding: NSASCIIStringEncoding]);
    }
    printf ("\n");
    
    for (info in infoArray) {
        for (NSInteger index = 0; index < sizeof(serviceFields) / sizeof(serviceFields[0]); index++) {
            printf("%-*s", (int)serviceFields[index].width + 1, [formatPropertyValue (info[serviceFields[index].key], serviceFields[index].integerBase) cStringUsingEncoding: NSASCIIStringEncoding]);
        }
        printf ("\n");
    }
}


static void listServices(IOHIDEventSystemClientRef client) {
    NSArray *services = (NSArray *)CFBridgingRelease(IOHIDEventSystemClientCopyServices(client));
    NSDictionary *info;
    
    if (!services) {
        return;
    }
    
    NSMutableArray * infoArray = [[NSMutableArray alloc] init];
    for (id service in services) {
        info = createServiceInfoDictionary((__bridge IOHIDServiceClientRef)service);
        if (info) {
            [infoArray addObject: info];
        }
    }

    printf("Services:\n");
 
    listPrint (infoArray);
}

static void listDevices(IOHIDManagerRef manager) {
    NSSet *devices = CFBridgingRelease(IOHIDManagerCopyDevices(manager));
    NSDictionary *info;
    if (!devices) {
        return;
    }
    
    
    NSMutableArray * infoArray = [[NSMutableArray alloc] init];
    for (id device in devices) {
        info = createDeviceInfoDictionary((__bridge IOHIDDeviceRef)device);
        if (info) {
            [infoArray addObject: info];
        }
    }
    
    printf("Devices:\n");

    listPrint (infoArray);
}

int list (int argc, const char * argv[]) {
    int                         status      = STATUS_SUCCESS;
    int                         arg;
    IOHIDEventSystemClientRef   client      = NULL;
    IOHIDManagerRef             manager     = NULL;
    char                        *matching   = NULL;
    
    while ((arg = getopt_long(argc, (char **) argv, listOptionShort, listOptionLong, NULL)) != -1) {
        switch (arg) {
            case 'h':
                printf ("%s", listUsage);
                goto exit;
            case 'm':
                matching = optarg;
                break;
            default:
                status = STATUS_ERROR;
                goto exit;
        }
    }
    
    client = IOHIDEventSystemClientCreateWithType(kCFAllocatorDefault, kIOHIDEventSystemClientTypeMonitor, NULL);
    if (!client) {
        status = kUnknownErr;
        goto exit;
    }
    
    manager = IOHIDManagerCreate(kCFAllocatorDefault, 0);
    if (!manager) {
        status = kUnknownErr;
        goto exit;
    }
    
    if (matching && !setClientMatching(client, matching)) {
        printf("bad matching string: %s\n", matching);
    }
    
    listServices(client);
    printf("\n");
    
    if (matching) {
        if (!setManagerMatching(manager, matching)) {
            printf("bad matching string: %s\n", matching);
            IOHIDManagerSetDeviceMatching(manager, NULL);
        }
    } else {
        IOHIDManagerSetDeviceMatching(manager, NULL);
    }
    
    listDevices(manager);
    
exit:
    if (client) {
        CFRelease(client);
    }
    
    if (manager) {
        CFRelease(manager);
    }
    
    return status;
}