Actually, don’t use rumble threads at all, because IOHIDDeviceSetReport seems to queue stuff despite being blocking

This commit is contained in:
Lior Halphon 2020-05-30 22:18:32 +03:00
parent af5cb72edc
commit c9b401135f
2 changed files with 85 additions and 125 deletions

View File

@ -272,9 +272,7 @@
- (void)setRumble:(double)amp - (void)setRumble:(double)amp
{ {
dispatch_async(dispatch_get_main_queue(), ^{
[lastController setRumbleAmplitude:amp]; [lastController setRumbleAmplitude:amp];
});
} }
- (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis - (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis

View File

@ -39,8 +39,6 @@ static bool axesEmulateButtons = false;
static bool axes2DEmulateButtons = false; static bool axes2DEmulateButtons = false;
static bool hatsEmulateButtons = false; static bool hatsEmulateButtons = false;
static NSLock *globalRumbleThreadLock;
@interface JOYController () @interface JOYController ()
+ (void)controllerAdded:(IOHIDDeviceRef) device; + (void)controllerAdded:(IOHIDDeviceRef) device;
+ (void)controllerRemoved:(IOHIDDeviceRef) device; + (void)controllerRemoved:(IOHIDDeviceRef) device;
@ -95,8 +93,10 @@ static void HIDInput(void *context, IOReturn result, void *sender, IOHIDValueRef
static void HIDReport(void *context, IOReturn result, void *sender, IOHIDReportType type, static void HIDReport(void *context, IOReturn result, void *sender, IOHIDReportType type,
uint32_t reportID, uint8_t *report, CFIndex reportLength) uint32_t reportID, uint8_t *report, CFIndex reportLength)
{ {
if (reportLength) {
[(__bridge JOYController *)context gotReport:[[NSData alloc] initWithBytesNoCopy:report length:reportLength freeWhenDone:NO]]; [(__bridge JOYController *)context gotReport:[[NSData alloc] initWithBytesNoCopy:report length:reportLength freeWhenDone:NO]];
} }
}
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
uint8_t reportID; uint8_t reportID;
@ -152,12 +152,9 @@ typedef union {
bool _isSwitch; // Does this controller use the Switch protocol? bool _isSwitch; // Does this controller use the Switch protocol?
bool _isDualShock3; // Does this controller use DS3 outputs? bool _isDualShock3; // Does this controller use DS3 outputs?
JOYVendorSpecificOutput _lastVendorSpecificOutput; JOYVendorSpecificOutput _lastVendorSpecificOutput;
NSLock *_rumbleThreadLock; volatile double _rumbleAmplitude;
volatile double _rumblePWMRatio;
bool _physicallyConnected; bool _physicallyConnected;
bool _logicallyConnected; bool _logicallyConnected;
bool _rumbleThreadRunning;
volatile bool _forceStopRumbleThread;
NSDictionary *_hacks; NSDictionary *_hacks;
NSMutableData *_lastReport; NSMutableData *_lastReport;
@ -166,7 +163,8 @@ typedef union {
JOYElement *_previousAxisElement; JOYElement *_previousAxisElement;
uint8_t _playerLEDs; uint8_t _playerLEDs;
double _sentRumbleAmp;
unsigned _rumbleCounter;
} }
- (instancetype)initWithDevice:(IOHIDDeviceRef) device hacks:(NSDictionary *)hacks - (instancetype)initWithDevice:(IOHIDDeviceRef) device hacks:(NSDictionary *)hacks
@ -358,7 +356,6 @@ typedef union {
_axes2DEmulatedButtons = [NSMutableDictionary dictionary]; _axes2DEmulatedButtons = [NSMutableDictionary dictionary];
_hatEmulatedButtons = [NSMutableDictionary dictionary]; _hatEmulatedButtons = [NSMutableDictionary dictionary];
_iokitToJOY = [NSMutableDictionary dictionary]; _iokitToJOY = [NSMutableDictionary dictionary];
_rumbleThreadLock = [[NSLock alloc] init];
//NSMutableArray *axes3d = [NSMutableArray array]; //NSMutableArray *axes3d = [NSMutableArray array];
@ -368,10 +365,6 @@ typedef union {
_isDualShock3 = [_hacks[JOYIsDualShock3] boolValue]; _isDualShock3 = [_hacks[JOYIsDualShock3] boolValue];
NSDictionary *customReports = hacks[JOYCustomReports]; NSDictionary *customReports = hacks[JOYCustomReports];
if (hacks[JOYCustomReports]) {
_multiElements = [NSMutableDictionary dictionary];
_fullReportElements = [NSMutableDictionary dictionary];
_lastReport = [NSMutableData dataWithLength:MAX( _lastReport = [NSMutableData dataWithLength:MAX(
MAX( MAX(
[(__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxInputReportSizeKey)) unsignedIntValue], [(__bridge NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxInputReportSizeKey)) unsignedIntValue],
@ -381,6 +374,11 @@ typedef union {
)]; )];
IOHIDDeviceRegisterInputReportCallback(device, _lastReport.mutableBytes, _lastReport.length, HIDReport, (void *)self); IOHIDDeviceRegisterInputReportCallback(device, _lastReport.mutableBytes, _lastReport.length, HIDReport, (void *)self);
if (hacks[JOYCustomReports]) {
_multiElements = [NSMutableDictionary dictionary];
_fullReportElements = [NSMutableDictionary dictionary];
for (NSNumber *_reportID in customReports) { for (NSNumber *_reportID in customReports) {
signed reportID = [_reportID intValue]; signed reportID = [_reportID intValue];
bool isOutput = false; bool isOutput = false;
@ -555,7 +553,7 @@ typedef union {
- (void)gotReport:(NSData *)report - (void)gotReport:(NSData *)report
{ {
JOYFullReportElement *element = _fullReportElements[@(*(uint8_t *)report.bytes)]; JOYFullReportElement *element = _fullReportElements[@(*(uint8_t *)report.bytes)];
if (!element) return; if (element) {
[element updateValue:report]; [element updateValue:report];
NSArray<JOYElement *> *subElements = _multiElements[element]; NSArray<JOYElement *> *subElements = _multiElements[element];
@ -563,9 +561,10 @@ typedef union {
for (JOYElement *subElement in subElements) { for (JOYElement *subElement in subElements) {
[self _elementChanged:subElement]; [self _elementChanged:subElement];
} }
return;
} }
} }
[self updateRumble];
}
- (void)elementChanged:(IOHIDElementRef)element - (void)elementChanged:(IOHIDElementRef)element
{ {
@ -697,7 +696,6 @@ typedef union {
} }
} }
_physicallyConnected = false; _physicallyConnected = false;
[self _forceStopRumbleThread]; // Stop the rumble thread.
[exposedControllers removeObject:self]; [exposedControllers removeObject:self];
_device = nil; _device = nil;
} }
@ -731,43 +729,30 @@ typedef union {
} }
} }
- (void)rumbleThread - (void)updateRumble
{ {
unsigned rumbleCounter = 0; if (!self.connected) {
return;
}
if (_rumbleElement.max == 1 && _rumbleElement.min == 0) { if (_rumbleElement.max == 1 && _rumbleElement.min == 0) {
while (self.connected && !_forceStopRumbleThread) { double ampToSend = _rumbleCounter < round(_rumbleAmplitude * PWM_RESOLUTION);
if ([_rumbleElement setValue:rumbleCounter < round(_rumblePWMRatio * PWM_RESOLUTION)]) { if (ampToSend != _sentRumbleAmp) {
break; [_rumbleElement setValue:ampToSend];
} _sentRumbleAmp = ampToSend;
rumbleCounter += round(_rumblePWMRatio * PWM_RESOLUTION);
if (rumbleCounter >= PWM_RESOLUTION) {
rumbleCounter -= PWM_RESOLUTION;
} }
_rumbleCounter += round(_rumbleAmplitude * PWM_RESOLUTION);
if (_rumbleCounter >= PWM_RESOLUTION) {
_rumbleCounter -= PWM_RESOLUTION;
} }
} }
else { else {
while (self.connected && !_forceStopRumbleThread) { if (_rumbleAmplitude == _sentRumbleAmp) {
[_rumbleElement setValue:_rumblePWMRatio * (_rumbleElement.max - _rumbleElement.min) + _rumbleElement.min]; return;
} }
} _sentRumbleAmp = _rumbleAmplitude;
[_rumbleThreadLock lock];
[_rumbleElement setValue:0];
_rumbleThreadRunning = false;
_forceStopRumbleThread = false;
[_rumbleThreadLock unlock];
}
- (void)setRumbleAmplitude:(double)amp /* andFrequency: (double)frequency */
{
double frequency = 144; // I have no idea what I'm doing.
if (amp < 0) amp = 0;
if (amp > 1) amp = 1;
if (_isSwitch) { if (_isSwitch) {
if (amp == 0) { double frequency = 144;
amp = 1; double amp = _rumbleAmplitude;
frequency = 0;
}
uint8_t highAmp = amp * 0x64; uint8_t highAmp = amp * 0x64;
uint8_t lowAmp = amp * 0x32 + 0x40; uint8_t lowAmp = amp * 0x32 + 0x40;
@ -801,33 +786,21 @@ typedef union {
} }
else if (_isDualShock3) { else if (_isDualShock3) {
_lastVendorSpecificOutput.ds3Output.reportID = 1; _lastVendorSpecificOutput.ds3Output.reportID = 1;
_lastVendorSpecificOutput.ds3Output.rumbleLeftDuration = _lastVendorSpecificOutput.ds3Output.rumbleRightDuration = amp? 0xff : 0; _lastVendorSpecificOutput.ds3Output.rumbleLeftDuration = _lastVendorSpecificOutput.ds3Output.rumbleRightDuration = _rumbleAmplitude? 0xff : 0;
_lastVendorSpecificOutput.ds3Output.rumbleLeftStrength = _lastVendorSpecificOutput.ds3Output.rumbleRightStrength = amp * 0xff; _lastVendorSpecificOutput.ds3Output.rumbleLeftStrength = _lastVendorSpecificOutput.ds3Output.rumbleRightStrength = round(_rumbleAmplitude * 0xff);
[self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.ds3Output length:sizeof(_lastVendorSpecificOutput.ds3Output)]]; [self sendReport:[NSData dataWithBytes:&_lastVendorSpecificOutput.ds3Output length:sizeof(_lastVendorSpecificOutput.ds3Output)]];
} }
else { else {
[_rumbleThreadLock lock]; [_rumbleElement setValue:_rumbleAmplitude * (_rumbleElement.max - _rumbleElement.min) + _rumbleElement.min];
_rumblePWMRatio = amp; }
if (!_rumbleThreadRunning) { // PWM thread not running, start it. }
if (amp != 0) { }
/* TODO: The PWM thread does not handle correctly the case of having a multi-port controller where more
than one controller uses rumble. At least make sure any sibling controllers don't have their
PWM thread running. */
[globalRumbleThreadLock lock]; - (void)setRumbleAmplitude:(double)amp /* andFrequency: (double)frequency */
for (JOYController *controller in [JOYController allControllers]) { {
if (controller != self && controller->_device == _device) { if (amp < 0) amp = 0;
[controller _forceStopRumbleThread]; if (amp > 1) amp = 1;
} _rumbleAmplitude = amp;
}
_rumblePWMRatio = amp;
_rumbleThreadRunning = true;
[self performSelectorInBackground:@selector(rumbleThread) withObject:nil];
[globalRumbleThreadLock unlock];
}
}
[_rumbleThreadLock unlock];
}
} }
- (bool)isConnected - (bool)isConnected
@ -835,16 +808,6 @@ typedef union {
return _logicallyConnected && _physicallyConnected; return _logicallyConnected && _physicallyConnected;
} }
- (void)_forceStopRumbleThread
{
[_rumbleThreadLock lock];
if (_rumbleThreadRunning) {
_forceStopRumbleThread = true;
}
[_rumbleThreadLock unlock];
while (_rumbleThreadRunning);
}
+ (void)controllerAdded:(IOHIDDeviceRef) device + (void)controllerAdded:(IOHIDDeviceRef) device
{ {
NSString *name = (__bridge NSString *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); NSString *name = (__bridge NSString *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
@ -902,7 +865,6 @@ typedef union {
controllers = [NSMutableDictionary dictionary]; controllers = [NSMutableDictionary dictionary];
exposedControllers = [NSMutableArray array]; exposedControllers = [NSMutableArray array];
globalRumbleThreadLock = [[NSLock alloc] init];
NSArray *array = @[ NSArray *array = @[
CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick), CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick),
CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad), CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad),