hammerspoon/extensions/razer/HSRazerManager.m

218 lines
8.6 KiB
Objective-C

#import "HSRazerManager.h"
#pragma mark - IOKit C callbacks
static void HIDcallback(void* context, IOReturn result, void* sender, IOHIDValueRef value)
{
NSNumber *locationID = (__bridge NSNumber *)IOHIDDeviceGetProperty(sender, CFSTR(kIOHIDLocationIDKey));
if (!locationID) {
return;
}
HSRazerManager *manager = (__bridge HSRazerManager *)context;
for (HSRazerDevice *device in manager.devices) {
if (device.locationID == locationID) {
IOHIDElementRef elem = IOHIDValueGetElement(value);
uint32_t scancode = IOHIDElementGetUsage(elem);
long pressed = IOHIDValueGetIntegerValue(value);
if (scancode < 4 || scancode > 231) {
return;
}
NSString *scancodeString = [NSString stringWithFormat:@"%d",scancode];
[device deviceButtonPress:scancodeString pressed:pressed];
}
}
}
static void HIDconnect(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {
//NSLog(@"[hs.razer] connect: %p:%p", context, (void *)device);
HSRazerManager *manager = (__bridge HSRazerManager *)context;
[manager deviceDidConnect:device];
}
static void HIDdisconnect(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {
//NSLog(@"[hs.razer] disconnect: %p", (void *)device);
HSRazerManager *manager = (__bridge HSRazerManager *)context;
[manager deviceDidDisconnect:device];
IOHIDDeviceRegisterInputValueCallback(device, NULL, NULL);
}
#pragma mark - Razer Manager implementation
@implementation HSRazerManager
- (id)init {
self = [super init];
if (self) {
self.devices = [[NSMutableArray alloc] initWithCapacity:1];
self.discoveryCallbackRef = LUA_NOREF;
// Create a HID device manager
self.ioHIDManager = CFBridgingRelease(IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone));
//NSLog(@"[hs.razer] Created HID Manager: %p", (void *)self.ioHIDManager);
// Configure the HID manager to match against Razer devices:
NSString *vendorIDKey = @(kIOHIDVendorIDKey);
NSString *productIDKey = @(kIOHIDProductIDKey);
NSDictionary *matchTartarusV2 = @{vendorIDKey: @USB_VID_RAZER,
productIDKey: @USB_PID_RAZER_TARTARUS_V2};
IOHIDManagerSetDeviceMatchingMultiple((__bridge IOHIDManagerRef)self.ioHIDManager,
(__bridge CFArrayRef)@[matchTartarusV2]);
// Add our callbacks for relevant events:
IOHIDManagerRegisterDeviceMatchingCallback((__bridge IOHIDManagerRef)self.ioHIDManager,
HIDconnect,
(__bridge void*)self);
IOHIDManagerRegisterDeviceRemovalCallback((__bridge IOHIDManagerRef)self.ioHIDManager,
HIDdisconnect,
(__bridge void*)self);
IOHIDManagerRegisterInputValueCallback((__bridge IOHIDManagerRef)self.ioHIDManager,
HIDcallback,
(__bridge void*)self);
// Start our HID manager:
IOHIDManagerScheduleWithRunLoop((__bridge IOHIDManagerRef)self.ioHIDManager,
CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
}
return self;
}
- (void)doGC {
if (!(__bridge IOHIDManagerRef)self.ioHIDManager) {
// Something is wrong and the manager doesn't exist, so just bail:
return;
}
// Remove our callbacks:
IOHIDManagerRegisterDeviceMatchingCallback((__bridge IOHIDManagerRef)self.ioHIDManager, NULL, (__bridge void*)self);
IOHIDManagerRegisterDeviceRemovalCallback((__bridge IOHIDManagerRef)self.ioHIDManager, NULL, (__bridge void*)self);
// Remove our HID manager from the runloop:
IOHIDManagerUnscheduleFromRunLoop((__bridge IOHIDManagerRef)self.ioHIDManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
// Deallocate the HID manager:
self.ioHIDManager = nil;
}
- (BOOL)startHIDManager {
IOReturn tIOReturn = IOHIDManagerOpen((__bridge IOHIDManagerRef)self.ioHIDManager, kIOHIDOptionsTypeNone);
return tIOReturn == kIOReturnSuccess;
}
- (BOOL)stopHIDManager {
if (!(__bridge IOHIDManagerRef)self.ioHIDManager) {
return YES;
}
IOReturn tIOReturn = IOHIDManagerClose((__bridge IOHIDManagerRef)self.ioHIDManager, kIOHIDOptionsTypeNone);
return tIOReturn == kIOReturnSuccess;
}
- (HSRazerDevice*)deviceDidConnect:(IOHIDDeviceRef)device {
/*
Handy Resources:
- HID Device Property Keys
https://developer.apple.com/documentation/iokit/iohidkeys_h_user-space/hid_device_property_keys
*/
NSNumber *vendorID = (__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
NSNumber *productID = (__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
NSNumber *locationID = (__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
// Make sure the vendor is Razer:
if (vendorID.intValue != USB_VID_RAZER) {
//NSLog(@"[hs.razer] deviceDidConnect from unknown vendor: %d", vendorID.intValue);
return nil;
}
HSRazerDevice *razerDevice = nil;
BOOL alreadyRegistered = NO;
// Make sure the product ID matches:
switch (productID.intValue) {
case USB_PID_RAZER_TARTARUS_V2:
// We only want to register each device once, as they might have multiple
// HID objects for the same physical hardware:
alreadyRegistered = NO;
for (HSRazerDevice *checkDevice in self.devices) {
if (checkDevice.locationID == locationID) {
alreadyRegistered = YES;
}
}
if (!alreadyRegistered) {
//NSLog(@"[hs.razer] Razer Tartarus V2 detected.");
razerDevice = [[HSRazerTartarusV2Device alloc] initWithDevice:device manager:self];
// Save the location ID for making sure we're communicating with the right hardware
// when changing LED backlights:
razerDevice.locationID = locationID;
// Setup Event Tap:
[razerDevice setupEventTap];
break;
}
default:
//NSLog(@"[hs.razer] deviceDidConnect from unknown device: %d", productID.intValue);
break;
}
if (!razerDevice) {
//NSLog(@"[hs.razer] deviceDidConnect: no HSRazerDevice was created, ignoring");
return nil;
}
[self.devices addObject:razerDevice];
LuaSkin *skin = [LuaSkin sharedWithState:NULL];
razerDevice.lsCanary = [skin createGCCanary];
_lua_stackguard_entry(skin.L);
if (self.discoveryCallbackRef == LUA_NOREF || self.discoveryCallbackRef == LUA_REFNIL) {
[skin logWarn:@"hs.razer detected a device connecting, but no discovery callback has been set. See hs.razer.discoveryCallback()"];
} else {
[skin pushLuaRef:razerRefTable ref:self.discoveryCallbackRef];
lua_pushboolean(skin.L, 1);
[skin pushNSObject:razerDevice];
[skin protectedCallAndError:@"hs.razer:deviceDidConnect" nargs:2 nresults:0];
}
//NSLog(@"Created Razer device: %p", (__bridge void*)deviceId);
//NSLog(@"[hs.razer] Now have %lu devices", self.devices.count);
_lua_stackguard_exit(skin.L);
return razerDevice;
}
- (void)deviceDidDisconnect:(IOHIDDeviceRef)device {
for (HSRazerDevice *razerDevice in self.devices) {
if (razerDevice.device == device) {
[razerDevice invalidate];
LuaSkin *skin = [LuaSkin sharedWithState:NULL];
_lua_stackguard_entry(skin.L);
if (self.discoveryCallbackRef == LUA_NOREF || self.discoveryCallbackRef == LUA_REFNIL) {
[skin logWarn:@"hs.razer detected a device disconnecting, but no callback has been set. See hs.razer.discoveryCallback()"];
} else {
[skin pushLuaRef:razerRefTable ref:self.discoveryCallbackRef];
lua_pushboolean(skin.L, 0);
[skin pushNSObject:razerDevice];
[skin protectedCallAndError:@"hs.razer:deviceDidDisconnect" nargs:2 nresults:0];
}
[self.devices removeObject:razerDevice];
_lua_stackguard_exit(skin.L);
return;
}
}
//NSLog(@"[hs.razer] ERROR: A Razer was disconnected that we didn't know about");
return;
}
@end