#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFString.h>
#include <IOKit/IOKitLib.h>
#include <stdio.h>

#define IOKIT_FILTER_CLASS "IOHIDEventService"

CFStringRef getHIDVendorID(io_service_t service) {
    CFTypeRef vendorID = IORegistryEntryCreateCFProperty(service, CFSTR(kIOHIDManufacturerIDKey), kCFAllocatorDefault, 0);
    if (vendorID) {
        return vendorID;
    } else {
        return CFSTR("");
    }
}

CFStringRef getHIDProductID(io_service_t service) {
    CFTypeRef productID = IORegistryEntryCreateCFProperty(service, CFSTR(kIOHIDProductIDKey), kCFAllocatorDefault, 0);
    if (productID) {
        return productID;
    } else {
        return CFSTR("");
    }
}

void printHIDDevice(io_service_t service) {
    CFStringRef vendorID = getHIDVendorID(service);
    CFStringRef productID = getHIDProductID(service);
    printf("%s:%s\n", CFStringGetCStringPtr(vendorID, kCFStringEncodingUTF8), CFStringGetCStringPtr(productID, kCFStringEncodingUTF8));
}

int main(int argc, const char * argv[]) {
    kern_return_t kr;
    mach_port_t masterPort;
    CFMutableDictionaryRef matching;
    io_iterator_t iterator;
    io_service_t service;
    kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
    if (kr != KERN_SUCCESS) {
        printf("error getting master port\n");
        return 1;
    }
    matching = IOServiceMatching(IOKIT_FILTER_CLASS);
    if (!matching) {
        printf("no matching\n");
        return 1;
    }
    CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), CFSTR(""));
    CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), CFSTR(""));
    kr = IOServiceGetMatchingServices(masterPort, matching, &iterator);
    if (kr != KERN_SUCCESS) {
        printf("error getting matching services\n");
        return 1;
    }
    while ((service = IOIteratorNext(iterator)) != IO_OBJECT_NULL) {
        printHIDDevice(service);
        IOObjectRelease(service);
    }
    return 0;
}

// Educational Purposes Only