1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
#import "JOYElement.h"
#import <IOKit/hid/IOHIDLib.h>
#import <objc/runtime.h>
@implementation JOYElement
{
id _element;
IOHIDDeviceRef _device;
int32_t _min, _max;
}
- (int32_t)min
{
return MIN(_min, _max);
}
- (int32_t)max
{
return MAX(_max, _min);
}
-(void)setMin:(int32_t)min
{
_min = min;
}
- (void)setMax:(int32_t)max
{
_max = max;
}
/* Ugly hack because IOHIDDeviceCopyMatchingElements is slow */
+ (NSArray *) cookiesToSkipForDevice:(IOHIDDeviceRef)device
{
id _device = (__bridge id)device;
NSMutableArray *ret = objc_getAssociatedObject(_device, _cmd);
if (ret) return ret;
ret = [NSMutableArray array];
NSArray *nones = CFBridgingRelease(IOHIDDeviceCopyMatchingElements(device,
(__bridge CFDictionaryRef)@{@(kIOHIDElementTypeKey): @(kIOHIDElementTypeInput_NULL)},
0));
for (id none in nones) {
[ret addObject:@(IOHIDElementGetCookie((__bridge IOHIDElementRef)none))];
}
objc_setAssociatedObject(_device, _cmd, ret, OBJC_ASSOCIATION_RETAIN);
return ret;
}
- (instancetype)initWithElement:(IOHIDElementRef)element
{
if ((self = [super init])) {
_element = (__bridge id)element;
_usage = IOHIDElementGetUsage(element);
_usagePage = IOHIDElementGetUsagePage(element);
_uniqueID = (uint32_t)IOHIDElementGetCookie(element);
_min = (int32_t) IOHIDElementGetLogicalMin(element);
_max = (int32_t) IOHIDElementGetLogicalMax(element);
_reportID = IOHIDElementGetReportID(element);
IOHIDElementRef parent = IOHIDElementGetParent(element);
_parentID = parent? (uint32_t)IOHIDElementGetCookie(parent) : -1;
_device = IOHIDElementGetDevice(element);
/* Catalina added a new input type in a way that breaks cookie consistency across macOS versions,
we shall adjust our cookies to to compensate */
unsigned cookieShift = 0, parentCookieShift = 0;
for (NSNumber *none in [JOYElement cookiesToSkipForDevice:_device]) {
if (none.unsignedIntValue < _uniqueID) {
cookieShift++;
}
if (none.unsignedIntValue < (int32_t)_parentID) {
parentCookieShift++;
}
}
_uniqueID -= cookieShift;
_parentID -= parentCookieShift;
}
return self;
}
- (int32_t)value
{
IOHIDValueRef value = NULL;
IOHIDDeviceGetValue(_device, (__bridge IOHIDElementRef)_element, &value);
if (!value) return 0;
CFRelease(CFRetain(value)); // For some reason, this is required to prevent leaks.
return (int32_t)IOHIDValueGetIntegerValue(value);
}
- (NSData *)dataValue
{
IOHIDValueRef value = NULL;
IOHIDDeviceGetValue(_device, (__bridge IOHIDElementRef)_element, &value);
if (!value) return 0;
CFRelease(CFRetain(value)); // For some reason, this is required to prevent leaks.
return [NSData dataWithBytes:IOHIDValueGetBytePtr(value) length:IOHIDValueGetLength(value)];
}
- (IOReturn)setValue:(uint32_t)value
{
IOHIDValueRef ivalue = IOHIDValueCreateWithIntegerValue(NULL, (__bridge IOHIDElementRef)_element, 0, value);
IOReturn ret = IOHIDDeviceSetValue(_device, (__bridge IOHIDElementRef)_element, ivalue);
CFRelease(ivalue);
return ret;
}
- (IOReturn)setDataValue:(NSData *)value
{
IOHIDValueRef ivalue = IOHIDValueCreateWithBytes(NULL, (__bridge IOHIDElementRef)_element, 0, value.bytes, value.length);
IOReturn ret = IOHIDDeviceSetValue(_device, (__bridge IOHIDElementRef)_element, ivalue);
CFRelease(ivalue);
return ret;
}
/* For use as a dictionary key */
- (NSUInteger)hash
{
return self.uniqueID;
}
- (BOOL)isEqual:(id)object
{
return self->_element == object;
}
- (id)copyWithZone:(NSZone *)zone;
{
return self;
}
@end
|