Skip to content

Instantly share code, notes, and snippets.

@ghazel
Last active February 27, 2019 08:42
Show Gist options
  • Save ghazel/e2e0f2644f816cb4167021a2e0566357 to your computer and use it in GitHub Desktop.
Save ghazel/e2e0f2644f816cb4167021a2e0566357 to your computer and use it in GitHub Desktop.
touchbar IOHID watcher
// compile and run from the commandline with:
// clang -framework CoreFoundation -framework CoreGraphics -framework IOKit ./HID.c -o hid
// ./hid
#include <IOKit/hid/IOHIDValue.h>
#include <IOKit/hid/IOHIDManager.h>
#include <ApplicationServices/ApplicationServices.h>
CFMutableDictionaryRef myCreateDeviceMatchingDictionary(UInt32 pUsagePage, UInt32 pUsage)
{
CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFNumberRef usagePage = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pUsagePage);
CFNumberRef usage = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pUsage);
if (matchDict && usagePage && usage) {
CFDictionarySetValue(matchDict, CFSTR(kIOHIDDeviceUsagePageKey), usagePage);
CFDictionarySetValue(matchDict, CFSTR(kIOHIDDeviceUsageKey), usage);
CFRelease(usagePage);
CFRelease(usage);
return matchDict;
}
return 0;
}
CFMachPortRef eventTap;
CFRunLoopTimerRef disableTimer;
static void TimerCallback(CFRunLoopTimerRef timer, void *info)
{
printf("disabling CGEventTap\n");
CGEventTapEnable(eventTap, false);
CFRelease(disableTimer);
disableTimer = NULL;
}
static CGEventRef EventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eventRef, void *_Nullable userInfo)
{
printf("EventTapCallback type:%d\n", type);
if (type != -1) {
if (disableTimer) {
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), disableTimer, kCFRunLoopDefaultMode);
CFRelease(disableTimer);
disableTimer = NULL;
}
CFRunLoopTimerContext timerContext = {0, NULL, NULL, NULL, NULL};
disableTimer = CFRunLoopTimerCreate(kCFAllocatorDefault,
// 50ms is just a guess. One iteration with CFRunLoopPerformBlock was not enough
CFAbsoluteTimeGetCurrent() + 0.050,
0, 0, 0,
TimerCallback,
&timerContext);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), disableTimer, kCFRunLoopDefaultMode);
}
return eventRef;
}
static void hidInputReportCallback(void *context, IOReturn result, void *sender, IOHIDReportType type,
uint32_t reportID, uint8_t *reportValue, CFIndex reportLength)
{
IOHIDDeviceRef device = (IOHIDDeviceRef)sender;
CFTypeRef name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
printf("Device `%s`: ", CFStringGetCStringPtr(name, kCFStringEncodingUTF8));
for (int i = 0 ; i < reportLength; i++) {
printf("%3d ", (char)reportValue[i]);
}
printf("\nEnabling CGEventTap\n");
CGEventTapEnable(eventTap, true);
}
int main(void)
{
IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
// found from: ioreg -irc IOHIDDevice -w 0
CFMutableDictionaryRef touchbar = myCreateDeviceMatchingDictionary(13, 5);
CFMutableDictionaryRef touchbar2 = myCreateDeviceMatchingDictionary(65280, 35);
CFMutableDictionaryRef touchbar3 = myCreateDeviceMatchingDictionary(65280, 22);
CFMutableDictionaryRef matchesList[] = {touchbar3};
CFArrayRef matches = CFArrayCreate(kCFAllocatorDefault, (const void **)matchesList, sizeof(matchesList)/sizeof(matchesList[0]), NULL);
IOHIDManagerSetDeviceMatchingMultiple(hidManager, matches);
IOHIDManagerRegisterInputReportCallback(hidManager, hidInputReportCallback, NULL);
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, kCGEventMaskForAllEvents, EventTapCallback, NULL);
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
if (runLoopSource) {
CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopDefaultMode);
CGEventTapEnable(eventTap, false);
}
CFRunLoopRun();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment