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);
NSInteger elementUsagePage = IOHIDElementGetUsagePage(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 logicalMin = IOHIDElementGetLogicalMin(hidElementRef);
NSInteger logicalMax = IOHIDElementGetLogicalMax(hidElementRef);
@ -335,8 +344,8 @@ static NSDictionary *hidUsageTable = nil;
}
else
{
NSInteger lowerThreshold = ((logicalMax - logicalMin) / 4) + logicalMin;
NSInteger upperThreshold = (((logicalMax - logicalMin) * 3) / 4) + logicalMin;
NSInteger lowerThreshold = ((logicalMax - logicalMin) / 3) + logicalMin;
NSInteger upperThreshold = (((logicalMax - logicalMin) * 2) / 3) + logicalMin;
NSNumber *onState = [NSNumber numberWithBool:YES];
NSNumber *offState = [NSNumber numberWithBool:NO];
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];
}
// 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];
if (inputAttributesList != nil)
{
// 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];
}
CFRelease(hidValueRef);
} while (1);

View File

@ -72,6 +72,21 @@
- (void) set:(NSString *)elementCode attributes:(NSDictionary *)mappingAttributes
{
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];
}

View File

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

View File

@ -22,6 +22,8 @@
#import "cocoa_globals.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
@synthesize prefWindow;
@ -38,6 +40,7 @@
lastConfigButton = nil;
configInput = 0;
configInputList = [[NSMutableDictionary alloc] initWithCapacity:32];
keyNameTable = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"KeyNames" ofType:@"plist"]];
cdsController = nil;
@ -53,6 +56,7 @@
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[configInputList release];
[keyNameTable release];
[super dealloc];
@ -348,6 +352,7 @@
if ([theButton state] == NSOnState)
{
lastConfigButton = theButton;
[configInputList removeAllObjects];
configInput = [theButton tag];
}
else
@ -398,17 +403,39 @@
NSNumber *onState = (NSNumber *)[inputProperties valueForKey:@"on"];
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];
if (inputOn)
{
NSString *deviceCode = (NSString *)[inputProperties valueForKey:@"deviceCode"];
NSString *deviceName = (NSString *)[inputProperties valueForKey:@"deviceName"];
NSString *elementCode = (NSString *)[inputProperties valueForKey:@"elementCode"];
NSString *elementName = (NSString *)[inputProperties valueForKey:@"elementName"];
[self addMappingById:configInput deviceCode:deviceCode deviceName:deviceName elementCode:elementCode elementName:elementName];
[self inputButtonCancelConfig];
break;
if (inputOnDate == nil)
{
[configInputList setValue:[NSDate date] forKey:deviceElementCode];
}
}
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 inputButtonCancelConfig];
break;
}
}
}
}
}