2
0
luboslenco 3 долоо хоног өмнө
parent
commit
f2657fdc37

+ 0 - 1
base/sources/backends/macos_system.h

@@ -27,7 +27,6 @@
 - (void)scrollWheel:(NSEvent *)theEvent;
 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender;
 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender;
-- (void)update;
 - (BOOL)acceptsFirstResponder;
 - (BOOL)becomeFirstResponder;
 - (BOOL)resignFirstResponder;

+ 401 - 407
base/sources/backends/macos_system.m

@@ -27,9 +27,7 @@ static NSApplication *myapp;
 static NSWindow *window;
 static BasicMTKView *view;
 static char language[3];
-#ifdef WITH_GAMEPAD
-static struct HIDManager *hidManager;
-#endif
+static int current_cursor_index = 0;
 
 @implementation BasicMTKView
 
@@ -411,9 +409,6 @@ static int getMouseY(NSEvent *event) {
 	return YES;
 }
 
-- (void)update {
-}
-
 - (id)initWithFrame:(NSRect)frameRect {
 	self = [super initWithFrame:frameRect];
 
@@ -466,445 +461,134 @@ void iron_copy_to_clipboard(const char *text) {
 	[board setString:[NSString stringWithUTF8String:text] forType:NSStringPboardType];
 }
 
-#ifdef WITH_GAMEPAD
-
-static void inputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef);
-static void valueAvailableCallback(void *inContext, IOReturn inResult, void *inSender);
-static void reset(struct HIDGamepad *gamepad);
-static void initDeviceElements(struct HIDGamepad *gamepad, CFArrayRef elements);
-static void buttonChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int buttonIndex);
-static void axisChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int axisIndex);
+int iron_count_displays(void) {
+	NSArray *screens = [NSScreen screens];
+	return (int)[screens count];
+}
 
-static void cstringFromCFStringRef(CFStringRef string, char *cstr, size_t clen) {
-	cstr[0] = '\0';
-	if (string != NULL) {
-		char temp[256];
-		if (CFStringGetCString(string, temp, 256, kCFStringEncodingUTF8)) {
-			temp[iron_mini(255, (int)(clen - 1))] = '\0';
-			strncpy(cstr, temp, clen);
+int iron_primary_display(void) {
+	NSArray *screens = [NSScreen screens];
+	NSScreen *mainScreen = [NSScreen mainScreen];
+	int max_displays = 8;
+	for (int i = 0; i < max_displays; ++i) {
+		if (mainScreen == screens[i]) {
+			return i;
 		}
 	}
+	return -1;
 }
 
-void HIDGamepad_init(struct HIDGamepad *gamepad) {
-	reset(gamepad);
-}
-
-void HIDGamepad_destroy(struct HIDGamepad *gamepad) {
-	HIDGamepad_unbind(gamepad);
-}
-
-void HIDGamepad_bind(struct HIDGamepad *gamepad, IOHIDDeviceRef inDeviceRef, int inPadIndex) {
-	gamepad->hidDeviceRef = inDeviceRef;
-	gamepad->padIndex = inPadIndex;
-
-	IOHIDDeviceOpen(gamepad->hidDeviceRef, kIOHIDOptionsTypeSeizeDevice);
-	IOHIDDeviceRegisterInputValueCallback(gamepad->hidDeviceRef, inputValueCallback, gamepad);
-	IOHIDDeviceScheduleWithRunLoop(gamepad->hidDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+void iron_display_init(void) {}
 
-	gamepad->hidQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, gamepad->hidDeviceRef, 32, kIOHIDOptionsTypeNone);
-	if (CFGetTypeID(gamepad->hidQueueRef) == IOHIDQueueGetTypeID()) {
-		IOHIDQueueStart(gamepad->hidQueueRef);
-		IOHIDQueueRegisterValueAvailableCallback(gamepad->hidQueueRef, valueAvailableCallback, gamepad);
-		IOHIDQueueScheduleWithRunLoop(gamepad->hidQueueRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-	}
+iron_display_mode_t iron_display_current_mode(int display) {
+	NSArray *screens = [NSScreen screens];
+	NSScreen *screen = screens[display];
+	NSRect screenRect = [screen frame];
+	iron_display_mode_t dm;
+	dm.width = screenRect.size.width;
+	dm.height = screenRect.size.height;
+	dm.frequency = 60;
+	dm.bits_per_pixel = 32;
 
-	CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(gamepad->hidDeviceRef, NULL, kIOHIDOptionsTypeNone);
-	initDeviceElements(gamepad, elementCFArrayRef);
+	NSDictionary *description = [screen deviceDescription];
+	NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
+	NSNumber *screenNumber = [description objectForKey:@"NSScreenNumber"];
+	CGSize displayPhysicalSize = CGDisplayScreenSize([screenNumber unsignedIntValue]); // in millimeters
+	double ppi = displayPixelSize.width / (displayPhysicalSize.width * 0.039370);      // Convert MM to INCH
+	dm.pixels_per_inch = round(ppi);
 
-	{
-		CFNumberRef vendorIdRef = (CFNumberRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDVendorIDKey));
-		CFNumberGetValue(vendorIdRef, kCFNumberIntType, &gamepad->hidDeviceVendorID);
+	return dm;
+}
 
-		CFNumberRef productIdRef = (CFNumberRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDProductIDKey));
-		CFNumberGetValue(productIdRef, kCFNumberIntType, &gamepad->hidDeviceProductID);
+void iron_internal_mouse_lock() {
+	iron_mouse_hide();
+}
 
-		CFStringRef vendorRef = (CFStringRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDManufacturerKey));
-		cstringFromCFStringRef(vendorRef, gamepad->hidDeviceVendor, sizeof(gamepad->hidDeviceVendor));
+void iron_internal_mouse_unlock(void) {
+	iron_mouse_show();
+}
 
-		CFStringRef productRef = (CFStringRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDProductKey));
-		cstringFromCFStringRef(productRef, gamepad->hidDeviceProduct, sizeof(gamepad->hidDeviceProduct));
-	}
+bool iron_mouse_can_lock(void) {
+	return true;
 }
 
-static void initDeviceElements(struct HIDGamepad *gamepad, CFArrayRef elements) {
+void iron_mouse_show(void) {
+	CGDisplayShowCursor(kCGDirectMainDisplay);
+}
 
-	for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i) {
-		IOHIDElementRef elementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
-		IOHIDElementType elemType = IOHIDElementGetType(elementRef);
+void iron_mouse_hide(void) {
+	CGDisplayHideCursor(kCGDirectMainDisplay);
+}
 
-		IOHIDElementCookie cookie = IOHIDElementGetCookie(elementRef);
+void iron_mouse_set_position(int x, int y) {
+	NSWindow *window = windows[0].handle;
+	float scale = [window backingScaleFactor];
+	NSRect rect = [[NSScreen mainScreen] frame];
 
-		uint32_t usagePage = IOHIDElementGetUsagePage(elementRef);
-		uint32_t usage = IOHIDElementGetUsage(elementRef);
+	CGPoint point;
+	point.x = window.frame.origin.x + (x / scale);
+	point.y = rect.size.height - (window.frame.origin.y + (y / scale));
 
-		// Match up items
-		switch (usagePage) {
-		case kHIDPage_GenericDesktop:
-			switch (usage) {
-			case kHIDUsage_GD_X: // Left stick X
-				gamepad->axis[0] = cookie;
-				break;
-			case kHIDUsage_GD_Y: // Left stick Y
-				gamepad->axis[1] = cookie;
-				break;
-			case kHIDUsage_GD_Z: // Left trigger
-				gamepad->axis[4] = cookie;
-				break;
-			case kHIDUsage_GD_Rx: // Right stick X
-				gamepad->axis[2] = cookie;
-				break;
-			case kHIDUsage_GD_Ry: // Right stick Y
-				gamepad->axis[3] = cookie;
-				break;
-			case kHIDUsage_GD_Rz: // Right trigger
-				gamepad->axis[5] = cookie;
-				break;
-			case kHIDUsage_GD_Hatswitch:
-				break;
-			default:
-				break;
-			}
-			break;
-		case kHIDPage_Button:
-			if ((usage >= 1) && (usage <= 15)) {
-				// Button 1-11
-				gamepad->buttons[usage - 1] = cookie;
-			}
-			break;
-		default:
-			break;
-		}
+	CGDisplayMoveCursorToPoint(0, point);
+	CGAssociateMouseAndMouseCursorPosition(true);
+}
 
-		if (elemType == kIOHIDElementTypeInput_Misc || elemType == kIOHIDElementTypeInput_Button || elemType == kIOHIDElementTypeInput_Axis) {
-			if (!IOHIDQueueContainsElement(gamepad->hidQueueRef, elementRef))
-				IOHIDQueueAddElement(gamepad->hidQueueRef, elementRef);
-		}
-	}
+void iron_mouse_get_position(int *x, int *y) {
+	NSWindow *window = windows[0].handle;
+	NSPoint point = [window mouseLocationOutsideOfEventStream];
+	*x = (int)point.x;
+	*y = (int)point.y;
 }
 
-void HIDGamepad_unbind(struct HIDGamepad *gamepad) {
-	if (gamepad->hidQueueRef) {
-		IOHIDQueueStop(gamepad->hidQueueRef);
-		IOHIDQueueUnscheduleFromRunLoop(gamepad->hidQueueRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-	}
-	if (gamepad->hidDeviceRef) {
-		IOHIDDeviceUnscheduleFromRunLoop(gamepad->hidDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-		IOHIDDeviceClose(gamepad->hidDeviceRef, kIOHIDOptionsTypeSeizeDevice);
-	}
-	reset(gamepad);
+void iron_mouse_set_cursor(int cursor_index) {
 }
 
-static void reset(struct HIDGamepad *gamepad) {
-	gamepad->padIndex = -1;
-	gamepad->hidDeviceRef = NULL;
-	gamepad->hidQueueRef = NULL;
-	gamepad->hidDeviceVendor[0] = '\0';
-	gamepad->hidDeviceProduct[0] = '\0';
-	gamepad->hidDeviceVendorID = 0;
-	gamepad->hidDeviceProductID = 0;
-	memset(gamepad->axis, 0, sizeof(gamepad->axis));
-	memset(gamepad->buttons, 0, sizeof(gamepad->buttons));
+void iron_keyboard_show(void) {
+	keyboardShown = true;
 }
 
-static void buttonChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int buttonIndex) {
-	double rawValue = IOHIDValueGetScaledValue(valueRef, kIOHIDValueScaleTypePhysical);
-	double min = IOHIDElementGetLogicalMin(elementRef);
-	double max = IOHIDElementGetLogicalMax(elementRef);
-	double normalize = (rawValue - min) / (max - min);
-	iron_internal_gamepad_trigger_button(gamepad->padIndex, buttonIndex, normalize);
+void iron_keyboard_hide(void) {
+	keyboardShown = false;
 }
 
-static void axisChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int axisIndex) {
-	double rawValue = IOHIDValueGetScaledValue(valueRef, kIOHIDValueScaleTypePhysical);
-	double min = IOHIDElementGetPhysicalMin(elementRef);
-	double max = IOHIDElementGetPhysicalMax(elementRef);
-	double normalize = normalize = (((rawValue - min) / (max - min)) * 2) - 1;
-	if (axisIndex % 2 == 1) {
-		normalize = -normalize;
-	}
-	iron_internal_gamepad_trigger_axis(gamepad->padIndex, axisIndex, normalize);
+bool iron_keyboard_active(void) {
+	return keyboardShown;
 }
 
-static void inputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef) {}
+const char *iron_system_id(void) {
+	return "macOS";
+}
 
-static void valueAvailableCallback(void *inContext, IOReturn inResult, void *inSender) {
-	struct HIDGamepad *pad = (struct HIDGamepad *)inContext;
-	do {
-		IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout((IOHIDQueueRef)inSender, 0.);
-		if (!valueRef) {
-			break;
-		}
+const char **iron_video_formats(void) {
+	return videoFormats;
+}
 
-		IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef);
-		IOHIDElementCookie cookie = IOHIDElementGetCookie(elementRef);
+void iron_set_keep_screen_on(bool on) {}
 
-		for (int i = 0, c = sizeof(pad->buttons); i < c; ++i) {
-			if (cookie == pad->buttons[i]) {
-				buttonChanged(pad, elementRef, valueRef, i);
-				break;
-			}
-		}
+double iron_frequency(void) {
+	mach_timebase_info_data_t info;
+	mach_timebase_info(&info);
+	return (double)info.denom / (double)info.numer / 1e-9;
+}
 
-		for (int i = 0, c = sizeof(pad->axis); i < c; ++i) {
-			if (cookie == pad->axis[i]) {
-				axisChanged(pad, elementRef, valueRef, i);
-				break;
-			}
-		}
+uint64_t iron_timestamp(void) {
+	return mach_absolute_time();
+}
 
-		CFRelease(valueRef);
-	} while (1);
+bool with_autoreleasepool(bool (*f)(void)) {
+	@autoreleasepool {
+		return f();
+	}
 }
 
-const char *iron_gamepad_vendor(int gamepad) {
-	return "unknown";
+const char *iron_get_resource_path(void) {
+	return [[[NSBundle mainBundle] resourcePath] cStringUsingEncoding:NSUTF8StringEncoding];
 }
 
-const char *iron_gamepad_product_name(int gamepad) {
-	return "unknown";
+@interface IronApplication : NSApplication {
 }
-
-static int initHIDManager(struct HIDManager *manager);
-static bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef);
-static CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage);
-static void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
-static void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
-
-void HIDManager_init(struct HIDManager *manager) {
-	manager->managerRef = 0x0;
-	initHIDManager(manager);
-}
-
-void HIDManager_destroy(struct HIDManager *manager) {
-	if (manager->managerRef) {
-		IOHIDManagerUnscheduleFromRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-		IOHIDManagerClose(manager->managerRef, kIOHIDOptionsTypeNone);
-	}
-}
-
-static int initHIDManager(struct HIDManager *manager) {
-	manager->managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
-	if (CFGetTypeID(manager->managerRef) == IOHIDManagerGetTypeID()) {
-
-		CFMutableArrayRef matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
-		if (matchingCFArrayRef) {
-			CFDictionaryRef matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
-			addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef);
-			matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
-			addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef);
-		}
-		else {
-			iron_error("%s: CFArrayCreateMutable failed.", __PRETTY_FUNCTION__);
-			return -1;
-		}
-
-		IOHIDManagerSetDeviceMatchingMultiple(manager->managerRef, matchingCFArrayRef);
-		CFRelease(matchingCFArrayRef);
-		IOHIDManagerOpen(manager->managerRef, kIOHIDOptionsTypeNone);
-		IOHIDManagerRegisterDeviceMatchingCallback(manager->managerRef, deviceConnected, manager);
-		IOHIDManagerRegisterDeviceRemovalCallback(manager->managerRef, deviceRemoved, manager);
-		IOHIDManagerScheduleWithRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-		return 0;
-	}
-	return -1;
-}
-
-bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef) {
-	if (matchingCFDictRef) {
-		CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef);
-		CFRelease(matchingCFDictRef);
-		return true;
-	}
-	return false;
-}
-
-CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage) {
-	CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-	if (result) {
-		if (inUsagePage) {
-			CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage);
-			if (pageCFNumberRef) {
-				CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsagePageKey), pageCFNumberRef);
-				CFRelease(pageCFNumberRef);
-				if (inUsage) {
-					CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
-					if (usageCFNumberRef) {
-						CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
-						CFRelease(usageCFNumberRef);
-					}
-				}
-			}
-		}
-	}
-	return result;
-}
-
-void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
-	struct HIDManager *manager = (struct HIDManager *)inContext;
-	struct HIDManagerDeviceRecord *device = &manager->devices[0];
-	for (int i = 0; i < IRON_MAX_HID_DEVICES; ++i, ++device) {
-		if (!device->connected) {
-			device->connected = true;
-			device->device = inIOHIDDeviceRef;
-			HIDGamepad_bind(&device->pad, inIOHIDDeviceRef, i);
-			break;
-		}
-	}
-}
-
-void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
-	struct HIDManager *manager = (struct HIDManager *)inContext;
-	struct HIDManagerDeviceRecord *device = &manager->devices[0];
-	for (int i = 0; i < IRON_MAX_HID_DEVICES; ++i, ++device) {
-		if (device->connected && device->device == inIOHIDDeviceRef) {
-			device->connected = false;
-			device->device = NULL;
-			HIDGamepad_unbind(&device->pad);
-			break;
-		}
-	}
-}
-
-#endif
-
-int iron_count_displays(void) {
-	NSArray *screens = [NSScreen screens];
-	return (int)[screens count];
-}
-
-int iron_primary_display(void) {
-	NSArray *screens = [NSScreen screens];
-	NSScreen *mainScreen = [NSScreen mainScreen];
-	int max_displays = 8;
-	for (int i = 0; i < max_displays; ++i) {
-		if (mainScreen == screens[i]) {
-			return i;
-		}
-	}
-	return -1;
-}
-
-void iron_display_init(void) {}
-
-iron_display_mode_t iron_display_current_mode(int display) {
-	NSArray *screens = [NSScreen screens];
-	NSScreen *screen = screens[display];
-	NSRect screenRect = [screen frame];
-	iron_display_mode_t dm;
-	dm.width = screenRect.size.width;
-	dm.height = screenRect.size.height;
-	dm.frequency = 60;
-	dm.bits_per_pixel = 32;
-
-	NSDictionary *description = [screen deviceDescription];
-	NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
-	NSNumber *screenNumber = [description objectForKey:@"NSScreenNumber"];
-	CGSize displayPhysicalSize = CGDisplayScreenSize([screenNumber unsignedIntValue]); // in millimeters
-	double ppi = displayPixelSize.width / (displayPhysicalSize.width * 0.039370);      // Convert MM to INCH
-	dm.pixels_per_inch = round(ppi);
-
-	return dm;
-}
-
-void iron_internal_mouse_lock() {
-	iron_mouse_hide();
-}
-
-void iron_internal_mouse_unlock(void) {
-	iron_mouse_show();
-}
-
-bool iron_mouse_can_lock(void) {
-	return true;
-}
-
-void iron_mouse_show(void) {
-	CGDisplayShowCursor(kCGDirectMainDisplay);
-}
-
-void iron_mouse_hide(void) {
-	CGDisplayHideCursor(kCGDirectMainDisplay);
-}
-
-void iron_mouse_set_position(int x, int y) {
-	NSWindow *window = windows[0].handle;
-	float scale = [window backingScaleFactor];
-	NSRect rect = [[NSScreen mainScreen] frame];
-
-	CGPoint point;
-	point.x = window.frame.origin.x + (x / scale);
-	point.y = rect.size.height - (window.frame.origin.y + (y / scale));
-
-	CGDisplayMoveCursorToPoint(0, point);
-	CGAssociateMouseAndMouseCursorPosition(true);
-}
-
-void iron_mouse_get_position(int *x, int *y) {
-	NSWindow *window = windows[0].handle;
-	NSPoint point = [window mouseLocationOutsideOfEventStream];
-	*x = (int)point.x;
-	*y = (int)point.y;
-}
-
-void iron_mouse_set_cursor(int cursor_index) {}
-
-void iron_keyboard_show(void) {
-	keyboardShown = true;
-}
-
-void iron_keyboard_hide(void) {
-	keyboardShown = false;
-}
-
-bool iron_keyboard_active(void) {
-	return keyboardShown;
-}
-
-const char *iron_system_id(void) {
-	return "macOS";
-}
-
-const char **iron_video_formats(void) {
-	return videoFormats;
-}
-
-void iron_set_keep_screen_on(bool on) {}
-
-double iron_frequency(void) {
-	mach_timebase_info_data_t info;
-	mach_timebase_info(&info);
-	return (double)info.denom / (double)info.numer / 1e-9;
-}
-
-uint64_t iron_timestamp(void) {
-	return mach_absolute_time();
-}
-
-#ifdef WITH_GAMEPAD
-
-bool iron_gamepad_connected(int num) {
-	return true;
-}
-
-void iron_gamepad_rumble(int gamepad, float left, float right) {}
-
-#endif
-
-bool with_autoreleasepool(bool (*f)(void)) {
-	@autoreleasepool {
-		return f();
-	}
-}
-
-const char *iron_get_resource_path(void) {
-	return [[[NSBundle mainBundle] resourcePath] cStringUsingEncoding:NSUTF8StringEncoding];
-}
-
-@interface IronApplication : NSApplication {
-}
-- (void)terminate:(id)sender;
-@end
+- (void)terminate:(id)sender;
+@end
 
 @interface IronAppDelegate : NSObject <NSWindowDelegate> {
 }
@@ -1178,3 +862,313 @@ iron_window_mode_t iron_window_get_mode() {
 int iron_window_display() {
 	return 0;
 }
+
+#ifdef WITH_GAMEPAD
+
+static struct HIDManager *hidManager;
+
+bool iron_gamepad_connected(int num) {
+	return true;
+}
+
+void iron_gamepad_rumble(int gamepad, float left, float right) {}
+
+static void inputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef);
+static void valueAvailableCallback(void *inContext, IOReturn inResult, void *inSender);
+static void reset(struct HIDGamepad *gamepad);
+static void initDeviceElements(struct HIDGamepad *gamepad, CFArrayRef elements);
+static void buttonChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int buttonIndex);
+static void axisChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int axisIndex);
+
+static void cstringFromCFStringRef(CFStringRef string, char *cstr, size_t clen) {
+	cstr[0] = '\0';
+	if (string != NULL) {
+		char temp[256];
+		if (CFStringGetCString(string, temp, 256, kCFStringEncodingUTF8)) {
+			temp[iron_mini(255, (int)(clen - 1))] = '\0';
+			strncpy(cstr, temp, clen);
+		}
+	}
+}
+
+void HIDGamepad_init(struct HIDGamepad *gamepad) {
+	reset(gamepad);
+}
+
+void HIDGamepad_destroy(struct HIDGamepad *gamepad) {
+	HIDGamepad_unbind(gamepad);
+}
+
+void HIDGamepad_bind(struct HIDGamepad *gamepad, IOHIDDeviceRef inDeviceRef, int inPadIndex) {
+	gamepad->hidDeviceRef = inDeviceRef;
+	gamepad->padIndex = inPadIndex;
+
+	IOHIDDeviceOpen(gamepad->hidDeviceRef, kIOHIDOptionsTypeSeizeDevice);
+	IOHIDDeviceRegisterInputValueCallback(gamepad->hidDeviceRef, inputValueCallback, gamepad);
+	IOHIDDeviceScheduleWithRunLoop(gamepad->hidDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+	gamepad->hidQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, gamepad->hidDeviceRef, 32, kIOHIDOptionsTypeNone);
+	if (CFGetTypeID(gamepad->hidQueueRef) == IOHIDQueueGetTypeID()) {
+		IOHIDQueueStart(gamepad->hidQueueRef);
+		IOHIDQueueRegisterValueAvailableCallback(gamepad->hidQueueRef, valueAvailableCallback, gamepad);
+		IOHIDQueueScheduleWithRunLoop(gamepad->hidQueueRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+	}
+
+	CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(gamepad->hidDeviceRef, NULL, kIOHIDOptionsTypeNone);
+	initDeviceElements(gamepad, elementCFArrayRef);
+
+	{
+		CFNumberRef vendorIdRef = (CFNumberRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDVendorIDKey));
+		CFNumberGetValue(vendorIdRef, kCFNumberIntType, &gamepad->hidDeviceVendorID);
+
+		CFNumberRef productIdRef = (CFNumberRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDProductIDKey));
+		CFNumberGetValue(productIdRef, kCFNumberIntType, &gamepad->hidDeviceProductID);
+
+		CFStringRef vendorRef = (CFStringRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDManufacturerKey));
+		cstringFromCFStringRef(vendorRef, gamepad->hidDeviceVendor, sizeof(gamepad->hidDeviceVendor));
+
+		CFStringRef productRef = (CFStringRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDProductKey));
+		cstringFromCFStringRef(productRef, gamepad->hidDeviceProduct, sizeof(gamepad->hidDeviceProduct));
+	}
+}
+
+static void initDeviceElements(struct HIDGamepad *gamepad, CFArrayRef elements) {
+
+	for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i) {
+		IOHIDElementRef elementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+		IOHIDElementType elemType = IOHIDElementGetType(elementRef);
+
+		IOHIDElementCookie cookie = IOHIDElementGetCookie(elementRef);
+
+		uint32_t usagePage = IOHIDElementGetUsagePage(elementRef);
+		uint32_t usage = IOHIDElementGetUsage(elementRef);
+
+		// Match up items
+		switch (usagePage) {
+		case kHIDPage_GenericDesktop:
+			switch (usage) {
+			case kHIDUsage_GD_X: // Left stick X
+				gamepad->axis[0] = cookie;
+				break;
+			case kHIDUsage_GD_Y: // Left stick Y
+				gamepad->axis[1] = cookie;
+				break;
+			case kHIDUsage_GD_Z: // Left trigger
+				gamepad->axis[4] = cookie;
+				break;
+			case kHIDUsage_GD_Rx: // Right stick X
+				gamepad->axis[2] = cookie;
+				break;
+			case kHIDUsage_GD_Ry: // Right stick Y
+				gamepad->axis[3] = cookie;
+				break;
+			case kHIDUsage_GD_Rz: // Right trigger
+				gamepad->axis[5] = cookie;
+				break;
+			case kHIDUsage_GD_Hatswitch:
+				break;
+			default:
+				break;
+			}
+			break;
+		case kHIDPage_Button:
+			if ((usage >= 1) && (usage <= 15)) {
+				// Button 1-11
+				gamepad->buttons[usage - 1] = cookie;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (elemType == kIOHIDElementTypeInput_Misc || elemType == kIOHIDElementTypeInput_Button || elemType == kIOHIDElementTypeInput_Axis) {
+			if (!IOHIDQueueContainsElement(gamepad->hidQueueRef, elementRef))
+				IOHIDQueueAddElement(gamepad->hidQueueRef, elementRef);
+		}
+	}
+}
+
+void HIDGamepad_unbind(struct HIDGamepad *gamepad) {
+	if (gamepad->hidQueueRef) {
+		IOHIDQueueStop(gamepad->hidQueueRef);
+		IOHIDQueueUnscheduleFromRunLoop(gamepad->hidQueueRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+	}
+	if (gamepad->hidDeviceRef) {
+		IOHIDDeviceUnscheduleFromRunLoop(gamepad->hidDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+		IOHIDDeviceClose(gamepad->hidDeviceRef, kIOHIDOptionsTypeSeizeDevice);
+	}
+	reset(gamepad);
+}
+
+static void reset(struct HIDGamepad *gamepad) {
+	gamepad->padIndex = -1;
+	gamepad->hidDeviceRef = NULL;
+	gamepad->hidQueueRef = NULL;
+	gamepad->hidDeviceVendor[0] = '\0';
+	gamepad->hidDeviceProduct[0] = '\0';
+	gamepad->hidDeviceVendorID = 0;
+	gamepad->hidDeviceProductID = 0;
+	memset(gamepad->axis, 0, sizeof(gamepad->axis));
+	memset(gamepad->buttons, 0, sizeof(gamepad->buttons));
+}
+
+static void buttonChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int buttonIndex) {
+	double rawValue = IOHIDValueGetScaledValue(valueRef, kIOHIDValueScaleTypePhysical);
+	double min = IOHIDElementGetLogicalMin(elementRef);
+	double max = IOHIDElementGetLogicalMax(elementRef);
+	double normalize = (rawValue - min) / (max - min);
+	iron_internal_gamepad_trigger_button(gamepad->padIndex, buttonIndex, normalize);
+}
+
+static void axisChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int axisIndex) {
+	double rawValue = IOHIDValueGetScaledValue(valueRef, kIOHIDValueScaleTypePhysical);
+	double min = IOHIDElementGetPhysicalMin(elementRef);
+	double max = IOHIDElementGetPhysicalMax(elementRef);
+	double normalize = normalize = (((rawValue - min) / (max - min)) * 2) - 1;
+	if (axisIndex % 2 == 1) {
+		normalize = -normalize;
+	}
+	iron_internal_gamepad_trigger_axis(gamepad->padIndex, axisIndex, normalize);
+}
+
+static void inputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef) {}
+
+static void valueAvailableCallback(void *inContext, IOReturn inResult, void *inSender) {
+	struct HIDGamepad *pad = (struct HIDGamepad *)inContext;
+	do {
+		IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout((IOHIDQueueRef)inSender, 0.);
+		if (!valueRef) {
+			break;
+		}
+
+		IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef);
+		IOHIDElementCookie cookie = IOHIDElementGetCookie(elementRef);
+
+		for (int i = 0, c = sizeof(pad->buttons); i < c; ++i) {
+			if (cookie == pad->buttons[i]) {
+				buttonChanged(pad, elementRef, valueRef, i);
+				break;
+			}
+		}
+
+		for (int i = 0, c = sizeof(pad->axis); i < c; ++i) {
+			if (cookie == pad->axis[i]) {
+				axisChanged(pad, elementRef, valueRef, i);
+				break;
+			}
+		}
+
+		CFRelease(valueRef);
+	} while (1);
+}
+
+const char *iron_gamepad_vendor(int gamepad) {
+	return "unknown";
+}
+
+const char *iron_gamepad_product_name(int gamepad) {
+	return "unknown";
+}
+
+static int initHIDManager(struct HIDManager *manager);
+static bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef);
+static CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage);
+static void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
+static void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
+
+void HIDManager_init(struct HIDManager *manager) {
+	manager->managerRef = 0x0;
+	initHIDManager(manager);
+}
+
+void HIDManager_destroy(struct HIDManager *manager) {
+	if (manager->managerRef) {
+		IOHIDManagerUnscheduleFromRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+		IOHIDManagerClose(manager->managerRef, kIOHIDOptionsTypeNone);
+	}
+}
+
+static int initHIDManager(struct HIDManager *manager) {
+	manager->managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+	if (CFGetTypeID(manager->managerRef) == IOHIDManagerGetTypeID()) {
+
+		CFMutableArrayRef matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+		if (matchingCFArrayRef) {
+			CFDictionaryRef matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
+			addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef);
+			matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
+			addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef);
+		}
+		else {
+			iron_error("%s: CFArrayCreateMutable failed.", __PRETTY_FUNCTION__);
+			return -1;
+		}
+
+		IOHIDManagerSetDeviceMatchingMultiple(manager->managerRef, matchingCFArrayRef);
+		CFRelease(matchingCFArrayRef);
+		IOHIDManagerOpen(manager->managerRef, kIOHIDOptionsTypeNone);
+		IOHIDManagerRegisterDeviceMatchingCallback(manager->managerRef, deviceConnected, manager);
+		IOHIDManagerRegisterDeviceRemovalCallback(manager->managerRef, deviceRemoved, manager);
+		IOHIDManagerScheduleWithRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+		return 0;
+	}
+	return -1;
+}
+
+bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef) {
+	if (matchingCFDictRef) {
+		CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef);
+		CFRelease(matchingCFDictRef);
+		return true;
+	}
+	return false;
+}
+
+CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage) {
+	CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+	if (result) {
+		if (inUsagePage) {
+			CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage);
+			if (pageCFNumberRef) {
+				CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsagePageKey), pageCFNumberRef);
+				CFRelease(pageCFNumberRef);
+				if (inUsage) {
+					CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
+					if (usageCFNumberRef) {
+						CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
+						CFRelease(usageCFNumberRef);
+					}
+				}
+			}
+		}
+	}
+	return result;
+}
+
+void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
+	struct HIDManager *manager = (struct HIDManager *)inContext;
+	struct HIDManagerDeviceRecord *device = &manager->devices[0];
+	for (int i = 0; i < IRON_MAX_HID_DEVICES; ++i, ++device) {
+		if (!device->connected) {
+			device->connected = true;
+			device->device = inIOHIDDeviceRef;
+			HIDGamepad_bind(&device->pad, inIOHIDDeviceRef, i);
+			break;
+		}
+	}
+}
+
+void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
+	struct HIDManager *manager = (struct HIDManager *)inContext;
+	struct HIDManagerDeviceRecord *device = &manager->devices[0];
+	for (int i = 0; i < IRON_MAX_HID_DEVICES; ++i, ++device) {
+		if (device->connected && device->device == inIOHIDDeviceRef) {
+			device->connected = false;
+			device->device = NULL;
+			HIDGamepad_unbind(&device->pad);
+			break;
+		}
+	}
+}
+
+#endif