diff options
Diffstat (limited to '')
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | board/Makefile | 8 | ||||
-rw-r--r-- | platform/darwin.c | 167 |
4 files changed, 180 insertions, 3 deletions
@@ -3,4 +3,5 @@ clak.exe board/boards.h board/*/sound.h board/*.dll -board/*.so
\ No newline at end of file +board/*.so +board/*.dylib @@ -1,7 +1,7 @@ NAME = clak PREFIX = $(HOME)/.local -CC = gcc +CC = clang CFLAGS += -std=c99 -Wall -Wextra -Wshadow -Werror ifeq ($(OS),Windows_NT) @@ -14,6 +14,9 @@ else PKG_CONF_LIBS = sdl2 SDL2_mixer x11 xi CFLAGS += `pkg-config --cflags $(PKG_CONF_LIBS)` LDLIBS += `pkg-config --libs $(PKG_CONF_LIBS)` + else ifeq ($(UNAME_S),Darwin) + PLATFORM = darwin + CFLAGS += -framework CoreFoundation -framework IOKit -framework AppKit endif endif diff --git a/board/Makefile b/board/Makefile index ae3d691..ba53910 100644 --- a/board/Makefile +++ b/board/Makefile @@ -6,8 +6,14 @@ CFLAGS += -std=c99 -Wall -Wextra -Wshadow -Werror -pedantic -shared ifeq ($(OS),Windows_NT) OUTEXT = dll else - OUTEXT = so CFLAGS += -fPIC + + UNAME_S := $(shell uname) + ifeq ($(UNAME_S),Linux) + OUTEXT = so + else ifeq ($(UNAME_S),Darwin) + OUTEXT = dylib + endif endif default: all 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 */ +} |