aboutsummaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorNicholas Tay <nick@windblume.net>2022-11-03 00:56:38 +1100
committerNicholas Tay <nick@windblume.net>2022-11-03 01:00:06 +1100
commit0f5a18991f320a2df370994326f882a144c45b02 (patch)
tree685ad7abb3883311c085c203038a9a1566acb847 /platform
parent7fe48a5a3f65c759adf7ff4b778bcf8a18334c7b (diff)
downloadclak-0f5a18991f320a2df370994326f882a144c45b02.tar.gz
clak-0f5a18991f320a2df370994326f882a144c45b02.tar.bz2
clak-0f5a18991f320a2df370994326f882a144c45b02.zip
Initial macOS support (key hooking only, no sound yet)
Thought I'd commit this first, since it'll be some Objective-C stuff coming (tested it out in another mini probe). Mostly has stuff similar to Linux so probably should abstract some out to *nix common. But uses IOKit to get into the typing events. Will use Obj-C for now for sound, since it'll probably be a NSSound thing, hooked up with NSCache (like Linux) to reduce loading into NS format multiple times. Also probably would need to free the sound object on finish sound, and not sure how I'd do Obj-C delegates from C... it would be fun to figure it out though eventually Also switched the Makefile to use clang, it's warnings do seem to be nicer :) and is what `gcc` is aliased to on a Mac by default anyway.
Diffstat (limited to 'platform')
-rw-r--r--platform/darwin.c167
1 files changed, 167 insertions, 0 deletions
diff --git a/platform/darwin.c b/platform/darwin.c
new file mode 100644
index 0000000..820c012
--- /dev/null
+++ b/platform/darwin.c
@@ -0,0 +1,167 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <dlfcn.h>
+#include <IOKit/hid/IOHIDManager.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 float volume = 0;
+static IOHIDManagerRef hid_manager = NULL;
+
+bool sound_init(float _volume)
+{
+ volume = _volume;
+
+ /* TODO: Init sound, NSCache probably too via bridge? */
+
+ return true;
+}
+
+void sound_play(unsigned char *buffer, unsigned int buffer_len)
+{
+ (void) buffer;
+ (void) buffer_len;
+
+ /* TODO: Play the sound via obj-c for now */
+}
+
+/* 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 */
+}