#include #include #include #include #include "darwin-native.bridging.h" #include "platform.h" #include "../board/board.h" /* TODO: Try out objc_msgsend for fun in future to not even have Obj-C! */ extern void keyboard_on_down(void); extern void keyboard_on_up(void); static IOHIDManagerRef hid_manager = NULL; bool sound_init(float volume) { /* Call Obj-C function */ return macos_sound_init(volume); } void sound_play(unsigned char *buffer, unsigned int buffer_len) { /* Call Obj-C function */ macos_sound_play(buffer, buffer_len); } /* TODO: Maybe merge some of this stuff with linux, lots of overlap */ void handle_signal(int signal) { (void) signal; /* Terminate the runloop, which will kick out of enter_idle */ CFRunLoopStop(CFRunLoopGetMain()); } void enter_idle(void) { /* Setup quit handling */ struct sigaction sa = { .sa_handler = handle_signal }; sigaction(SIGINT, &sa, NULL); /* Enter the CoreFoundation run loop and it will pump events for our callback */ CFRunLoopRun(); } /* TODO: probably also merge into a *nix module */ struct board *load_board(char *board_name) { char so_name[100]; int r = snprintf(so_name, 100, "./board/%s.dylib", board_name); if (r < 0 || r >= 100) { printf("ERROR: Invalid board name.\n"); return false; } void *board_module = dlopen(so_name, RTLD_LAZY); if (board_module == NULL) { printf("ERROR: Could not load board module.\n"); return false; } fn_board_init board_init = dlsym(board_module, "board_init"); if (board_init == NULL) { printf("ERROR: No board initialisation function could be loaded.\n"); return false; } struct board_data data = { .sound_play = &sound_play }; return board_init(data); } /* https://developer.apple.com/documentation/iokit/iohidvaluecallback */ void hid_callback(void *context, IOReturn result, void *sender, IOHIDValueRef value) { (void) context; (void) result; (void) sender; IOHIDElementRef elem = IOHIDValueGetElement(value); /* seems like 'usage' is the scancode */ int32_t scancode = IOHIDElementGetUsage(elem); /* someething < 4 (usually -1) is emitted on every press, not sure what it really means. * But it doesn't seem to be a valid scancode regardless... * https://www.win.tue.nl/~aeb/linux/kbd/scancodes-14.html */ if (scancode < 4) return; /* seems like getting 'int value' is if it is key up or down * true(1) = down */ bool key_down = IOHIDValueGetIntegerValue(value) == 1; // printf(">>> value: %d, down: %d\n", scancode, key_down); if (key_down) keyboard_on_down(); else keyboard_on_up(); } bool keyboard_hook(void) { fprintf(stderr, "Setting up keyboard manager...\n"); hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); if (hid_manager == NULL) { printf("ERROR: IOKit HID manager failed to initialise.\n"); return false; } /* TODO: handle errors on allocs below? */ /* We want to hook keyboard, so setup the filter to match the device for IOKit. * It wants a CFArray of CFDictionary, so we set that up */ fprintf(stderr, "Setting up IOKit hook...\n"); const void *keyboard_keys[] = { CFSTR(kIOHIDDeviceUsagePageKey), CFSTR(kIOHIDDeviceUsageKey) }; int gd = kHIDPage_GenericDesktop; int gdk = kHIDUsage_GD_Keyboard; const void *keyboard_values[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &gd), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &gdk) }; /* Usage Page = Generic Desktop, Usage = Keyboard */ CFDictionaryRef keyboard_dict = CFDictionaryCreate( kCFAllocatorDefault, keyboard_keys, keyboard_values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); const void *match_values[] = { keyboard_dict }; /* 1 element, the keyboard filter dict */ CFArrayRef device_matches = CFArrayCreate( kCFAllocatorDefault, match_values, 1, &kCFTypeArrayCallBacks ); IOHIDManagerSetDeviceMatchingMultiple(hid_manager, device_matches); CFRelease(keyboard_dict); CFRelease(device_matches); IOHIDManagerRegisterInputValueCallback(hid_manager, hid_callback, NULL); IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); /* Schedule the HID manager for run loop later */ IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); return true; } void keyboard_unhook(void) { fprintf(stderr, "Cleaning up keyboard hooks...\n"); IOHIDManagerClose(hid_manager, kIOHIDOptionsTypeNone); /* TODO: Cleanup sound system */ }