COnfigurable joypad controls and video options

This commit is contained in:
Michael Buckley 2019-11-10 13:02:21 -08:00
parent b52cc5ee8b
commit 8117af03b1
16 changed files with 1091 additions and 134 deletions

View File

@ -20,9 +20,25 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate> @interface AppDelegate : NSObject <NSApplicationDelegate, S9xInputDelegate>
- (void)setButtonCode:(S9xButtonCode)buttonCode forKeyCode:(int16)keyCode player:(int8)player; - (void)setButtonCode:(S9xButtonCode)buttonCode forKeyCode:(int16)keyCode player:(int8)player;
- (void)clearButton:(S9xButtonCode)button forPlayer:(int8)player;
- (NSArray<S9xJoypad *> *)listJoypads;
- (void)setPlayer:(int8)player forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index;
- (BOOL)setButton:(S9xButtonCode)button forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index cookie:(uint32)cookie value:(int32)value;
- (void)clearJoypadForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index buttonCode:(S9xButtonCode)buttonCode;
- (NSString *)labelForVendorID:(uint32)vendorID productID:(uint32)productID cookie:(uint32)cookie value:(int32)value;
- (NSString *)prefsKeyForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index;
- (BOOL)getValuesFromString:(NSString *)str vendorID:(uint32 *)vendorID productID:(uint32 *)productID index:(uint32 *)index;
- (NSString *)prefValueForCookie:(uint32)cookie value:(int32)value;
- (BOOL)getValuesFromString:(NSString *)str cookie:(uint32 *)cookie value:(int32 *)value;
- (void)setVideoMode:(int)videoMode;
- (void)setShowFPS:(BOOL)showFPS;
@end @end

View File

@ -39,7 +39,8 @@ static NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow";
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.s9xEngine = [S9xEngine new]; self.s9xEngine = [S9xEngine new];
[self setupKeyboard]; self.s9xEngine.inputDelegate = self;
[self setupDefaults];
[self importRecentItems]; [self importRecentItems];
NSWindow *window = [[NSWindow alloc] initWithContentRect:s9xView.frame styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO]; NSWindow *window = [[NSWindow alloc] initWithContentRect:s9xView.frame styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
@ -76,7 +77,7 @@ static NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow";
// Insert code here to tear down your application // Insert code here to tear down your application
} }
- (void)setupKeyboard - (void)setupDefaults
{ {
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
@ -140,6 +141,49 @@ static NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow";
[self setButtonCode:buttonCode forKeyCode:self.keys[control].integerValue player:player]; [self setButtonCode:buttonCode forKeyCode:self.keys[control].integerValue player:player];
} }
for ( S9xJoypad *joypad in [self listJoypads])
{
NSMutableDictionary *joypadPrefs = [[defaults objectForKey:kJoypadInputPrefs] mutableCopy];
if (joypadPrefs == nil)
{
joypadPrefs = [NSMutableDictionary new];
[defaults synchronize];
}
NSString *key = [self prefsKeyForVendorID:joypad.vendorID productID:joypad.productID index:joypad.index];
NSMutableDictionary *devicePrefs = [joypadPrefs[key] mutableCopy];
if (devicePrefs == nil)
{
devicePrefs = [NSMutableDictionary new];
for (S9xJoypadInput *input in [self.s9xEngine getInputsForVendorID:joypad.vendorID productID:joypad.productID index:joypad.index])
{
devicePrefs[@(input.buttonCode).stringValue] = [self prefValueForCookie:input.cookie value:input.value];
}
joypadPrefs[key] = devicePrefs;
[defaults setObject:joypadPrefs forKey:kJoypadInputPrefs];
[defaults synchronize];
}
else
{
[self.s9xEngine clearJoypadForVendorID:joypad.vendorID productID:joypad.productID index:joypad.index];
for (NSString *buttonCodeString in devicePrefs)
{
S9xButtonCode buttonCode = (S9xButtonCode)buttonCodeString.intValue;
NSString *str = devicePrefs[buttonCodeString];
uint32 cookie = 0;
int32 value = -1;
if ([self getValuesFromString:str cookie:&cookie value:&value])
{
[self setButton:buttonCode forVendorID:joypad.vendorID productID:joypad.productID index:joypad.index cookie:cookie value:value];
}
}
}
}
[self importKeySettings]; [self importKeySettings];
[self importGraphicsSettings]; [self importGraphicsSettings];
[defaults synchronize]; [defaults synchronize];
@ -167,6 +211,133 @@ static NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow";
} }
} }
- (void)clearButton:(S9xButtonCode)button forPlayer:(int8)player
{
[self.s9xEngine clearButton:button forPlayer:player];
NSMutableDictionary *keyDict = [[NSUserDefaults.standardUserDefaults objectForKey:kKeyboardPrefs] mutableCopy];
[keyDict removeObjectForKey:@(button).stringValue];
[NSUserDefaults.standardUserDefaults setObject:[keyDict copy] forKey:kKeyboardPrefs];
[NSUserDefaults.standardUserDefaults synchronize];
}
- (NSArray<S9xJoypad *> *)listJoypads
{
return [self.s9xEngine listJoypads];
}
- (NSString *)prefsKeyForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index
{
return [NSString stringWithFormat:@"%@:%@:%@", @(vendorID).stringValue, @(productID).stringValue, @(index).stringValue];
}
- (BOOL)getValuesFromString:(NSString *)str vendorID:(uint32 *)vendorID productID:(uint32 *)productID index:(uint32 *)index
{
if (vendorID == NULL || productID == NULL || index == NULL)
{
return NO;
}
NSArray<NSString *> *components = [str componentsSeparatedByString:@":"];
if (components.count != 3)
{
return NO;
}
*vendorID = components[0].intValue;
*productID = components[1].intValue;
*index = components[2].intValue;
return YES;
}
- (NSString *)prefValueForCookie:(uint32)cookie value:(int32)value
{
return [NSString stringWithFormat:@"%@:%@", @(cookie).stringValue, @(value).stringValue];
}
- (BOOL)getValuesFromString:(NSString *)str cookie:(uint32 *)cookie value:(int32 *)value
{
if (cookie == NULL || value == NULL)
{
return NO;
}
NSArray<NSString *> *components = [str componentsSeparatedByString:@":"];
if (components.count != 2)
{
return NO;
}
*cookie = components.firstObject.intValue;
*value = components.lastObject.intValue;
return YES;
}
- (void)setPlayer:(int8)player forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index
{
int8 oldPlayer = -1;
[self.s9xEngine setPlayer:player forVendorID:vendorID productID:productID index:index oldPlayer:&oldPlayer];
NSMutableDictionary *playersDict = [[NSUserDefaults.standardUserDefaults objectForKey:kJoypadPlayerPrefs] mutableCopy];
if (playersDict == nil)
{
playersDict = [NSMutableDictionary new];
}
if (oldPlayer >= 0 && player != oldPlayer)
{
[playersDict removeObjectForKey:@(oldPlayer).stringValue];
}
playersDict[@(player).stringValue] = [self prefsKeyForVendorID:vendorID productID:productID index:index];
[NSUserDefaults.standardUserDefaults setObject:[playersDict copy] forKey:kJoypadPlayerPrefs];
}
- (BOOL)setButton:(S9xButtonCode)button forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index cookie:(uint32)cookie value:(int32)value
{
S9xButtonCode oldButton = (S9xButtonCode)-1;
BOOL result = [self.s9xEngine setButton:button forVendorID:vendorID productID:productID index:index cookie:cookie value:value oldButton:&oldButton];
NSMutableDictionary *prefsDict = [[NSUserDefaults.standardUserDefaults objectForKey:kJoypadInputPrefs] mutableCopy];
NSString *key = [self prefsKeyForVendorID:vendorID productID:productID index:index];
NSMutableDictionary *joypadDict = [prefsDict[key] mutableCopy];
if (result && button != oldButton)
{
[joypadDict removeObjectForKey:@(oldButton).stringValue];
}
joypadDict[@(button).stringValue] = [self prefValueForCookie:cookie value:value];
prefsDict[key] = [joypadDict copy];
[NSUserDefaults.standardUserDefaults setObject:[prefsDict copy] forKey:kJoypadInputPrefs];
return result;
}
- (void)clearJoypadForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index buttonCode:(S9xButtonCode)buttonCode
{
[self.s9xEngine clearJoypadForVendorID:vendorID productID:productID index:index buttonCode:buttonCode];
NSString *key = [self prefsKeyForVendorID:vendorID productID:productID index:index];
NSMutableDictionary *joypadsDict = [[NSUserDefaults.standardUserDefaults objectForKey:kJoypadInputPrefs] mutableCopy];
NSMutableDictionary *deviceDict = [joypadsDict[key] mutableCopy];
[deviceDict removeObjectForKey:@(buttonCode).stringValue];
joypadsDict[key] = deviceDict;
[NSUserDefaults.standardUserDefaults setObject:[joypadsDict copy] forKey:kJoypadInputPrefs];
[NSUserDefaults.standardUserDefaults synchronize];
}
- (NSString *)labelForVendorID:(uint32)vendorID productID:(uint32)productID cookie:(uint32)cookie value:(int32)value
{
return [self.s9xEngine labelForVendorID:vendorID productID:productID cookie:cookie value:value];
}
- (void)importRecentItems - (void)importRecentItems
{ {
const NSInteger maxRecents = 20; const NSInteger maxRecents = 20;
@ -216,12 +387,16 @@ static NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow";
[NSUserDefaults.standardUserDefaults setBool:(data.length > 0 && ((char *)data.bytes)[0]) forKey:kShowFPSPref]; [NSUserDefaults.standardUserDefaults setBool:(data.length > 0 && ((char *)data.bytes)[0]) forKey:kShowFPSPref];
} }
[self setShowFPS:[NSUserDefaults.standardUserDefaults boolForKey:kShowFPSPref]];
data = [self objectForPrefOSCode:'Vmod']; data = [self objectForPrefOSCode:'Vmod'];
if ( data != nil) if ( data != nil)
{ {
[NSUserDefaults.standardUserDefaults setInteger:((data.length >= 0 && ((char *)data.bytes)[0]) ? VIDEOMODE_SMOOTH : VIDEOMODE_BLOCKY) forKey:kVideoModePref]; [NSUserDefaults.standardUserDefaults setInteger:((data.length >= 0 && ((char *)data.bytes)[0]) ? VIDEOMODE_SMOOTH : VIDEOMODE_BLOCKY) forKey:kVideoModePref];
} }
[self setVideoMode:(int)[NSUserDefaults.standardUserDefaults integerForKey:kVideoModePref]];
} }
- (id)objectForPrefOSCode:(uint32_t)osCode - (id)objectForPrefOSCode:(uint32_t)osCode
@ -304,4 +479,33 @@ static NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow";
[self.prefsWindowController.window makeKeyAndOrderFront:self]; [self.prefsWindowController.window makeKeyAndOrderFront:self];
} }
- (void)setVideoMode:(int)videoMode
{
[self.s9xEngine setVideoMode:videoMode];
[NSUserDefaults.standardUserDefaults setObject:@(videoMode) forKey:kVideoModePref];
[NSUserDefaults.standardUserDefaults synchronize];
}
- (void)setShowFPS:(BOOL)showFPS
{
[self.s9xEngine setShowFPS:showFPS];
[NSUserDefaults.standardUserDefaults setObject:@(showFPS) forKey:kShowFPSPref];
[NSUserDefaults.standardUserDefaults synchronize];
}
- (IBAction)resume:(id)sender
{
[self.s9xEngine resume];
}
- (BOOL)handleInput:(S9xJoypadInput *)input fromJoypad:(S9xJoypad *)joypad
{
if (NSApp.keyWindow == self.prefsWindowController.window)
{
return [((S9xPrefsViewController *) self.prefsWindowController.contentViewController) handleInput:input fromJoypad:joypad];
}
return NO;
}
@end @end

View File

@ -95,6 +95,18 @@
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Emulation" id="Kjk-eO-rPI">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Emulation" id="s2F-Pd-YT5">
<items>
<menuItem title="Resume" keyEquivalent="r" id="m8Y-5s-VGC">
<connections>
<action selector="resume:" target="-1" id="jxw-ZM-JXi"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v"> <menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO"> <menu key="submenu" title="View" id="HyV-fh-RgO">

View File

@ -20,11 +20,15 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@class S9xJoypadInput;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface S9xButtonConfigTextField : NSSearchField<NSTextFieldDelegate> @interface S9xButtonConfigTextField : NSSearchField<NSSearchFieldDelegate>
@property (nonatomic, assign) CGKeyCode keyCode; @property (nonatomic, assign) CGKeyCode keyCode;
@property (nonatomic, strong, nullable) S9xJoypadInput *joypadInput;
@property (nonatomic, assign) BOOL disableKeyboardInput;
@end @end

View File

@ -30,10 +30,21 @@
self.delegate = self; self.delegate = self;
self.placeholderString = @""; self.placeholderString = @"";
[[self cell] setSearchButtonCell:nil]; [[self cell] setSearchButtonCell:nil];
NSButtonCell *cancelButton = [[self cell] cancelButtonCell];
cancelButton.target = self;
cancelButton.action = @selector(clearSearch:);
} }
- (void)setKeyCode:(CGKeyCode)keyCode - (void)setKeyCode:(CGKeyCode)keyCode
{ {
if (keyCode == (CGKeyCode)-1)
{
_keyCode = keyCode;
self.stringValue = @"";
return;
}
NSString *stringValue = nil; NSString *stringValue = nil;
switch (keyCode) switch (keyCode)
@ -266,11 +277,19 @@
- (void)keyUp:(NSEvent *)event - (void)keyUp:(NSEvent *)event
{ {
[self setKeyCode:event.keyCode]; if (!self.disableKeyboardInput )
{
[self setKeyCode:event.keyCode];
}
} }
- (void)flagsChanged:(NSEvent *)event - (void)flagsChanged:(NSEvent *)event
{ {
if (self.disableKeyboardInput)
{
return;
}
NSEventModifierFlags flags = event.modifierFlags; NSEventModifierFlags flags = event.modifierFlags;
if ( flags & NSEventModifierFlagShift ) if ( flags & NSEventModifierFlagShift )
@ -297,8 +316,27 @@
[self.currentEditor selectAll:self]; [self.currentEditor selectAll:self];
} }
- (void)clearSearch:(id)sender
{
self.stringValue = @"";
if (self.disableKeyboardInput)
{
self.joypadInput = nil;
}
else
{
self.keyCode = -1;
}
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector
{ {
if (self.disableKeyboardInput)
{
return NO;
}
if (commandSelector == @selector(insertTab:)) if (commandSelector == @selector(insertTab:))
{ {
[self setKeyCode:kVK_Tab]; [self setKeyCode:kVK_Tab];
@ -313,4 +351,9 @@
return NO; return NO;
} }
- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
{
return !self.disableKeyboardInput;
}
@end @end

View File

@ -21,5 +21,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
extern NSString * const kKeyboardPrefs; extern NSString * const kKeyboardPrefs;
extern NSString * const kJoypadInputPrefs;
extern NSString * const kJoypadPlayerPrefs;
extern NSString * const kShowFPSPref; extern NSString * const kShowFPSPref;
extern NSString * const kVideoModePref; extern NSString * const kVideoModePref;

View File

@ -21,5 +21,7 @@
#import "S9xPrefsConstants.h" #import "S9xPrefsConstants.h"
NSString * const kKeyboardPrefs = @"KeyboardConfig"; NSString * const kKeyboardPrefs = @"KeyboardConfig";
NSString * const kJoypadInputPrefs = @"JoypadInputs";
NSString * const kJoypadPlayerPrefs = @"JoypadPlayers";
NSString * const kShowFPSPref = @"ShowFPS"; NSString * const kShowFPSPref = @"ShowFPS";
NSString * const kVideoModePref = @"VideoMode"; NSString * const kVideoModePref = @"VideoMode";

View File

@ -21,7 +21,7 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface S9xPrefsViewController : NSViewController @interface S9xPrefsViewController : NSViewController<S9xInputDelegate>
@end @end

View File

@ -36,12 +36,58 @@
- (void)awakeFromNib - (void)awakeFromNib
{ {
AppDelegate *appDelegate = (AppDelegate *)NSApp.delegate;
NSUInteger joypadIndex = 0;
for (S9xJoypad *joypad in [appDelegate listJoypads])
{
NSMenuItem *item = [NSMenuItem new];
item.title = joypad.name;
item.tag = joypadIndex++;
item.representedObject = joypad;
[self.devicePopUp.menu addItem:item];
}
[self selectDeviceForPlayer:0];
for (NSView *subview in self.view.subviews) for (NSView *subview in self.view.subviews)
{ {
if ( [subview isKindOfClass:[S9xButtonConfigTextField class]] ) if ( [subview isKindOfClass:[S9xButtonConfigTextField class]] )
{ {
S9xButtonConfigTextField *field = (S9xButtonConfigTextField *)subview; S9xButtonConfigTextField *field = (S9xButtonConfigTextField *)subview;
[field addObserver:self forKeyPath:@"keyCode" options:NSKeyValueObservingOptionNew context:NULL]; [field addObserver:self forKeyPath:@"keyCode" options:NSKeyValueObservingOptionNew context:NULL];
[field addObserver:self forKeyPath:@"joypadInput" options:NSKeyValueObservingOptionNew context:NULL];
}
}
}
- (void)selectDeviceForPlayer:(int8_t)player
{
AppDelegate *appDelegate = (AppDelegate *)NSApp.delegate;
NSString* joypadKey = [[NSUserDefaults.standardUserDefaults objectForKey:kJoypadPlayerPrefs] objectForKey:@(player).stringValue];
[self.devicePopUp selectItemAtIndex:0];
if (joypadKey != nil)
{
uint32 vendorID = 0;
uint32 productID = 0;
uint32 index = 0;
if ( [appDelegate getValuesFromString:joypadKey vendorID:&vendorID productID:&productID index:&index])
{
S9xJoypad *joypad = [S9xJoypad new];
joypad.vendorID = vendorID;
joypad.productID = productID;
joypad.index = index;
for (NSMenuItem *item in self.devicePopUp.menu.itemArray)
{
if ([joypad isEqual:item.representedObject])
{
[self.devicePopUp selectItem:item];
break;
}
}
} }
} }
} }
@ -59,9 +105,10 @@
[self.videoModePopup selectItemAtIndex:index]; [self.videoModePopup selectItemAtIndex:index];
self.showFPSCheckbox.state = [NSUserDefaults.standardUserDefaults boolForKey:kShowFPSPref]; self.showFPSCheckbox.state = [NSUserDefaults.standardUserDefaults boolForKey:kShowFPSPref];
NSMutableDictionary<NSNumber *, NSNumber *> *controlsDict = [NSMutableDictionary new];
if (self.devicePopUp.selectedItem.tag < 0) if (self.devicePopUp.selectedItem.tag < 0)
{ {
NSMutableDictionary<NSNumber *, NSNumber *> *controlsDict = [NSMutableDictionary new];
NSDictionary *keyboardDict = [NSUserDefaults.standardUserDefaults objectForKey:kKeyboardPrefs]; NSDictionary *keyboardDict = [NSUserDefaults.standardUserDefaults objectForKey:kKeyboardPrefs];
NSInteger playerNum = self.playerPopUp.selectedItem.tag; NSInteger playerNum = self.playerPopUp.selectedItem.tag;
@ -69,46 +116,182 @@
{ {
controlsDict[@(i)] = keyboardDict[@(i + playerNum).stringValue]; controlsDict[@(i)] = keyboardDict[@(i + playerNum).stringValue];
} }
}
for (NSView *subview in self.view.subviews) for (NSView *subview in self.view.subviews)
{
if ( [subview isKindOfClass:[S9xButtonConfigTextField class]] )
{ {
S9xButtonConfigTextField *field = (S9xButtonConfigTextField *)subview; if ([subview isKindOfClass:[S9xButtonConfigTextField class]])
[field removeObserver:self forKeyPath:@"keyCode"];
NSNumber *keyCode = controlsDict[@(field.tag)];
if ( keyCode != nil )
{ {
field.keyCode = keyCode.intValue; S9xButtonConfigTextField *field = (S9xButtonConfigTextField *)subview;
}
else
{
field.stringValue = @"";
}
[field addObserver:self forKeyPath:@"keyCode" options:NSKeyValueObservingOptionNew context:NULL]; [field removeObserver:self forKeyPath:@"keyCode"];
[field removeObserver:self forKeyPath:@"joypadInput"];
NSNumber *keyCode = controlsDict[@(field.tag)];
field.joypadInput = nil;
if ( keyCode != nil )
{
field.keyCode = keyCode.intValue;
}
else
{
field.keyCode = -1;
}
[field addObserver:self forKeyPath:@"keyCode" options:NSKeyValueObservingOptionNew context:NULL];
[field addObserver:self forKeyPath:@"joypadInput" options:NSKeyValueObservingOptionNew context:NULL];
field.disableKeyboardInput = NO;
}
}
}
else
{
AppDelegate *appDelegate = (AppDelegate *)NSApp.delegate;
S9xJoypad *joypad = self.devicePopUp.selectedItem.representedObject;
NSString *joypadKey = [appDelegate prefsKeyForVendorID:joypad.vendorID productID:joypad.productID index:joypad.index];
NSDictionary *joypadDIct = [[NSUserDefaults.standardUserDefaults objectForKey:kJoypadInputPrefs] objectForKey:joypadKey];
for (NSView *subview in self.view.subviews)
{
if ([subview isKindOfClass:[S9xButtonConfigTextField class]])
{
S9xButtonConfigTextField *textField = (S9xButtonConfigTextField *)subview;
[textField removeObserver:self forKeyPath:@"keyCode"];
[textField removeObserver:self forKeyPath:@"joypadInput"];
uint32 cookie = 0;
int32 value = 0;
S9xButtonCode buttonCode = (S9xButtonCode)textField.tag;
NSString *inputString = joypadDIct[@(buttonCode).stringValue];
textField.keyCode = -1;
if ([appDelegate getValuesFromString:inputString cookie:&cookie value:&value])
{
S9xJoypadInput *input = [S9xJoypadInput new];
input.cookie = cookie;
input.value = value;
input.buttonCode = buttonCode;
textField.joypadInput = input;
textField.stringValue = [appDelegate labelForVendorID:joypad.vendorID productID:joypad.productID cookie:cookie value:value];
}
else
{
textField.joypadInput = nil;
textField.stringValue = @"";
}
[textField addObserver:self forKeyPath:@"keyCode" options:NSKeyValueObservingOptionNew context:NULL];
[textField addObserver:self forKeyPath:@"joypadInput" options:NSKeyValueObservingOptionNew context:NULL];
textField.disableKeyboardInput = YES;
}
} }
} }
} }
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{ {
if ( [keyPath isEqualToString:@"keyCode"] ) if ([keyPath isEqualToString:@"keyCode"])
{ {
S9xButtonConfigTextField *field = (S9xButtonConfigTextField *)object; S9xButtonConfigTextField *field = (S9xButtonConfigTextField *)object;
S9xButtonCode buttonCode = (S9xButtonCode)field.tag; S9xButtonCode buttonCode = (S9xButtonCode)field.tag;
uint16_t keyCode = field.keyCode; uint16_t keyCode = field.keyCode;
int8_t player = self.playerPopUp.selectedItem.tag; int8_t player = self.playerPopUp.selectedItem.tag;
[((AppDelegate *) NSApp.delegate) setButtonCode:buttonCode forKeyCode:keyCode player:player]; if (keyCode != (CGKeyCode)-1)
{
[((AppDelegate *) NSApp.delegate) setButtonCode:buttonCode forKeyCode:keyCode player:player];
}
else
{
[((AppDelegate *) NSApp.delegate) clearButton:buttonCode forPlayer:player];
}
[NSUserDefaults.standardUserDefaults synchronize]; [NSUserDefaults.standardUserDefaults synchronize];
[self refresh]; [self refresh];
} }
else if ( [keyPath isEqualToString:@"joypadInput"])
{
S9xButtonConfigTextField *field = (S9xButtonConfigTextField *)object;
S9xButtonCode buttonCode = (S9xButtonCode)field.tag;
S9xJoypad *joypad = self.devicePopUp.selectedItem.representedObject;
if ([joypad isKindOfClass:[S9xJoypad class]])
{
S9xJoypadInput *input = field.joypadInput;
if (input != nil)
{
[((AppDelegate *)NSApp.delegate) setButton:buttonCode forVendorID:joypad.vendorID productID:joypad.productID index:joypad.index cookie:input.cookie value:input.value];
}
else
{
[((AppDelegate *)NSApp.delegate) clearJoypadForVendorID:joypad.vendorID productID:joypad.productID index:joypad.index buttonCode:buttonCode];
}
}
[NSUserDefaults.standardUserDefaults synchronize];
[self refresh];
}
}
- (IBAction)playerDropdownChanged:(NSPopUpButton *)sender
{
[self selectDeviceForPlayer:sender.selectedTag];
[self refresh];
}
- (IBAction)deviceDropdownChanged:(NSPopUpButton *)sender
{
if (sender.selectedTag >= 0)
{
AppDelegate *appDelegate = (AppDelegate *)NSApp.delegate;
S9xJoypad *joypad = sender.selectedItem.representedObject;
[appDelegate setPlayer:self.playerPopUp.selectedTag forVendorID:joypad.vendorID productID:joypad.productID index:joypad.index];
[NSUserDefaults.standardUserDefaults synchronize];
}
[self refresh];
}
- (IBAction)showFPS:(NSButton *)sender
{
AppDelegate *appDelegate = (AppDelegate *)NSApp.delegate;
[appDelegate setShowFPS:sender.state == NSOnState];
}
- (IBAction)setVideoMode:(NSPopUpButton *)sender
{
AppDelegate *appDelegate = (AppDelegate *)NSApp.delegate;
[appDelegate setVideoMode:(int)sender.selectedTag];
}
- (BOOL)handleInput:(S9xJoypadInput *)input fromJoypad:(S9xJoypad *)joypad
{
id firstResponder = self.view.window.firstResponder;
if ([firstResponder isFieldEditor])
{
firstResponder = [firstResponder delegate];
}
if ([firstResponder respondsToSelector:@selector(setJoypadInput:)])
{
S9xJoypad *currentJoypad = self.devicePopUp.selectedItem.representedObject;
if ([joypad isEqual:currentJoypad])
{
[firstResponder setJoypadInput:input];
return YES;
}
}
return NO;
} }
@end @end

View File

@ -22,25 +22,28 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rxW-aA-Bai"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rxW-aA-Bai">
<rect key="frame" x="18" y="538" width="77" height="16"/> <rect key="frame" x="18" y="538" width="191" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Video Mode" id="XoT-95-aIF"> <textFieldCell key="cell" lineBreakMode="clipping" title="Video Mode (Requires Restart)" id="XoT-95-aIF">
<font key="font" usesAppearanceFont="YES"/> <font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qNi-B0-E49"> <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qNi-B0-E49">
<rect key="frame" x="99" y="532" width="85" height="25"/> <rect key="frame" x="213" y="532" width="85" height="25"/>
<popUpButtonCell key="cell" type="push" title="Blocky" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Xg7-hz-RRs" id="IRO-Ia-Tlm"> <popUpButtonCell key="cell" type="push" title="Blocky" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Xg7-hz-RRs" id="IRO-Ia-Tlm">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/> <font key="font" metaFont="menu"/>
<menu key="menu" id="VtI-7w-Yps"> <menu key="menu" id="VtI-7w-Yps">
<items> <items>
<menuItem title="Blocky" state="on" id="Xg7-hz-RRs"/> <menuItem title="Blocky" state="on" id="Xg7-hz-RRs"/>
<menuItem title="Smooth" tag="1" id="ucR-ef-Wfk"/> <menuItem title="Smooth" tag="2" id="ucR-ef-Wfk"/>
</items> </items>
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
<connections>
<action selector="setVideoMode:" target="-2" id="oj8-pk-Kdg"/>
</connections>
</popUpButton> </popUpButton>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hfx-Lg-t9P"> <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hfx-Lg-t9P">
<rect key="frame" x="18" y="503" width="472" height="25"/> <rect key="frame" x="18" y="503" width="472" height="25"/>
@ -60,6 +63,9 @@
</items> </items>
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
<connections>
<action selector="playerDropdownChanged:" target="-2" id="P4K-gg-QUK"/>
</connections>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qMp-14-9zW"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qMp-14-9zW">
<rect key="frame" x="18" y="443" width="88" height="16"/> <rect key="frame" x="18" y="443" width="88" height="16"/>
@ -211,6 +217,9 @@
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
</buttonCell> </buttonCell>
<connections>
<action selector="showFPS:" target="-2" id="CM2-bf-aFb"/>
</connections>
</button> </button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TuE-fA-Shh"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TuE-fA-Shh">
<rect key="frame" x="230" y="353" width="151" height="16"/> <rect key="frame" x="230" y="353" width="151" height="16"/>
@ -311,6 +320,9 @@
</items> </items>
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
<connections>
<action selector="deviceDropdownChanged:" target="-2" id="oH3-xl-IZF"/>
</connections>
</popUpButton> </popUpButton>
<searchField wantsLayer="YES" verticalHuggingPriority="750" tag="1" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5wn-WM-n7f" customClass="S9xButtonConfigTextField"> <searchField wantsLayer="YES" verticalHuggingPriority="750" tag="1" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5wn-WM-n7f" customClass="S9xButtonConfigTextField">
<rect key="frame" x="112" y="410" width="100" height="22"/> <rect key="frame" x="112" y="410" width="100" height="22"/>

View File

@ -22,12 +22,117 @@
#ifndef _mac_joypad_h_ #ifndef _mac_joypad_h_
#define _mac_joypad_h_ #define _mac_joypad_h_
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "mac-controls.h" #include "mac-controls.h"
struct JoypadDevice {
uint16 vendorID;
uint16 productID;
uint32 index;
bool operator==(const struct JoypadDevice &o) const
{
return vendorID == o.vendorID && productID == o.productID && index == o.index;
}
bool operator<(const struct JoypadDevice &o) const
{
return vendorID < o.vendorID || productID < o.productID || index < o.index;
}
};
struct JoypadCookie {
struct JoypadDevice device;
uint32 cookie;
JoypadCookie() {}
struct JoypadCookie &operator=(const struct JoypadCookie &o)
{
device = o.device;
cookie = o.cookie;
return *this;
}
bool operator==(const struct JoypadCookie &o) const
{
return device == o.device && cookie == o.cookie;
}
bool operator<(const struct JoypadCookie &o) const
{
return device < o.device || cookie < o.cookie;
}
};
struct JoypadCookieInfo {
uint32 usage;
uint32 index;
int32 midpoint;
int32 min;
int32 max;
};
struct JoypadInput {
struct JoypadCookie cookie;
int32 value;
bool operator==(const struct JoypadInput &o) const
{
return cookie == o.cookie && value == o.value;
}
bool operator<(const struct JoypadInput &o) const
{
return cookie < o.cookie || value < o.value;
}
};
namespace std {
template <>
struct hash<struct JoypadDevice>
{
std::size_t operator()(const JoypadDevice& k) const
{
return k.vendorID ^ k.productID ^ k.index;
}
};
template <>
struct hash<struct JoypadCookie>
{
std::size_t operator()(const JoypadCookie& k) const
{
return std::hash<struct JoypadDevice>()(k.device) ^ k.cookie;
}
};
template <>
struct hash<struct JoypadInput>
{
std::size_t operator()(const JoypadInput& k) const
{
return std::hash<struct JoypadCookie>()(k.cookie) ^ k.value;
}
};
}
void SetUpHID (void); void SetUpHID (void);
void ReleaseHID (void); void ReleaseHID (void);
void SetPlayerForJoypad(int8 playerNum, uint32 vendorID, uint32 productID, uint8 index, int8 *oldPlayerNum); std::unordered_set<struct JoypadDevice> ListJoypads (void);
void SetButtonCodeForJoypadControl(uint32 vendorID, uint32 productID, uint8 index, uint32 cookie, int32 value, S9xButtonCode buttonCode, bool overwrite, S9xButtonCode *oldButtonCode); std::string NameForDevice(struct JoypadDevice device);
void SetPlayerForJoypad(int8 playerNum, uint32 vendorID, uint32 productID, uint32 index, int8 *oldPlayerNum);
bool SetButtonCodeForJoypadControl(uint32 vendorID, uint32 productID, uint32 index, uint32 cookie, int32 value, S9xButtonCode buttonCode, bool overwrite, S9xButtonCode *oldButtonCode);
void ClearButtonCodeForJoypad(uint32 vendorID, uint32 productID, uint32 index, S9xButtonCode buttonCode);
void ClearJoypad(uint32 vendorID, uint32 productID, uint32 index);
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJuypadButtons(uint32 vendorID, uint32 productID, uint32 index);
std::string LabelForInput(uint32 vendorID, uint32 productID, uint32 cookie, int32 value);
#endif #endif

View File

@ -20,8 +20,6 @@
#include <map> #include <map>
#include <unordered_map>
#include <unordered_set>
#include "port.h" #include "port.h"
@ -60,107 +58,15 @@
typedef hu_device_t *pRecDevice; typedef hu_device_t *pRecDevice;
typedef hu_element_t *pRecElement; typedef hu_element_t *pRecElement;
struct JoypadDevice {
uint16 vendorID;
uint16 productID;
uint32 index;
bool operator==(const struct JoypadDevice &o) const
{
return vendorID == o.vendorID && productID == o.productID && index == o.index;
}
bool operator<(const struct JoypadDevice &o) const
{
return vendorID < o.vendorID || productID < o.productID || index < o.index;
}
};
struct JoypadCookie {
struct JoypadDevice device;
uint32 cookie;
JoypadCookie() {}
struct JoypadCookie &operator=(const struct JoypadCookie &o)
{
device = o.device;
cookie = o.cookie;
return *this;
}
bool operator==(const struct JoypadCookie &o) const
{
return device == o.device && cookie == o.cookie;
}
bool operator<(const struct JoypadCookie &o) const
{
return device < o.device || cookie < o.cookie;
}
};
struct JoypadCookieInfo {
uint32 usage;
uint32 index;
int32 midpoint;
int32 min;
int32 max;
};
struct JoypadInput {
struct JoypadCookie cookie;
int32 value;
bool operator==(const struct JoypadInput &o) const
{
return cookie == o.cookie && value == o.value;
}
bool operator<(const struct JoypadInput &o) const
{
return cookie < o.cookie || value < o.value;
}
};
namespace std {
template <>
struct hash<struct JoypadDevice>
{
std::size_t operator()(const JoypadDevice& k) const
{
return k.vendorID ^ k.productID ^ k.index;
}
};
template <>
struct hash<struct JoypadCookie>
{
std::size_t operator()(const JoypadCookie& k) const
{
return std::hash<struct JoypadDevice>()(k.device) ^ k.cookie;
}
};
template <>
struct hash<struct JoypadInput>
{
std::size_t operator()(const JoypadInput& k) const
{
return std::hash<struct JoypadCookie>()(k.cookie) ^ k.value;
}
};
}
std::unordered_set<JoypadDevice> allDevices; std::unordered_set<JoypadDevice> allDevices;
std::unordered_map<JoypadDevice, std::map<uint8, std::map<int8, S9xButtonCode>>> defaultAxes; std::unordered_map<JoypadDevice, std::map<uint8, std::map<int8, S9xButtonCode>>> defaultAxes;
std::unordered_map<JoypadDevice, std::map<uint8, S9xButtonCode>> defaultButtons; std::unordered_map<JoypadDevice, std::map<uint8, S9xButtonCode>> defaultButtons;
std::unordered_map<JoypadDevice, std::map<uint8, S9xButtonCode>> defaultHatValues; std::unordered_map<JoypadDevice, std::map<uint8, S9xButtonCode>> defaultHatValues;
// TODO: Hook these next two up
std::unordered_map<JoypadDevice, int8> playerNumByDevice; std::unordered_map<JoypadDevice, int8> playerNumByDevice;
std::unordered_map<uint32, int8> deviceIndexByPort; std::unordered_map<uint32, int8> deviceIndexByPort;
std::unordered_map<JoypadCookie, JoypadCookieInfo> infoByCookie; std::unordered_map<JoypadCookie, JoypadCookieInfo> infoByCookie;
std::unordered_map<JoypadInput, S9xButtonCode> buttonCodeByJoypadInput; std::unordered_map<JoypadInput, S9xButtonCode> buttonCodeByJoypadInput;
std::unordered_map<JoypadDevice, std::string> namesByDevice;
@interface NSData (S9xHexString) @interface NSData (S9xHexString)
+(id)s9x_dataWithHexString:(NSString *)hex; +(id)s9x_dataWithHexString:(NSString *)hex;
@ -191,6 +97,20 @@ std::unordered_map<JoypadInput, S9xButtonCode> buttonCodeByJoypadInput;
IOHIDManagerRef hidManager = NULL; IOHIDManagerRef hidManager = NULL;
std::unordered_set<struct JoypadDevice> ListJoypads (void) {
return allDevices;
}
std::string NameForDevice(struct JoypadDevice device) {
auto it = namesByDevice.find(device);
if (it != namesByDevice.end())
{
return it->second;
}
return "";
}
void gamepadAction(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef v) { void gamepadAction(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef v) {
os_unfair_lock_lock(&keyLock); os_unfair_lock_lock(&keyLock);
@ -244,6 +164,22 @@ void gamepadAction(void *inContext, IOReturn inResult, void *inSender, IOHIDValu
inputStruct.cookie = cookieStruct; inputStruct.cookie = cookieStruct;
inputStruct.value = (int32_t)IOHIDValueGetIntegerValue(v); inputStruct.value = (int32_t)IOHIDValueGetIntegerValue(v);
S9xJoypad *objcJoypad = [S9xJoypad new];
objcJoypad.vendorID = deviceStruct.vendorID;
objcJoypad.productID = deviceStruct.productID;
objcJoypad.index = deviceStruct.index;
S9xJoypadInput *objcInput = [S9xJoypadInput new];
objcInput.cookie = inputStruct.cookie.cookie;
objcInput.value =inputStruct.value;
os_unfair_lock_unlock(&keyLock);
if ([inputDelegate handleInput:objcInput fromJoypad:objcJoypad])
{
return;
}
os_unfair_lock_lock(&keyLock);
struct JoypadInput oppositeInputStruct = inputStruct; struct JoypadInput oppositeInputStruct = inputStruct;
if (info.min != info.max) if (info.min != info.max)
@ -552,6 +488,7 @@ void AddDevice (IOHIDDeviceRef device)
{ {
NSNumber *vendor = (NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)); NSNumber *vendor = (NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
NSNumber *product = (NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)); NSNumber *product = (NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
NSString *name = (NSString *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
NSMutableArray<NSDictionary *> *buttons = [NSMutableArray new]; NSMutableArray<NSDictionary *> *buttons = [NSMutableArray new];
NSMutableArray<NSDictionary *> *axes = [NSMutableArray new]; NSMutableArray<NSDictionary *> *axes = [NSMutableArray new];
@ -570,6 +507,14 @@ void AddDevice (IOHIDDeviceRef device)
} }
allDevices.insert(deviceStruct); allDevices.insert(deviceStruct);
std::string s = std::string(name.UTF8String);
if (deviceStruct.index > 0)
{
s = s + " (" + std::to_string(deviceStruct.index + 1) + ")";
}
namesByDevice[deviceStruct] = s;
uint32_t port = ((NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey))).unsignedIntValue; uint32_t port = ((NSNumber *)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey))).unsignedIntValue;
deviceIndexByPort[port] = deviceStruct.index; deviceIndexByPort[port] = deviceStruct.index;
@ -662,7 +607,7 @@ void AddDevice (IOHIDDeviceRef device)
} }
} }
// TODO: Extend axisIndex into defaultAxes info.usage = axisDict[@kIOHIDElementUsageKey].intValue;
info.index = axisIndex++; info.index = axisIndex++;
infoByCookie[cookie] = info; infoByCookie[cookie] = info;
} }
@ -698,6 +643,46 @@ void AddDevice (IOHIDDeviceRef device)
CFRelease(properties); CFRelease(properties);
} }
void ClearJoypad(uint32 vendorID, uint32 productID, uint32 index)
{
struct JoypadDevice device;
device.vendorID = vendorID;
device.productID = productID;
device.index = index;
for (auto it = buttonCodeByJoypadInput.begin(); it != buttonCodeByJoypadInput.end();)
{
if (it->first.cookie.device == device)
{
buttonCodeByJoypadInput.erase(it++);
}
else
{
++it;
}
}
}
std::unordered_map<struct JoypadInput, S9xButtonCode> GetJuypadButtons(uint32 vendorID, uint32 productID, uint32 index)
{
struct JoypadDevice device;
device.vendorID = vendorID;
device.productID = productID;
device.index = index;
std::unordered_map<struct JoypadInput, S9xButtonCode> joypadButtons;
for (auto it = buttonCodeByJoypadInput.begin(); it != buttonCodeByJoypadInput.end(); ++it)
{
if ( it->first.cookie.device == device)
{
joypadButtons[it->first] = it->second;
}
}
return joypadButtons;
}
void SetUpHID (void) void SetUpHID (void)
{ {
hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
@ -776,7 +761,7 @@ void ReleaseHID (void)
} }
} }
void SetPlayerForJoypad(int8 playerNum, uint32 vendorID, uint32 productID, uint8 index, int8 *oldPlayerNum) void SetPlayerForJoypad(int8 playerNum, uint32 vendorID, uint32 productID, uint32 index, int8 *oldPlayerNum)
{ {
struct JoypadDevice device; struct JoypadDevice device;
device.vendorID = vendorID; device.vendorID = vendorID;
@ -796,8 +781,13 @@ void SetPlayerForJoypad(int8 playerNum, uint32 vendorID, uint32 productID, uint8
playerNumByDevice[device] = playerNum; playerNumByDevice[device] = playerNum;
} }
void SetButtonCodeForJoypadControl(uint32 vendorID, uint32 productID, uint8 index, uint32 cookie, int32 value, S9xButtonCode buttonCode, bool overwrite, S9xButtonCode *oldButtonCode) bool SetButtonCodeForJoypadControl(uint32 vendorID, uint32 productID, uint32 index, uint32 cookie, int32 value, S9xButtonCode buttonCode, bool overwrite, S9xButtonCode *oldButtonCode)
{ {
if (buttonCode < 0 || buttonCode >= kNumButtons)
{
return false;
}
if (oldButtonCode != NULL) if (oldButtonCode != NULL)
{ {
*oldButtonCode = (S9xButtonCode)-1; *oldButtonCode = (S9xButtonCode)-1;
@ -818,11 +808,11 @@ void SetButtonCodeForJoypadControl(uint32 vendorID, uint32 productID, uint8 inde
if ( info.min != info.max ) if ( info.min != info.max )
{ {
if (value < info.min) if (value <= info.min)
{ {
value = info.min; value = info.min;
} }
else if (value > info.max) else if (value >= info.max)
{ {
value = info.max; value = info.max;
} }
@ -846,8 +836,195 @@ void SetButtonCodeForJoypadControl(uint32 vendorID, uint32 productID, uint8 inde
*oldButtonCode = buttonCodeByJoypadInput[input]; *oldButtonCode = buttonCodeByJoypadInput[input];
} }
for (auto it = buttonCodeByJoypadInput.begin(); it != buttonCodeByJoypadInput.end();)
{
if (it->second == buttonCode && it->first.cookie.device == device)
{
if (overwrite)
{
buttonCodeByJoypadInput.erase(it++);
}
else
{
++it;
}
}
else
{
++it;
}
}
if (overwrite) if (overwrite)
{ {
buttonCodeByJoypadInput[input] = buttonCode; buttonCodeByJoypadInput[input] = buttonCode;
} }
return true;
}
void ClearButtonCodeForJoypad(uint32 vendorID, uint32 productID, uint32 index, S9xButtonCode buttonCode)
{
struct JoypadDevice device;
device.vendorID = vendorID;
device.productID = productID;
device.index = index;
for (auto it = buttonCodeByJoypadInput.begin(); it != buttonCodeByJoypadInput.end();)
{
if (it->first.cookie.device == device && it->second == buttonCode)
{
buttonCodeByJoypadInput.erase(it++);
}
else
{
++it;
}
}
}
std::string LabelForInput(uint32 vendorID, uint32 productID, uint32 cookie, int32 value)
{
struct JoypadDevice deviceStruct;
deviceStruct.productID = productID;
deviceStruct.vendorID = vendorID;
deviceStruct.index = 0;
struct JoypadCookie cookieStruct;
cookieStruct.device = deviceStruct;
cookieStruct.cookie = cookie;
auto it = infoByCookie.find(cookieStruct);
if (it != infoByCookie.end())
{
auto info = it->second;
switch(info.usage)
{
case kHIDUsage_GD_X:
{
if (value <= info.min)
{
return "X-";
}
else if (value >= info.max)
{
return "X+";
}
}
case kHIDUsage_GD_Y:
{
if (value <= info.min)
{
return "Y-";
}
else if (value >= info.max)
{
return "Y+";
}
}
case kHIDUsage_GD_Z:
{
if (value <= info.min)
{
return "Z-";
}
else if (value >= info.max)
{
return "Z+";
}
}
case kHIDUsage_GD_Rx:
{
if (value <= info.min)
{
return "Right X-";
}
else if (value >= info.max)
{
return "Right X+";
}
}
case kHIDUsage_GD_Ry:
{
if (value <= info.min)
{
return "Right Y-";
}
else if (value >= info.max)
{
return "Right Y+";
}
}
case kHIDUsage_GD_Rz:
{
if (value <= info.min)
{
return "Right Z-";
}
else if (value >= info.max)
{
return "Right Z+";
}
}
case kHIDUsage_GD_Hatswitch:
{
auto defaultIT = defaultHatValues.find(deviceStruct);
if (defaultIT != defaultHatValues.end())
{
auto hatDict = defaultIT->second;
auto hatIT = hatDict.find(value);
if ( hatIT != hatDict.end())
{
switch (hatIT->second)
{
case kUp:
return "D-Pad Up";
case kDown:
return "D-Pad Down";
case kLeft:
return "D-Pad Left";
case kRight:
return "D-Pad Right";
default:
break;
}
}
}
if (value == 1)
{
return "D-Pad Up";
}
else if (value == 2)
{
return "D-Pad Right";
}
else if (value == 4)
{
return "D-Pad Down";
}
else if (value == 8)
{
return "D-Pad Left";
}
}
default:
{
return std::string("Button " + std::to_string(info.index));
}
}
}
return std::to_string(cookie);
} }

View File

@ -37,5 +37,6 @@ void InitKeyboard (void);
void DeinitKeyboard (void); void DeinitKeyboard (void);
bool SetKeyCode(int16 keyCode, S9xButtonCode button, int8 player, int16 *oldKeyCode, S9xButtonCode *oldButton, int8 *oldPlayer); bool SetKeyCode(int16 keyCode, S9xButtonCode button, int8 player, int16 *oldKeyCode, S9xButtonCode *oldButton, int8 *oldPlayer);
void ClearKeyCode(S9xButtonCode buttonCode, int8 player);
#endif #endif

View File

@ -81,3 +81,20 @@ bool SetKeyCode(int16 keyCode, S9xButtonCode buttonCode, int8 player, int16 *old
return true; return true;
} }
void ClearKeyCode(S9xButtonCode buttonCode, int8 player)
{
if (player < 0 || player >= MAC_MAX_PLAYERS || buttonCode < 0 || buttonCode >= kNumButtons)
{
return;
}
for ( int i = 0; i < MAC_NUM_KEYCODES; ++i)
{
struct S9xButton button = keyCodes[i];
if (button.player == player && button.buttonCode == buttonCode)
{
keyCodes[i] = { -1, -1 };
}
}
}

View File

@ -181,8 +181,29 @@ uint64 GetMicroseconds(void);
void CopyPressedKeys(uint8 keys[MAC_MAX_PLAYERS][kNumButtons], uint8 gamepadButtons[MAC_MAX_PLAYERS][kNumButtons]); void CopyPressedKeys(uint8 keys[MAC_MAX_PLAYERS][kNumButtons], uint8 gamepadButtons[MAC_MAX_PLAYERS][kNumButtons]);
@interface S9xJoypad : NSObject
@property (nonatomic, assign) uint32 vendorID;
@property (nonatomic, assign) uint32 productID;
@property (nonatomic, assign) uint8 index;
@property (nonatomic, copy) NSString *name;
@end
@interface S9xJoypadInput : NSObject
@property (nonatomic, assign) uint32 cookie;
@property (nonatomic, assign) int32 value;
@property (nonatomic, assign) S9xButtonCode buttonCode;
@end
@protocol S9xInputDelegate <NSObject>
- (BOOL)handleInput:(S9xJoypadInput *)input fromJoypad:(S9xJoypad *)joypad;
@end
extern id<S9xInputDelegate> inputDelegate;
@interface S9xEngine : NSObject @interface S9xEngine : NSObject
@property (nonatomic, weak) id<S9xInputDelegate> inputDelegate;
- (void)start; - (void)start;
- (void)stop; - (void)stop;
@ -192,9 +213,22 @@ void CopyPressedKeys(uint8 keys[MAC_MAX_PLAYERS][kNumButtons], uint8 gamepadButt
- (void)resume; - (void)resume;
- (BOOL)setButton:(S9xButtonCode)button forKey:(int16)key player:(int8)player oldButton:(S9xButtonCode *)oldButton oldPlayer:(int8 *)oldPlayer oldKey:(int16 *)oldKey; - (BOOL)setButton:(S9xButtonCode)button forKey:(int16)key player:(int8)player oldButton:(S9xButtonCode *)oldButton oldPlayer:(int8 *)oldPlayer oldKey:(int16 *)oldKey;
- (void)clearButton:(S9xButtonCode)button forPlayer:(int8)player;
- (NSArray<S9xJoypad *> *)listJoypads;
- (void)setPlayer:(int8)player forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index oldPlayer:(int8 *)oldPlayer;
- (BOOL)setButton:(S9xButtonCode)button forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index cookie:(uint32)cookie value:(int32)value oldButton:(S9xButtonCode *)oldButton;
- (void)clearJoypadForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index;
- (void)clearJoypadForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index buttonCode:(S9xButtonCode)buttonCode;
- (NSArray<S9xJoypadInput *> *)getInputsForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index;
- (NSString *)labelForVendorID:(uint32)vendorID productID:(uint32)productID cookie:(uint32)cookie value:(int32)value;
- (BOOL)loadROM:(NSURL *)fileURL; - (BOOL)loadROM:(NSURL *)fileURL;
- (void)setVideoMode:(int)videoMode;
- (void)setShowFPS:(BOOL)showFPS;
@end @end
#endif #endif

View File

@ -174,6 +174,8 @@ CFStringRef multiCartPath[2];
IconRef macIconRef[118]; IconRef macIconRef[118];
#endif #endif
id<S9xInputDelegate> inputDelegate = nil;
typedef enum typedef enum
{ {
ToggleBG0, ToggleBG0,
@ -2076,7 +2078,7 @@ static void ProcessInput (void)
bool8 keys[MAC_MAX_PLAYERS][kNumButtons]; bool8 keys[MAC_MAX_PLAYERS][kNumButtons];
bool8 gamepadButtons[MAC_MAX_PLAYERS][kNumButtons]; bool8 gamepadButtons[MAC_MAX_PLAYERS][kNumButtons];
bool8 isok, fnbtn, altbtn, tcbtn; bool8 isok, fnbtn, altbtn, tcbtn;
static bool8 toggleff = false, lastTimeTT = false, lastTimeFn = false, ffUp = false, ffDown = false, ffUpSp = false, ffDownSp = false; static bool8 toggleff = false, lastTimeTT = false, lastTimeFn = false, ffUp = false, ffDown = false;
if (rejectinput) if (rejectinput)
return; return;
@ -3018,6 +3020,101 @@ void QuitWithFatalError ( NSString *message)
pauseEmulation = false; pauseEmulation = false;
} }
- (NSArray<S9xJoypad *> *)listJoypads
{
os_unfair_lock_lock(&keyLock);
NSMutableArray<S9xJoypad *> *joypads = [NSMutableArray new];
for (auto joypadStruct : ListJoypads())
{
S9xJoypad *joypad = [S9xJoypad new];
joypad.vendorID = joypadStruct.vendorID;
joypad.productID = joypadStruct.productID;
joypad.index = joypadStruct.index;
joypad.name = [[NSString alloc] initWithUTF8String:NameForDevice(joypadStruct).c_str()];
[joypads addObject:joypad];
}
[joypads sortUsingComparator:^NSComparisonResult(S9xJoypad *a, S9xJoypad *b)
{
NSComparisonResult result = [a.name compare:b.name];
if ( result == NSOrderedSame )
{
result = [@(a.vendorID) compare:@(b.vendorID)];
}
if ( result == NSOrderedSame )
{
result = [@(a.productID) compare:@(b.productID)];
}
if ( result == NSOrderedSame )
{
result = [@(a.index) compare:@(b.index)];
}
return result;
}];
os_unfair_lock_unlock(&keyLock);
return joypads;
}
- (void)setPlayer:(int8)player forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index oldPlayer:(int8 *)oldPlayer
{
os_unfair_lock_lock(&keyLock);
SetPlayerForJoypad(player, vendorID, productID, index, oldPlayer);
os_unfair_lock_unlock(&keyLock);
}
- (BOOL)setButton:(S9xButtonCode)button forVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index cookie:(uint32)cookie value:(int32)value oldButton:(S9xButtonCode *)oldButton
{
BOOL result = NO;
os_unfair_lock_lock(&keyLock);
result = SetButtonCodeForJoypadControl(vendorID, productID, index, cookie, value, button, true, oldButton);
os_unfair_lock_unlock(&keyLock);
return result;
}
- (void)clearJoypadForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index
{
os_unfair_lock_lock(&keyLock);
ClearJoypad(vendorID, productID, index);
os_unfair_lock_unlock(&keyLock);
}
- (void)clearJoypadForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index buttonCode:(S9xButtonCode)buttonCode
{
os_unfair_lock_lock(&keyLock);
ClearButtonCodeForJoypad(vendorID, productID, index, buttonCode);
os_unfair_lock_unlock(&keyLock);
}
- (NSArray<S9xJoypadInput *> *)getInputsForVendorID:(uint32)vendorID productID:(uint32)productID index:(uint32)index
{
os_unfair_lock_lock(&keyLock);
NSMutableArray<S9xJoypadInput *> *inputs = [NSMutableArray new];
std::unordered_map<struct JoypadInput, S9xButtonCode> buttonCodeMap = GetJuypadButtons(vendorID, productID, index);
for (auto it = buttonCodeMap.begin(); it != buttonCodeMap.end(); ++it)
{
S9xJoypadInput *input = [S9xJoypadInput new];
input.cookie = it->first.cookie.cookie;
input.value = it->first.value;
input.buttonCode = it->second;
[inputs addObject:input];
}
os_unfair_lock_unlock(&keyLock);
return inputs;
}
- (NSString *)labelForVendorID:(uint32)vendorID productID:(uint32)productID cookie:(uint32)cookie value:(int32)value
{
return [NSString stringWithUTF8String:LabelForInput(vendorID, productID, cookie, value).c_str()];
}
- (BOOL)setButton:(S9xButtonCode)button forKey:(int16)key player:(int8)player oldButton:(S9xButtonCode *)oldButton oldPlayer:(int8 *)oldPlayer oldKey:(int16 *)oldKey - (BOOL)setButton:(S9xButtonCode)button forKey:(int16)key player:(int8)player oldButton:(S9xButtonCode *)oldButton oldPlayer:(int8 *)oldPlayer oldKey:(int16 *)oldKey
{ {
BOOL result = NO; BOOL result = NO;
@ -3027,6 +3124,13 @@ void QuitWithFatalError ( NSString *message)
return result; return result;
} }
- (void)clearButton:(S9xButtonCode)button forPlayer:(int8)player
{
os_unfair_lock_lock(&keyLock);
ClearKeyCode(button, player);
os_unfair_lock_unlock(&keyLock);
}
- (BOOL)loadROM:(NSURL *)fileURL - (BOOL)loadROM:(NSURL *)fileURL
{ {
if ( SNES9X_OpenCart(fileURL) ) if ( SNES9X_OpenCart(fileURL) )
@ -3041,4 +3145,45 @@ void QuitWithFatalError ( NSString *message)
return NO; return NO;
} }
- (void)setShowFPS:(BOOL)showFPS
{
Settings.DisplayFrameRate = showFPS;
}
- (void)setVideoMode:(int)mode
{
os_unfair_lock_lock(&renderLock);
videoMode = mode;
os_unfair_lock_unlock(&renderLock);
}
@dynamic inputDelegate;
- (void)setInputDelegate:(id<S9xInputDelegate>)delegate
{
inputDelegate = delegate;
}
- (id<S9xInputDelegate>)inputDelegate
{
return inputDelegate;
}
@end
@implementation S9xJoypad
- (BOOL)isEqual:(id)object
{
if (![object isKindOfClass:[self class]])
{
return NO;
}
S9xJoypad *other = (S9xJoypad *)object;
return (self.vendorID == other.vendorID && self.productID == other.productID && self.index == other.index);
}
@end
@implementation S9xJoypadInput
@end @end