aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile5
-rw-r--r--board/Makefile8
-rw-r--r--platform/darwin.c167
4 files changed, 180 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 5768e2e..c9120e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Makefile b/Makefile
index 1fdf54f..e21da87 100644
--- a/Makefile
+++ b/Makefile
@@ -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 */
+}