Cocoa Port:

- When detecting an analog input's on state, use the upper/lower third of the range instead of the upper/lower quarter of the range. This helps improve compatibility with certain joysticks.
- Perform an integer length bounds check before calling IOHIDValueGetIntegerValue(), since this function will cause a crash if the returned integer value is very large. This prevents crashing when using certain controllers, such as the PS3 controller.
- When mapping inputs in Input Preferences, require the user to hold the input in its on state for at least 0.1 seconds. In addition, the user must also perform a full on-off state transition with the input. This helps stabilize jittery inputs, and also helps ignore unintentional inputs from devices that constantly throw a large number of signals, such as the PS3 controller's accelerometer.
- When mapping inputs in Input Preferences, remove all previous mappings for the particular control before mapping the input. Since the UI doesn't yet support multiple bindings per control, the previous behavior was confusing for users.
This commit is contained in:
rogerman 2012-02-13 05:48:10 +00:00
parent 239301d405
commit e55ad2f34e
4 changed files with 70 additions and 15 deletions

View File

@ -323,6 +323,15 @@ static NSDictionary *hidUsageTable = nil;
IOHIDElementRef hidElementRef = IOHIDValueGetElement(hidValueRef); IOHIDElementRef hidElementRef = IOHIDValueGetElement(hidValueRef);
NSInteger elementUsagePage = IOHIDElementGetUsagePage(hidElementRef); NSInteger elementUsagePage = IOHIDElementGetUsagePage(hidElementRef);
NSInteger elementUsage = IOHIDElementGetUsage(hidElementRef); NSInteger elementUsage = IOHIDElementGetUsage(hidElementRef);
// IOHIDValueGetIntegerValue() will crash if the value length is too large.
// Do a bounds check here to prevent crashing. This workaround makes the PS3
// controller usable, since it returns a length of 39 on some elements.
if(IOHIDValueGetLength(hidValueRef) > 2)
{
return inputAttributesList;
}
NSInteger logicalValue = IOHIDValueGetIntegerValue(hidValueRef); NSInteger logicalValue = IOHIDValueGetIntegerValue(hidValueRef);
NSInteger logicalMin = IOHIDElementGetLogicalMin(hidElementRef); NSInteger logicalMin = IOHIDElementGetLogicalMin(hidElementRef);
NSInteger logicalMax = IOHIDElementGetLogicalMax(hidElementRef); NSInteger logicalMax = IOHIDElementGetLogicalMax(hidElementRef);
@ -335,8 +344,8 @@ static NSDictionary *hidUsageTable = nil;
} }
else else
{ {
NSInteger lowerThreshold = ((logicalMax - logicalMin) / 4) + logicalMin; NSInteger lowerThreshold = ((logicalMax - logicalMin) / 3) + logicalMin;
NSInteger upperThreshold = (((logicalMax - logicalMin) * 3) / 4) + logicalMin; NSInteger upperThreshold = (((logicalMax - logicalMin) * 2) / 3) + logicalMin;
NSNumber *onState = [NSNumber numberWithBool:YES]; NSNumber *onState = [NSNumber numberWithBool:YES];
NSNumber *offState = [NSNumber numberWithBool:NO]; NSNumber *offState = [NSNumber numberWithBool:NO];
NSString *elementCodeLowerThreshold = [NSString stringWithFormat:@"0x%04X/0x%04X/LowerThreshold", elementUsagePage, elementUsage]; NSString *elementCodeLowerThreshold = [NSString stringWithFormat:@"0x%04X/0x%04X/LowerThreshold", elementUsagePage, elementUsage];
@ -829,11 +838,14 @@ void HandleQueueValueAvailableCallback(void *inContext, IOReturn inResult, void
inputAttributesList = [CocoaHIDDevice inputArrayFromHIDValue:hidValueRef]; inputAttributesList = [CocoaHIDDevice inputArrayFromHIDValue:hidValueRef];
} }
// HID input devices don't register events, so we need to manually prevent if (inputAttributesList != nil)
// sleep and screensaver whenever we detect an input. {
UpdateSystemActivity(UsrActivity); // HID input devices don't register events, so we need to manually prevent
// sleep and screensaver whenever we detect an input.
UpdateSystemActivity(UsrActivity);
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:@"com.DeSmuME.DeSmuME.hidInputDetected" object:inputAttributesList userInfo:nil]; [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:@"com.DeSmuME.DeSmuME.hidInputDetected" object:inputAttributesList userInfo:nil];
}
CFRelease(hidValueRef); CFRelease(hidValueRef);
} while (1); } while (1);

View File

@ -72,6 +72,21 @@
- (void) set:(NSString *)elementCode attributes:(NSDictionary *)mappingAttributes - (void) set:(NSString *)elementCode attributes:(NSDictionary *)mappingAttributes
{ {
NSString *keyString = [[self.deviceCode stringByAppendingString:@":"] stringByAppendingString:elementCode]; NSString *keyString = [[self.deviceCode stringByAppendingString:@":"] stringByAppendingString:elementCode];
// Remove any input mappings with the same DS control name as the new mapping.
// We're doing this check since the current UI doesn't support multiple bindings per control at this time.
// When the UI is updated to support multiple bindings per control, this check will need to be removed.
NSArray *mappingKeys = [self.map allKeys];
for (NSString *key in mappingKeys)
{
NSDictionary *inputMapping = (NSDictionary *)[self.map valueForKey:key];
NSString *dsControlName = (NSString *)[inputMapping valueForKey:@"name"];
if ([dsControlName isEqualToString:(NSString *)[mappingAttributes valueForKey:@"name"]])
{
[self.map setValue:nil forKey:key];
}
}
[self.map setValue:mappingAttributes forKey:keyString]; [self.map setValue:mappingAttributes forKey:keyString];
} }

View File

@ -26,6 +26,7 @@
NSWindow *prefWindow; NSWindow *prefWindow;
NSButton *lastConfigButton; NSButton *lastConfigButton;
NSInteger configInput; NSInteger configInput;
NSMutableDictionary *configInputList;
NSDictionary *keyNameTable; NSDictionary *keyNameTable;
CocoaDSController *cdsController; CocoaDSController *cdsController;

View File

@ -22,6 +22,8 @@
#import "cocoa_globals.h" #import "cocoa_globals.h"
#import "cocoa_input.h" #import "cocoa_input.h"
#define INPUT_HOLD_TIME 0.1 // Time in seconds to hold a button in its on state when mapping an input.
@implementation InputPrefsView @implementation InputPrefsView
@synthesize prefWindow; @synthesize prefWindow;
@ -38,6 +40,7 @@
lastConfigButton = nil; lastConfigButton = nil;
configInput = 0; configInput = 0;
configInputList = [[NSMutableDictionary alloc] initWithCapacity:32];
keyNameTable = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"KeyNames" ofType:@"plist"]]; keyNameTable = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"KeyNames" ofType:@"plist"]];
cdsController = nil; cdsController = nil;
@ -53,6 +56,7 @@
{ {
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
[configInputList release];
[keyNameTable release]; [keyNameTable release];
[super dealloc]; [super dealloc];
@ -348,6 +352,7 @@
if ([theButton state] == NSOnState) if ([theButton state] == NSOnState)
{ {
lastConfigButton = theButton; lastConfigButton = theButton;
[configInputList removeAllObjects];
configInput = [theButton tag]; configInput = [theButton tag];
} }
else else
@ -398,17 +403,39 @@
NSNumber *onState = (NSNumber *)[inputProperties valueForKey:@"on"]; NSNumber *onState = (NSNumber *)[inputProperties valueForKey:@"on"];
if (onState != nil) if (onState != nil)
{ {
NSString *deviceCode = (NSString *)[inputProperties valueForKey:@"deviceCode"];
NSString *elementCode = (NSString *)[inputProperties valueForKey:@"elementCode"];
NSString *deviceElementCode = [[deviceCode stringByAppendingString:@":"] stringByAppendingString:elementCode];
NSDate *inputOnDate = [configInputList valueForKey:deviceElementCode];
inputOn = [onState boolValue]; inputOn = [onState boolValue];
if (inputOn) if (inputOn)
{ {
NSString *deviceCode = (NSString *)[inputProperties valueForKey:@"deviceCode"]; if (inputOnDate == nil)
NSString *deviceName = (NSString *)[inputProperties valueForKey:@"deviceName"]; {
NSString *elementCode = (NSString *)[inputProperties valueForKey:@"elementCode"]; [configInputList setValue:[NSDate date] forKey:deviceElementCode];
NSString *elementName = (NSString *)[inputProperties valueForKey:@"elementName"]; }
}
else
{
if (inputOnDate != nil)
{
if (([inputOnDate timeIntervalSinceNow] * -1.0) < INPUT_HOLD_TIME)
{
// If the button isn't held for at least INPUT_HOLD_TIME seconds, then reject the input.
[configInputList setValue:nil forKey:deviceElementCode];
}
else
{
// Add the input mapping.
NSString *deviceName = (NSString *)[inputProperties valueForKey:@"deviceName"];
NSString *elementName = (NSString *)[inputProperties valueForKey:@"elementName"];
[self addMappingById:configInput deviceCode:deviceCode deviceName:deviceName elementCode:elementCode elementName:elementName]; [self addMappingById:configInput deviceCode:deviceCode deviceName:deviceName elementCode:elementCode elementName:elementName];
[self inputButtonCancelConfig]; [self inputButtonCancelConfig];
break; break;
}
}
} }
} }
} }