mirror of
https://github.com/OpenEmu/OpenEmu-SDK.git
synced 2025-11-01 10:15:12 +00:00
1110 lines
50 KiB
Objective-C
1110 lines
50 KiB
Objective-C
/*
|
|
Copyright (c) 2012, OpenEmu Team
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the OpenEmu Team nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#import "OESystemBindings.h"
|
|
|
|
#import "OEBindingsController_Internal.h"
|
|
#import "OEControlDescription.h"
|
|
#import "OEControllerDescription.h"
|
|
#import "OEDeviceDescription.h"
|
|
#import "OEDeviceHandler.h"
|
|
#import "OEHIDDeviceHandler.h"
|
|
#import "OEHIDEvent.h"
|
|
#import "OEHIDEvent_Internal.h"
|
|
#import "OEKeyBindingDescription.h"
|
|
#import "OESystemController.h"
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
static NSString *const _OEKeyboardPlayerBindingRepresentationsKey = @"keyboardPlayerBindings";
|
|
static NSString *const _OEControllerBindingRepresentationsKey = @"controllerBindings";
|
|
|
|
NSString *const OEGlobalButtonSaveState = @"OEGlobalButtonSaveState";
|
|
NSString *const OEGlobalButtonLoadState = @"OEGlobalButtonLoadState";
|
|
NSString *const OEGlobalButtonQuickSave = @"OEGlobalButtonQuickSave";
|
|
NSString *const OEGlobalButtonQuickLoad = @"OEGlobalButtonQuickLoad";
|
|
NSString *const OEGlobalButtonFullScreen = @"OEGlobalButtonFullScreen";
|
|
NSString *const OEGlobalButtonMute = @"OEGlobalButtonMute";
|
|
NSString *const OEGlobalButtonVolumeDown = @"OEGlobalButtonVolumeDown";
|
|
NSString *const OEGlobalButtonVolumeUp = @"OEGlobalButtonVolumeUp";
|
|
NSString *const OEGlobalButtonStop = @"OEGlobalButtonStop";
|
|
NSString *const OEGlobalButtonReset = @"OEGlobalButtonReset";
|
|
NSString *const OEGlobalButtonPause = @"OEGlobalButtonPause";
|
|
NSString *const OEGlobalButtonRewind = @"OEGlobalButtonRewind";
|
|
NSString *const OEGlobalButtonFastForward = @"OEGlobalButtonFastForward";
|
|
NSString *const OEGlobalButtonSlowMotion = @"OEGlobalButtonSlowMotion";
|
|
NSString *const OEGlobalButtonStepFrameBackward = @"OEGlobalButtonStepFrameBackward";
|
|
NSString *const OEGlobalButtonStepFrameForward = @"OEGlobalButtonStepFrameForward";
|
|
NSString *const OEGlobalButtonDisplayMode = @"OEGlobalButtonDisplayMode";
|
|
NSString *const OEGlobalButtonScreenshot = @"OEGlobalButtonScreenshot";
|
|
|
|
@interface OEHIDEvent ()
|
|
- (OEHIDEvent *)OE_eventWithDeviceHandler:(OEDeviceHandler *)aDeviceHandler;
|
|
@end
|
|
|
|
@interface OESystemBindings ()
|
|
{
|
|
NSMutableSet<id<OESystemBindingsObserver>> *_bindingsObservers;
|
|
|
|
NSMutableDictionary<NSNumber *, OEKeyboardPlayerBindings *> *_keyboardPlayerBindings;
|
|
|
|
// Map devices identifiers to an array of saved bindings
|
|
// Each object in the array represent settings in the order they were added to the app
|
|
// When a new device with the same manufacturer is plugged in, it inherits the settings
|
|
// of the player they were first setup or the first player if the device doesn't exists
|
|
NSMutableDictionary<id, NSMutableArray *> *_parsedManufacturerBindings;
|
|
NSMutableDictionary<NSString *, NSArray<NSDictionary<NSString *, id> *> *> *_unparsedManufactuerBindings;
|
|
NSMutableDictionary<OEDeviceHandler *, OEDevicePlayerBindings *> *_deviceHandlersToBindings;
|
|
|
|
NSMutableDictionary<NSNumber *, OEDevicePlayerBindings *> *_devicePlayerBindings;
|
|
|
|
NSMutableDictionary<OEControllerDescription *, OEDevicePlayerBindings *> *_defaultDeviceBindings;
|
|
|
|
OEDevicePlayerBindings *_emptyConfiguration;
|
|
}
|
|
|
|
- (void)OE_notifyObserversDidSetEvent:(OEHIDEvent *)anEvent forBindingKey:(id)bindingKey playerNumber:(NSUInteger)playerNumber __attribute__((nonnull));
|
|
- (void)OE_notifyObserversDidUnsetEvent:(OEHIDEvent *)anEvent forBindingKey:(id)bindingKey playerNumber:(NSUInteger)playerNumber __attribute__((nonnull));
|
|
- (id)OE_playerBindings:(OEKeyboardPlayerBindings *)sender didSetKeyboardEvent:(OEHIDEvent *)anEvent forKey:(NSString *)keyName __attribute__((nonnull));
|
|
- (id)OE_playerBindings:(OEDevicePlayerBindings *)sender didSetDeviceEvent:(OEHIDEvent *)anEvent forKey:(NSString *)keyName __attribute__((nonnull));
|
|
|
|
@end
|
|
|
|
@implementation OESystemBindings
|
|
|
|
- (id)OE_initWithBindingsController:(nullable OEBindingsController *)parentController systemController:(OESystemController *)aController dictionaryRepresentation:(NSDictionary *)aDictionary
|
|
{
|
|
if(aController == nil) return nil;
|
|
|
|
if((self = [super init]))
|
|
{
|
|
_parsedManufacturerBindings = [NSMutableDictionary dictionary];
|
|
_defaultDeviceBindings = [NSMutableDictionary dictionary];
|
|
_deviceHandlersToBindings = [NSMutableDictionary dictionary];
|
|
|
|
_keyboardPlayerBindings = [NSMutableDictionary dictionary];
|
|
_devicePlayerBindings = [NSMutableDictionary dictionary];
|
|
|
|
_bindingsObservers = [NSMutableSet set];
|
|
|
|
_bindingsController = parentController;
|
|
_systemController = aController;
|
|
|
|
if(aDictionary != nil) [self OE_setUpKeyboardBindingsWithRepresentations:aDictionary[_OEKeyboardPlayerBindingRepresentationsKey]];
|
|
else [self OE_registerDefaultControls:[_systemController defaultKeyboardControls]];
|
|
|
|
_unparsedManufactuerBindings = [aDictionary[_OEControllerBindingRepresentationsKey] mutableCopy];
|
|
|
|
_emptyConfiguration = [[OEDevicePlayerBindings alloc] OE_initWithSystemBindings:self playerNumber:0 deviceHandler:nil];
|
|
[_emptyConfiguration OE_setBindingEvents:@{ }];
|
|
[_emptyConfiguration OE_setBindingDescriptions:[self OE_stringValuesForBindings:nil possibleKeys:_systemController.allKeyBindingsDescriptions]];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
#pragma mark - System Bindings general use methods
|
|
|
|
- (NSUInteger)numberOfPlayers
|
|
{
|
|
return [_systemController numberOfPlayers];
|
|
}
|
|
|
|
#pragma mark - Parse the receiver's representation dictionaries
|
|
|
|
- (void)OE_registerDefaultControls:(NSDictionary *)defaultControls
|
|
{
|
|
if([defaultControls count] == 0) return;
|
|
|
|
// We always assume player 1, if we want to have multiple players, we will have to use an NSArray instead
|
|
OEKeyboardPlayerBindings *bindings = [self keyboardPlayerBindingsForPlayer:1];
|
|
|
|
[defaultControls enumerateKeysAndObjectsUsingBlock:
|
|
^(NSString *key, NSNumber *obj, BOOL *stop)
|
|
{
|
|
OEHIDEvent *theEvent = [OEHIDEvent keyEventWithTimestamp:0
|
|
keyCode:[obj unsignedIntValue]
|
|
state:NSOnState
|
|
cookie:OEUndefinedCookie];
|
|
|
|
[bindings assignEvent:theEvent toKeyWithName:key];
|
|
}];
|
|
}
|
|
|
|
- (void)OE_setUpKeyboardBindingsWithRepresentations:(NSArray *)representations;
|
|
{
|
|
if(representations == nil) return;
|
|
|
|
// Convert keyboard bindings
|
|
_keyboardPlayerBindings = [[NSMutableDictionary alloc] initWithCapacity:[representations count]];
|
|
|
|
[representations enumerateObjectsUsingBlock:^(NSDictionary *encoded, NSUInteger idx, BOOL *stop) {
|
|
NSMutableDictionary *decodedBindings = [NSMutableDictionary dictionaryWithCapacity:[encoded count]];
|
|
|
|
[encoded enumerateKeysAndObjectsUsingBlock:^(NSString *keyName, id value, BOOL *stop) {
|
|
OEKeyBindingDescription *desc = _systemController.allKeyBindingsDescriptions[keyName];
|
|
|
|
if(desc)
|
|
decodedBindings[desc] = [OEHIDEvent eventWithDictionaryRepresentation:value];
|
|
}];
|
|
|
|
OEKeyboardPlayerBindings *controller = [[OEKeyboardPlayerBindings alloc] OE_initWithSystemBindings:self playerNumber:idx + 1];
|
|
|
|
[controller OE_setBindingEvents:decodedBindings];
|
|
[controller OE_setBindingDescriptions:[self OE_stringValuesForBindings:decodedBindings possibleKeys:_systemController.keyBindingsDescriptions]];
|
|
|
|
_keyboardPlayerBindings[@([controller playerNumber])] = controller;
|
|
}];
|
|
}
|
|
|
|
- (void)OE_parseDefaultControlValuesForControllerDescription:(OEControllerDescription *)controllerDescription
|
|
{
|
|
if(_defaultDeviceBindings[controllerDescription] != nil) return;
|
|
|
|
NSDictionary *representation = [_systemController defaultDeviceControls][[controllerDescription identifier]];
|
|
if(representation == nil) return;
|
|
|
|
_defaultDeviceBindings[controllerDescription] = [self OE_parsedDevicePlayerBindingsForRepresentation:representation withControllerDescription:controllerDescription useValueIdentifier:NO];
|
|
}
|
|
|
|
- (void)OE_parseManufacturerControlValuesForDeviceDescription:(OEDeviceDescription *)deviceDescription
|
|
{
|
|
if(_parsedManufacturerBindings[deviceDescription] != nil) return;
|
|
|
|
OEControllerDescription *controllerDescription = [deviceDescription controllerDescription];
|
|
NSString *genericDeviceIdentifier = [deviceDescription genericDeviceIdentifier];
|
|
NSArray<NSDictionary *> *genericDeviceBindingsToParse = _unparsedManufactuerBindings[genericDeviceIdentifier];
|
|
[_unparsedManufactuerBindings removeObjectForKey:genericDeviceIdentifier];
|
|
|
|
if(genericDeviceBindingsToParse == nil && _parsedManufacturerBindings[controllerDescription] != nil) return;
|
|
|
|
NSMutableArray<OEDevicePlayerBindings *> *parsedBindings = _parsedManufacturerBindings[controllerDescription] ? : [NSMutableArray array];
|
|
|
|
for(NSDictionary<NSString *, id> *representation in genericDeviceBindingsToParse)
|
|
[parsedBindings addObject:[self OE_parsedDevicePlayerBindingsForRepresentation:representation withControllerDescription:controllerDescription useValueIdentifier:YES]];
|
|
|
|
if(![controllerDescription isGeneric])
|
|
{
|
|
[self OE_parseDefaultControlValuesForControllerDescription:controllerDescription];
|
|
|
|
for(NSDictionary<NSString *, id> *representation in _unparsedManufactuerBindings[[controllerDescription identifier]])
|
|
[parsedBindings addObject:[self OE_parsedDevicePlayerBindingsForRepresentation:representation withControllerDescription:controllerDescription useValueIdentifier:NO]];
|
|
[_unparsedManufactuerBindings removeObjectForKey:[controllerDescription identifier]];
|
|
_parsedManufacturerBindings[controllerDescription] = parsedBindings;
|
|
}
|
|
|
|
_parsedManufacturerBindings[deviceDescription] = parsedBindings;
|
|
}
|
|
|
|
- (OEDevicePlayerBindings *)OE_parsedDevicePlayerBindingsForRepresentation:(NSDictionary<NSString *, id> *)representation withControllerDescription:(OEControllerDescription *)controllerDescription useValueIdentifier:(BOOL)useValueIdentifier
|
|
{
|
|
NSMutableDictionary<OEBindingDescription *, OEControlValueDescription *> *rawBindings = [NSMutableDictionary dictionaryWithCapacity:[_systemController.allKeyBindingsDescriptions count]];
|
|
[_systemController.allKeyBindingsDescriptions enumerateKeysAndObjectsUsingBlock:^(NSString *keyName, OEKeyBindingDescription *keyDesc, BOOL *stop) {
|
|
id controlIdentifier = representation[keyName];
|
|
if(controlIdentifier == nil)
|
|
return;
|
|
|
|
NSAssert(![controlIdentifier isKindOfClass:[NSNumber class]], @"Default for key %@ in System %@ was not converted to the new system.", keyName, [_systemController systemName]);
|
|
|
|
OEControlValueDescription *controlValue = [controllerDescription controlValueDescriptionForRepresentation:controlIdentifier];
|
|
OEHIDEvent *event = [controlValue event];
|
|
|
|
NSAssert(controlValue != nil, @"Unknown control value for identifier: '%@' associated with key name: '%@'", controlIdentifier, keyName);
|
|
|
|
if([event type] == OEHIDEventTypeHatSwitch && [keyDesc hatSwitchGroup] != nil)
|
|
{
|
|
// Sync the key direction with the hat switch direction, in case they're different.
|
|
enum { NORTH, EAST, SOUTH, WEST, HAT_COUNT };
|
|
|
|
OEHIDEventHatDirection direction = [event hatDirection];
|
|
NSUInteger currentDir = NORTH;
|
|
|
|
if(direction & OEHIDEventHatDirectionNorth) currentDir = NORTH;
|
|
if(direction & OEHIDEventHatDirectionEast) currentDir = EAST;
|
|
if(direction & OEHIDEventHatDirectionSouth) currentDir = SOUTH;
|
|
if(direction & OEHIDEventHatDirectionWest) currentDir = WEST;
|
|
|
|
keyDesc = [[keyDesc hatSwitchGroup] keys][currentDir];
|
|
}
|
|
|
|
rawBindings[[self OE_keyIdentifierForKeyDescription:keyDesc event:event]] = controlValue;
|
|
}];
|
|
|
|
OEDevicePlayerBindings *controller = [[OEDevicePlayerBindings alloc] OE_initWithSystemBindings:self playerNumber:0 deviceHandler:nil];
|
|
[controller OE_setBindingEvents:rawBindings];
|
|
[controller OE_setBindingDescriptions:[self OE_stringValuesForBindings:rawBindings possibleKeys:_systemController.allKeyBindingsDescriptions]];
|
|
|
|
return controller;
|
|
}
|
|
|
|
- (OEBindingDescription *)OE_keyIdentifierForKeyDescription:(OEKeyBindingDescription *)keyDescription event:(OEHIDEvent *)event;
|
|
{
|
|
OEBindingDescription *insertedKey = keyDescription;
|
|
OEHIDEventType eventType = [event type];
|
|
if(eventType == OEHIDEventTypeAxis || eventType == OEHIDEventTypeHatSwitch)
|
|
{
|
|
OEKeyBindingGroupDescription *keyGroup = (eventType == OEHIDEventTypeAxis ? keyDescription.axisGroup : keyDescription.hatSwitchGroup);
|
|
if(keyGroup != nil)
|
|
insertedKey = [keyGroup orientedKeyGroupWithBaseKey:keyDescription];
|
|
}
|
|
|
|
return insertedKey;
|
|
}
|
|
|
|
#pragma mark - Construct the receiver's representation dictionaries
|
|
|
|
- (NSDictionary<NSString *, __kindof id<OEPropertyList>> *)OE_dictionaryRepresentation;
|
|
{
|
|
NSMutableDictionary<NSString *, __kindof id<OEPropertyList>> *dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
|
|
|
|
void (^addToDictionary)(NSString *key, id value) =
|
|
^(NSString *key, __kindof id<OEPropertyList> value)
|
|
{
|
|
if(value)
|
|
dictionary[key] = value;
|
|
};
|
|
|
|
addToDictionary(_OEControllerBindingRepresentationsKey, [self OE_dictionaryRepresentationForControllerBindings]);
|
|
addToDictionary(_OEKeyboardPlayerBindingRepresentationsKey, [self OE_arrayRepresentationForKeyboardBindings]);
|
|
|
|
return [dictionary copy];
|
|
}
|
|
|
|
- (NSDictionary<NSString *, __kindof id<OEPropertyList>> *)OE_dictionaryRepresentationForControllerBindings;
|
|
{
|
|
NSMutableDictionary<NSString *, NSArray *> *ret = [_unparsedManufactuerBindings mutableCopy] ? : [NSMutableDictionary dictionaryWithCapacity:[_parsedManufacturerBindings count]];
|
|
|
|
[_parsedManufacturerBindings enumerateKeysAndObjectsUsingBlock:
|
|
^(id description, NSArray *controllers, BOOL *stop)
|
|
{
|
|
BOOL isDeviceDescription = [description isKindOfClass:[OEDeviceDescription class]];
|
|
if( ( isDeviceDescription && ![[description controllerDescription] isGeneric])
|
|
|| (!isDeviceDescription && [description isGeneric]))
|
|
return;
|
|
|
|
NSMutableArray<NSDictionary<NSString *, id> *> *controllerRepresentations = [NSMutableArray arrayWithCapacity:[controllers count]];
|
|
for(OEDevicePlayerBindings *controller in controllers)
|
|
{
|
|
NSDictionary<__kindof OEBindingDescription *, OEControlValueDescription *> *rawBindings = [controller bindingEvents];
|
|
NSMutableDictionary<NSString *, id> *bindingRepresentations = [NSMutableDictionary dictionaryWithCapacity:[rawBindings count]];
|
|
[rawBindings enumerateKeysAndObjectsUsingBlock:
|
|
^(__kindof OEBindingDescription *key, OEControlValueDescription *obj, BOOL *stop)
|
|
{
|
|
NSString *saveKey = nil;
|
|
if([key isKindOfClass:[OEKeyBindingDescription class]])
|
|
saveKey = [key name];
|
|
else if([key isKindOfClass:[OEOrientedKeyGroupBindingDescription class]])
|
|
saveKey = [[key baseKey] name];
|
|
else
|
|
{
|
|
NSLog(@"WARNING: Unkown Bindings key");
|
|
NSLog(@"%@", key);
|
|
saveKey = @"";
|
|
}
|
|
|
|
bindingRepresentations[saveKey] = obj.representation;
|
|
}];
|
|
|
|
[controllerRepresentations addObject:bindingRepresentations];
|
|
}
|
|
|
|
ret[[description identifier]] = controllerRepresentations;
|
|
}];
|
|
|
|
return ret;
|
|
}
|
|
|
|
- (NSMutableArray<NSDictionary<NSString *, __kindof id<OEPropertyList>> *> *)OE_arrayRepresentationForKeyboardBindings
|
|
{
|
|
NSMutableArray<NSDictionary<NSString *, __kindof id<OEPropertyList>> *> *ret = [NSMutableArray arrayWithCapacity:[_keyboardPlayerBindings count]];
|
|
NSUInteger numberOfPlayers = [self numberOfPlayers];
|
|
NSUInteger lastValidPlayerNumber = 1;
|
|
|
|
for(NSUInteger i = 1; i <= numberOfPlayers; i++)
|
|
{
|
|
NSDictionary *rawBindings = [_keyboardPlayerBindings[@(i)] bindingEvents];
|
|
NSMutableDictionary<NSString *, __kindof id<OEPropertyList>> *bindingRepresentations = [NSMutableDictionary dictionaryWithCapacity:[rawBindings count]];
|
|
[rawBindings enumerateKeysAndObjectsUsingBlock:
|
|
^(OEKeyBindingDescription *key, OEHIDEvent *event, BOOL *stop)
|
|
{
|
|
bindingRepresentations[[key name]] = event.dictionaryRepresentation;
|
|
}];
|
|
|
|
ret[[ret count]] = bindingRepresentations;
|
|
|
|
if(rawBindings != nil) lastValidPlayerNumber = i;
|
|
}
|
|
|
|
if(lastValidPlayerNumber < numberOfPlayers)
|
|
[ret removeObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(lastValidPlayerNumber, numberOfPlayers - lastValidPlayerNumber)]];
|
|
|
|
return ret;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Player Bindings Controller Representation
|
|
|
|
- (NSUInteger)playerNumberForEvent:(OEHIDEvent *)anEvent;
|
|
{
|
|
if([anEvent type] == OEHIDEventTypeKeyboard || [anEvent deviceHandler] == nil) return 0;
|
|
|
|
OEDeviceHandler *handler = [anEvent deviceHandler];
|
|
|
|
return [[[_devicePlayerBindings keysOfEntriesPassingTest:
|
|
^ BOOL (NSNumber *key, OEDevicePlayerBindings *obj, BOOL *stop)
|
|
{
|
|
if([obj deviceHandler] == handler)
|
|
{
|
|
*stop = YES;
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}] anyObject] integerValue];
|
|
}
|
|
|
|
- (OEDevicePlayerBindings *)devicePlayerBindingsForPlayer:(NSUInteger)playerNumber;
|
|
{
|
|
if(playerNumber == 0 || playerNumber > [self numberOfPlayers]) return nil;
|
|
|
|
return _devicePlayerBindings[@(playerNumber)];
|
|
}
|
|
|
|
- (OEKeyboardPlayerBindings *)keyboardPlayerBindingsForPlayer:(NSUInteger)playerNumber;
|
|
{
|
|
if(playerNumber == 0 || playerNumber > [self numberOfPlayers]) return nil;
|
|
|
|
OEKeyboardPlayerBindings *ret = _keyboardPlayerBindings[@(playerNumber)];
|
|
|
|
if(ret == nil)
|
|
{
|
|
[self willChangeValueForKey:@"keyboardPlayerBindings"];
|
|
ret = [[OEKeyboardPlayerBindings alloc] OE_initWithSystemBindings:self playerNumber:playerNumber];
|
|
|
|
// At this point, if a keyboard bindings doesn't exist, it means none were saved for this player
|
|
[ret OE_setBindingDescriptions:[self OE_stringValuesForBindings:nil possibleKeys:_systemController.keyBindingsDescriptions]];
|
|
[ret OE_setBindingEvents:@{ }];
|
|
|
|
_keyboardPlayerBindings[@(playerNumber)] = ret;
|
|
|
|
[self didChangeValueForKey:@"keyboardPlayerBindings"];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
- (NSUInteger)playerForDeviceHandler:(OEDeviceHandler *)deviceHandler;
|
|
{
|
|
return [[self devicePlayerBindingsForDeviceHandler:deviceHandler] playerNumber];
|
|
}
|
|
|
|
- (OEDeviceHandler *)deviceHandlerForPlayer:(NSUInteger)playerNumber;
|
|
{
|
|
if(playerNumber == 0 || playerNumber > [self numberOfPlayers]) return nil;
|
|
|
|
OEDevicePlayerBindings *bindings = _devicePlayerBindings[@(playerNumber)];
|
|
|
|
NSAssert([bindings playerNumber] == playerNumber, @"Expected player number for bindings: %ld, got: %ld.", playerNumber, [bindings playerNumber]);
|
|
|
|
return [bindings deviceHandler];
|
|
}
|
|
|
|
- (OEDevicePlayerBindings *)devicePlayerBindingsForDeviceHandler:(OEDeviceHandler *)deviceHandler;
|
|
{
|
|
return _deviceHandlersToBindings[deviceHandler];
|
|
}
|
|
|
|
- (void)setDeviceHandler:(OEDeviceHandler *)deviceHandler forPlayer:(NSUInteger)playerNumber;
|
|
{
|
|
// Find the two bindings to switch.
|
|
OEDevicePlayerBindings *newBindings = [self devicePlayerBindingsForDeviceHandler:deviceHandler];
|
|
NSAssert(newBindings != nil, @"A device handler without device player bindings?!");
|
|
if([newBindings playerNumber] == playerNumber) return;
|
|
|
|
OEDevicePlayerBindings *oldBindings = [self devicePlayerBindingsForPlayer:playerNumber];
|
|
|
|
// Notify observers to remove all bindings to the old player number of each devices.
|
|
if(oldBindings != nil) [self OE_notifyObserversForRemovedDeviceBindings:oldBindings];
|
|
[self OE_notifyObserversForRemovedDeviceBindings:newBindings];
|
|
|
|
// Clean up the keys for each player keys so there's no confusion.
|
|
if(oldBindings != nil) [_devicePlayerBindings removeObjectForKey:@([oldBindings playerNumber])];
|
|
[_devicePlayerBindings removeObjectForKey:@([newBindings playerNumber])];
|
|
|
|
// Change the player numbers.
|
|
if(oldBindings != nil) [oldBindings OE_setPlayerNumber:[newBindings playerNumber]];
|
|
[newBindings OE_setPlayerNumber:playerNumber];
|
|
|
|
// Move the bindings in the array.
|
|
if(oldBindings != nil) _devicePlayerBindings[@([oldBindings playerNumber])] = oldBindings;
|
|
_devicePlayerBindings[@(playerNumber)] = newBindings;
|
|
|
|
// Notify observers to add all bindings for the new player layout.
|
|
if(oldBindings != nil) [self OE_notifyObserversForAddedDeviceBindings:oldBindings];
|
|
[self OE_notifyObserversForAddedDeviceBindings:newBindings];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Preference Panel Representation Helper Methods
|
|
|
|
- (NSString *)OE_descriptionForEvent:(id)anEvent;
|
|
{
|
|
if(anEvent == nil) return nil;
|
|
|
|
// Handle device events.
|
|
if([anEvent isKindOfClass:[OEControlValueDescription class]])
|
|
return [anEvent name];
|
|
|
|
// Handle keyboard events.
|
|
return ([anEvent respondsToSelector:@selector(displayDescription)]
|
|
? [anEvent displayDescription]
|
|
: [anEvent description]);
|
|
}
|
|
|
|
- (NSDictionary<NSString *, NSString *> *)OE_stringValuesForBindings:(nullable NSDictionary<__kindof OEBindingDescription *, OEControlValueDescription *> *)bindingsToConvert possibleKeys:(NSDictionary<NSString *, OEKeyBindingDescription *> *)nameToKeyMap;
|
|
{
|
|
NSMutableDictionary<NSString *, NSString *> *ret = [NSMutableDictionary dictionaryWithCapacity:[nameToKeyMap count]];
|
|
|
|
[bindingsToConvert enumerateKeysAndObjectsUsingBlock:
|
|
^(__kindof OEBindingDescription *key, OEControlValueDescription *obj, BOOL *stop)
|
|
{
|
|
NSAssert(![obj isKindOfClass:[OEControlDescription class]], @"A OEControlDescription object was wrongfully associated to the key '%@'.", key);
|
|
|
|
if([key isKindOfClass:[OEKeyBindingDescription class]])
|
|
// General case - the event value is attached to the key-name for bindings
|
|
[ret setObject:[self OE_descriptionForEvent:obj] forKey:[key name]];
|
|
else if([key isKindOfClass:[OEOrientedKeyGroupBindingDescription class]])
|
|
{
|
|
OEOrientedKeyGroupBindingDescription *group = key;
|
|
OEControlValueDescription *controlValue = obj;
|
|
OEHIDEvent *event = [controlValue event];
|
|
|
|
// In case of key-group, we need to create multiple key-strings for each cases of the key, usually 2 for axis type and 4 for hat switch type
|
|
NSAssert([controlValue isKindOfClass:[OEControlValueDescription class]], @"Only OEControlValueDescription can be associated with binding groups, got: %@ %@", [event class], event);
|
|
|
|
switch([group type])
|
|
{
|
|
case OEKeyGroupTypeAxis :
|
|
{
|
|
NSAssert([event type] == OEHIDEventTypeAxis, @"Excepting OEHIDEventTypeAxis event type for Axis key group, got: %@", NSStringFromOEHIDEventType([event type]));
|
|
|
|
ret[[[group baseKey] name]] = [controlValue name];
|
|
ret[[[group oppositeKey] name]] = [[controlValue associatedControlValueDescriptionForEvent:[event axisEventWithOppositeDirection]] name];
|
|
}
|
|
break;
|
|
case OEKeyGroupTypeHatSwitch :
|
|
{
|
|
NSAssert([event type] == OEHIDEventTypeHatSwitch, @"Excepting OEHIDEventTypeHatSwitch event type for Axis key group, got: %@", NSStringFromOEHIDEventType([event type]));
|
|
|
|
enum { NORTH, EAST, SOUTH, WEST, HAT_COUNT };
|
|
|
|
OEHIDEventHatDirection direction = [event hatDirection];
|
|
__block NSUInteger currentDir = NORTH;
|
|
|
|
if(direction & OEHIDEventHatDirectionNorth) currentDir = NORTH;
|
|
if(direction & OEHIDEventHatDirectionEast) currentDir = EAST;
|
|
if(direction & OEHIDEventHatDirectionSouth) currentDir = SOUTH;
|
|
if(direction & OEHIDEventHatDirectionWest) currentDir = WEST;
|
|
|
|
static OEHIDEventHatDirection dirs[HAT_COUNT] = { OEHIDEventHatDirectionNorth, OEHIDEventHatDirectionEast, OEHIDEventHatDirectionSouth, OEHIDEventHatDirectionWest };
|
|
|
|
[group enumerateKeysFromBaseKeyUsingBlock:
|
|
^(OEKeyBindingDescription *key, BOOL *stop)
|
|
{
|
|
ret[[key name]] = [[controlValue associatedControlValueDescriptionForEvent:[event hatSwitchEventWithDirection:dirs[currentDir % HAT_COUNT]]] name];
|
|
|
|
currentDir++;
|
|
}];
|
|
}
|
|
break;
|
|
default :
|
|
NSAssert(NO, @"Unknown Key Group Type");
|
|
break;
|
|
}
|
|
}
|
|
}];
|
|
|
|
return ret;
|
|
}
|
|
|
|
- (__kindof OEBindingDescription *)OE_playerBindings:(__kindof OEPlayerBindings *)sender didAssignEvent:(OEHIDEvent *)anEvent toKeyWithName:(NSString *)keyName;
|
|
{
|
|
NSAssert([anEvent isKindOfClass:[OEHIDEvent class]], @"Can only set OEHIDEvents for bindings.");
|
|
|
|
// Make a copy because events are reused to remember the previous state/timestamp
|
|
anEvent = [anEvent copy];
|
|
|
|
__kindof OEBindingDescription *ret = ([anEvent type] == OEHIDEventTypeKeyboard
|
|
? [self OE_playerBindings:sender didSetKeyboardEvent:anEvent forKey:keyName]
|
|
: [self OE_playerBindings:sender didSetDeviceEvent:anEvent forKey:keyName]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
- (void)OE_playerBindings:(__kindof OEPlayerBindings *)sender didRemoveEventForKeyWithName:(NSString *)aKey;
|
|
{
|
|
if([sender isKindOfClass:[OEKeyboardPlayerBindings class]])
|
|
[self OE_playerBindings:sender didUnsetKeyboardEventForKey:aKey];
|
|
else if([sender isKindOfClass:[OEDevicePlayerBindings class]])
|
|
[self OE_playerBindings:sender didUnsetDeviceEventForKey:aKey];
|
|
}
|
|
|
|
- (__kindof OEBindingDescription *)OE_playerBindings:(OEKeyboardPlayerBindings *)sender didSetKeyboardEvent:(OEHIDEvent *)anEvent forKey:(NSString *)keyName;
|
|
{
|
|
NSAssert([sender isKindOfClass:[OEKeyboardPlayerBindings class]], @"Invalid sender: OEKeyboardPlayerBindings expected, got: %@ %@", [sender class], sender);
|
|
|
|
OEKeyBindingDescription *keyDesc = _systemController.allKeyBindingsDescriptions[keyName];
|
|
NSAssert(keyDesc != nil, @"Could not find Key Binding Description for key with name \"%@\" in system \"%@\"", keyName, [[self systemController] systemIdentifier]);
|
|
|
|
// Trying to set the same event to the same key, ignore it
|
|
if([[[sender bindingEvents] objectForKey:keyDesc] isEqual:anEvent]) return keyDesc;
|
|
|
|
[_keyboardPlayerBindings enumerateKeysAndObjectsUsingBlock:
|
|
^(NSNumber *key, OEKeyboardPlayerBindings *playerBindings, BOOL *stop)
|
|
{
|
|
NSArray *keys = [[playerBindings bindingEvents] allKeysForObject:anEvent];
|
|
NSAssert([keys count] <= 1, @"More than one key is attached to the same event: %@ -> %@", anEvent, keys);
|
|
|
|
OEKeyBindingDescription *desc = [keys lastObject];
|
|
if(desc != nil)
|
|
{
|
|
[playerBindings OE_setBindingDescription:nil forKey:[desc name]];
|
|
[playerBindings OE_setBindingEvent:nil forKey:desc];
|
|
|
|
[self OE_notifyObserversDidUnsetEvent:anEvent forBindingKey:desc playerNumber:[desc isSystemWide] ? 0 : [playerBindings playerNumber]];
|
|
}
|
|
}];
|
|
|
|
id previousBinding = [sender OE_bindingEventForKey:keyDesc];
|
|
if(previousBinding != nil) [self OE_notifyObserversDidUnsetEvent:previousBinding forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
|
|
NSString *eventString = [self OE_descriptionForEvent:anEvent];
|
|
|
|
[sender OE_setBindingDescription:eventString forKey:keyName];
|
|
[sender OE_setBindingEvent:anEvent forKey:keyDesc];
|
|
|
|
[self OE_notifyObserversDidSetEvent:anEvent forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
[[self bindingsController] OE_setRequiresSynchronization];
|
|
|
|
return keyDesc;
|
|
}
|
|
|
|
- (void)OE_playerBindings:(OEKeyboardPlayerBindings *)sender didUnsetKeyboardEventForKey:(NSString *)keyName;
|
|
{
|
|
NSAssert([sender isKindOfClass:[OEKeyboardPlayerBindings class]], @"Invalid sender: OEKeyboardPlayerBindings expected, got: %@ %@", [sender class], sender);
|
|
|
|
OEKeyBindingDescription *keyDesc = _systemController.allKeyBindingsDescriptions[keyName];
|
|
NSAssert(keyDesc != nil, @"Could not find Key Binding Description for key with name \"%@\" in system \"%@\"", keyName, [[self systemController] systemIdentifier]);
|
|
|
|
OEHIDEvent *event = [sender bindingEvents][keyDesc];
|
|
if(event == nil) return;
|
|
|
|
[sender OE_setBindingDescription:nil forKey:[keyDesc name]];
|
|
[sender OE_setBindingEvent:nil forKey:keyDesc];
|
|
|
|
[self OE_notifyObserversDidUnsetEvent:event forBindingKey:keyDesc playerNumber:[keyDesc isSystemWide] ? 0 : [sender playerNumber]];
|
|
}
|
|
|
|
- (__kindof OEBindingDescription *)OE_playerBindings:(OEDevicePlayerBindings *)sender didSetDeviceEvent:(OEHIDEvent *)anEvent forKey:(NSString *)keyName;
|
|
{
|
|
NSAssert([sender isKindOfClass:[OEDevicePlayerBindings class]], @"Invalid sender: OEKeyboardPlayerBindings expected, got: %@ %@", [sender class], sender);
|
|
|
|
NSAssert(_systemController.allKeyBindingsDescriptions[keyName] != nil, @"Could not find Key Binding Description for key with name \"%@\" in system \"%@\"", keyName, [[self systemController] systemIdentifier]);
|
|
|
|
OEControlValueDescription *valueDesc = [[[sender deviceHandler] controllerDescription] controlValueDescriptionForEvent:anEvent];
|
|
NSAssert(valueDesc != nil, @"Controller type '%@' does not recognize the event '%@', when attempting to set the key with name: '%@'.", [[[sender deviceHandler] controllerDescription] identifier], anEvent, keyName);
|
|
|
|
// Sender is based on another device player bindings,
|
|
// it needs to be made independent and added to the manufacturer list.
|
|
if([sender OE_isDependent])
|
|
{
|
|
[sender OE_makeIndependent];
|
|
|
|
[_parsedManufacturerBindings[[[sender deviceHandler] deviceDescription]] addObject:sender];
|
|
}
|
|
|
|
// Search for keys bound to the same event.
|
|
NSArray<__kindof OEBindingDescription *> *keys = [[sender bindingEvents] allKeysForObject:valueDesc];
|
|
if([keys count] == 0)
|
|
{
|
|
keys = [[[sender bindingEvents] keysOfEntriesPassingTest:
|
|
^ BOOL (OEOrientedKeyGroupBindingDescription *key, OEControlValueDescription *obj, BOOL *stop)
|
|
{
|
|
if(![key isKindOfClass:[OEOrientedKeyGroupBindingDescription class]]) return NO;
|
|
|
|
return [obj controlDescription] == [valueDesc controlDescription];
|
|
}] allObjects];
|
|
}
|
|
|
|
// Remove bindings for these keys
|
|
NSAssert([keys count] <= 1, @"More than one key is attached to the same event: %@ -> %@", anEvent, keys);
|
|
__kindof OEBindingDescription *keyDesc = [keys lastObject];
|
|
|
|
if(keyDesc != nil)
|
|
{
|
|
NSArray<NSString *> *keys = nil;
|
|
if([keyDesc isKindOfClass:[OEKeyBindingDescription class]])
|
|
keys = @[ [keyDesc name] ];
|
|
else if([keyDesc isKindOfClass:[OEKeyBindingGroupDescription class]])
|
|
keys = [keyDesc keyNames];
|
|
|
|
for(NSString *key in keys) [sender OE_setBindingDescription:nil forKey:key];
|
|
|
|
[sender OE_setBindingEvent:nil forKey:keyDesc];
|
|
[self OE_notifyObserversDidUnsetEvent:anEvent forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
}
|
|
|
|
// Find the appropriate key for the event
|
|
keyDesc = _systemController.allKeyBindingsDescriptions[keyName];
|
|
[self OE_removeConcurrentBindings:sender ofKey:keyDesc withEvent:anEvent];
|
|
|
|
switch([anEvent type])
|
|
{
|
|
case OEHIDEventTypeAxis :
|
|
if([keyDesc axisGroup] != nil)
|
|
keyDesc = [[keyDesc axisGroup] orientedKeyGroupWithBaseKey:keyDesc];
|
|
break;
|
|
case OEHIDEventTypeHatSwitch :
|
|
if([keyDesc hatSwitchGroup] != nil)
|
|
{
|
|
OEKeyBindingGroupDescription *hatSwitchGroup = [keyDesc hatSwitchGroup];
|
|
|
|
// Sync the key direction with the hat switch direction, in case they're different.
|
|
enum { NORTH, EAST, SOUTH, WEST, HAT_COUNT };
|
|
OEHIDEventHatDirection direction = [anEvent hatDirection];
|
|
NSUInteger currentDir = NORTH;
|
|
|
|
if(direction & OEHIDEventHatDirectionNorth) currentDir = NORTH;
|
|
if(direction & OEHIDEventHatDirectionEast) currentDir = EAST;
|
|
if(direction & OEHIDEventHatDirectionSouth) currentDir = SOUTH;
|
|
if(direction & OEHIDEventHatDirectionWest) currentDir = WEST;
|
|
|
|
keyDesc = [hatSwitchGroup orientedKeyGroupWithBaseKey:hatSwitchGroup.keys[currentDir]];
|
|
}
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
|
|
[self OE_notifyObserversDidUnsetDeviceEventsOfPlayerBindings:sender forBindingKey:keyDesc];
|
|
|
|
// Update the bindings for the event
|
|
NSDictionary<NSString *, NSString *> *eventStrings = [self OE_stringValuesForBindings:@{ keyDesc : valueDesc } possibleKeys:_systemController.allKeyBindingsDescriptions];
|
|
|
|
[eventStrings enumerateKeysAndObjectsUsingBlock:
|
|
^(NSString *key, NSString *obj, BOOL *stop)
|
|
{
|
|
[sender OE_setBindingDescription:obj forKey:key];
|
|
}];
|
|
|
|
[sender OE_setBindingEvent:valueDesc forKey:keyDesc];
|
|
[self OE_notifyObserversDidSetEvent:anEvent forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
[[self bindingsController] OE_setRequiresSynchronization];
|
|
|
|
return keyDesc;
|
|
}
|
|
|
|
- (void)OE_notifyObserversDidUnsetDeviceEventsOfPlayerBindings:(OEDevicePlayerBindings *)sender forBindingKey:(id)bindingKey
|
|
{
|
|
OEDeviceHandler *deviceHandler = [sender deviceHandler];
|
|
OEHIDEvent *previousEventBinding = [[[sender OE_bindingEventForKey:bindingKey] event] eventWithDeviceHandler:deviceHandler];
|
|
if(previousEventBinding != nil) [self OE_notifyObserversDidUnsetEvent:previousEventBinding forBindingKey:bindingKey playerNumber:[sender playerNumber]];
|
|
|
|
if(![bindingKey isKindOfClass:[OEKeyBindingGroupDescription class]])
|
|
return;
|
|
|
|
for(OEKeyBindingDescription *keyDesc in [bindingKey keys])
|
|
{
|
|
OEHIDEvent *previousEventBinding = [[[sender OE_bindingEventForKey:keyDesc] event] eventWithDeviceHandler:deviceHandler];
|
|
if(previousEventBinding != nil) [self OE_notifyObserversDidUnsetEvent:previousEventBinding forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
}
|
|
}
|
|
|
|
- (void)OE_playerBindings:(OEDevicePlayerBindings *)sender didUnsetDeviceEventForKey:(NSString *)keyName;
|
|
{
|
|
NSAssert([sender isKindOfClass:[OEDevicePlayerBindings class]], @"Invalid sender: OEKeyboardPlayerBindings expected, got: %@ %@", [sender class], sender);
|
|
|
|
__block __kindof OEBindingDescription *keyDesc = _systemController.allKeyBindingsDescriptions[keyName];
|
|
NSAssert(keyDesc != nil, @"Could not find Key Binding Description for key with name \"%@\" in system \"%@\"", keyName, [[self systemController] systemIdentifier]);
|
|
|
|
// Sender is based on another device player bindings,
|
|
// it needs to be made independent and added to the manufacturer list.
|
|
if([sender OE_isDependent])
|
|
{
|
|
[sender OE_makeIndependent];
|
|
|
|
[_parsedManufacturerBindings[[[sender deviceHandler] deviceDescription]] addObject:sender];
|
|
}
|
|
|
|
__block OEControlValueDescription *valueDescToRemove = [sender bindingEvents][keyDesc];
|
|
if(valueDescToRemove == nil)
|
|
{
|
|
[[keyDesc axisGroup] enumerateOrientedKeyGroupsFromKey:keyDesc usingBlock:
|
|
^(OEOrientedKeyGroupBindingDescription *key, BOOL *stop)
|
|
{
|
|
OEControlValueDescription *valueDesc = [sender bindingEvents][key];
|
|
if(valueDesc == nil) return;
|
|
|
|
keyDesc = key;
|
|
valueDescToRemove = valueDesc;
|
|
*stop = YES;
|
|
}];
|
|
}
|
|
|
|
if(valueDescToRemove == nil)
|
|
{
|
|
[[keyDesc hatSwitchGroup] enumerateOrientedKeyGroupsFromKey:keyDesc usingBlock:
|
|
^(OEOrientedKeyGroupBindingDescription *key, BOOL *stop)
|
|
{
|
|
OEControlValueDescription *valueDesc = [sender bindingEvents][key];
|
|
if(valueDesc == nil) return;
|
|
|
|
keyDesc = key;
|
|
valueDescToRemove = valueDesc;
|
|
*stop = YES;
|
|
}];
|
|
}
|
|
|
|
if(valueDescToRemove == nil) return;
|
|
|
|
OEHIDEvent *eventToRemove = [[valueDescToRemove event] eventWithDeviceHandler:[sender deviceHandler]];
|
|
|
|
NSArray<NSString *> *keys = nil;
|
|
if([keyDesc isKindOfClass:[OEKeyBindingDescription class]])
|
|
keys = @[ [keyDesc name] ];
|
|
else if([keyDesc isKindOfClass:[OEKeyBindingGroupDescription class]])
|
|
keys = [keyDesc keyNames];
|
|
|
|
for(NSString *key in keys) [sender OE_setBindingDescription:nil forKey:key];
|
|
|
|
[sender OE_setBindingEvent:nil forKey:keyDesc];
|
|
[self OE_notifyObserversDidUnsetEvent:eventToRemove forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
}
|
|
|
|
- (void)OE_removeConcurrentBindings:(OEDevicePlayerBindings *)sender ofKey:(OEKeyBindingDescription *)keyDesc withEvent:(OEHIDEvent *)anEvent;
|
|
{
|
|
NSDictionary<__kindof OEBindingDescription *, OEControlValueDescription *> *rawBindings = [[sender bindingEvents] copy];
|
|
OEKeyBindingGroupDescription *axisGroup = keyDesc.axisGroup;
|
|
OEKeyBindingGroupDescription *hatGroup = keyDesc.hatSwitchGroup;
|
|
|
|
if(axisGroup == nil && hatGroup == nil) return;
|
|
|
|
OEDeviceHandler *handler = [sender deviceHandler];
|
|
|
|
switch([anEvent type])
|
|
{
|
|
case OEHIDEventTypeButton :
|
|
case OEHIDEventTypeTrigger :
|
|
{
|
|
[rawBindings enumerateKeysAndObjectsUsingBlock:
|
|
^ void (OEOrientedKeyGroupBindingDescription *keyDesc, OEControlValueDescription *valueDesc, BOOL *stop)
|
|
{
|
|
if(![keyDesc isKindOfClass:[OEOrientedKeyGroupBindingDescription class]]) return;
|
|
|
|
OEKeyBindingGroupDescription *keyGroup = [keyDesc parentKeyGroup];
|
|
if(keyGroup != axisGroup && keyGroup != hatGroup) return;
|
|
|
|
[[keyGroup keyNames] enumerateObjectsUsingBlock:
|
|
^(NSString *key, NSUInteger idx, BOOL *stop)
|
|
{
|
|
[sender OE_setBindingDescription:nil forKey:key];
|
|
}];
|
|
|
|
[sender OE_setBindingEvent:nil forKey:keyDesc];
|
|
[self OE_notifyObserversDidUnsetEvent:[[valueDesc event] OE_eventWithDeviceHandler:handler] forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
}];
|
|
}
|
|
break;
|
|
case OEHIDEventTypeAxis :
|
|
{
|
|
for(OEKeyBindingDescription *keyDesc in [axisGroup keys])
|
|
{
|
|
OEControlValueDescription *valueDesc = [rawBindings objectForKey:keyDesc];
|
|
if(valueDesc != nil)
|
|
{
|
|
[sender OE_setBindingDescription:nil forKey:[keyDesc name]];
|
|
[sender OE_setBindingEvent:nil forKey:keyDesc];
|
|
[self OE_notifyObserversDidUnsetEvent:[[valueDesc event] OE_eventWithDeviceHandler:handler] forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
}
|
|
}
|
|
|
|
[rawBindings enumerateKeysAndObjectsUsingBlock:
|
|
^ void (OEOrientedKeyGroupBindingDescription *keyDesc, OEControlValueDescription *valueDesc, BOOL *stop)
|
|
{
|
|
if(![keyDesc isKindOfClass:[OEOrientedKeyGroupBindingDescription class]]) return;
|
|
|
|
OEKeyBindingGroupDescription *keyGroup = [keyDesc parentKeyGroup];
|
|
if(keyGroup != axisGroup && keyGroup != hatGroup) return;
|
|
|
|
[[keyDesc keyNames] enumerateObjectsUsingBlock:
|
|
^(NSString *key, NSUInteger idx, BOOL *stop)
|
|
{
|
|
[sender OE_setBindingDescription:nil forKey:key];
|
|
}];
|
|
|
|
[sender OE_setBindingEvent:nil forKey:keyDesc];
|
|
[self OE_notifyObserversDidUnsetEvent:[[valueDesc event] OE_eventWithDeviceHandler:handler] forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
}];
|
|
}
|
|
break;
|
|
case OEHIDEventTypeHatSwitch :
|
|
{
|
|
NSMutableSet<OEKeyBindingGroupDescription *> *visitedAxisGroups = [NSMutableSet setWithObjects:axisGroup, hatGroup, nil];
|
|
for(OEKeyBindingDescription *keyDesc in [hatGroup keys])
|
|
{
|
|
OEControlValueDescription *valueDesc = [rawBindings objectForKey:keyDesc];
|
|
if(valueDesc != nil)
|
|
{
|
|
[sender OE_setBindingDescription:nil forKey:[keyDesc name]];
|
|
[sender OE_setBindingEvent:nil forKey:keyDesc];
|
|
[self OE_notifyObserversDidUnsetEvent:[[valueDesc event] OE_eventWithDeviceHandler:handler] forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
}
|
|
|
|
OEKeyBindingGroupDescription *temp = keyDesc.axisGroup;
|
|
if(temp != nil) [visitedAxisGroups addObject:temp];
|
|
}
|
|
|
|
[rawBindings enumerateKeysAndObjectsUsingBlock:
|
|
^(OEOrientedKeyGroupBindingDescription *keyDesc, OEControlValueDescription *valueDesc, BOOL *stop)
|
|
{
|
|
if(![visitedAxisGroups containsObject:keyDesc]) return;
|
|
|
|
[[keyDesc keyNames] enumerateObjectsUsingBlock:
|
|
^(NSString *key, NSUInteger idx, BOOL *stop)
|
|
{
|
|
[sender OE_setBindingDescription:nil forKey:key];
|
|
}];
|
|
|
|
[sender OE_setBindingEvent:nil forKey:keyDesc];
|
|
[self OE_notifyObserversDidUnsetEvent:[[valueDesc event] OE_eventWithDeviceHandler:handler] forBindingKey:keyDesc playerNumber:[sender playerNumber]];
|
|
}];
|
|
}
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Device Handlers Management
|
|
|
|
- (void)OE_didAddDeviceHandler:(OEDeviceHandler *)aHandler;
|
|
{
|
|
// Ignore extra keyboards for now
|
|
if([aHandler isKeyboardDevice]) return;
|
|
|
|
[self OE_notifyObserversForAddedDeviceBindings:[self OE_deviceBindingsForDeviceHandler:aHandler]];
|
|
}
|
|
|
|
- (void)OE_didRemoveDeviceHandler:(OEDeviceHandler *)aHandler;
|
|
{
|
|
// Ignore extra keyboards for now
|
|
if([aHandler isKeyboardDevice]) return;
|
|
|
|
OEDevicePlayerBindings *controller = [_deviceHandlersToBindings objectForKey:aHandler];
|
|
|
|
if(controller == nil)
|
|
{
|
|
NSLog(@"WARNING: Trying to remove a device that was not registered with %@", self);
|
|
return;
|
|
}
|
|
|
|
NSUInteger playerNumber = [controller playerNumber];
|
|
|
|
[self willChangeValueForKey:@"devicePlayerBindings"];
|
|
[_devicePlayerBindings removeObjectForKey:@(playerNumber)];
|
|
[self didChangeValueForKey:@"devicePlayerBindings"];
|
|
|
|
[self OE_notifyObserversForRemovedDeviceBindings:controller];
|
|
|
|
[controller OE_makeIndependent];
|
|
|
|
[controller OE_setDeviceHandler:nil];
|
|
[controller OE_setPlayerNumber:0];
|
|
}
|
|
|
|
- (void)OE_notifyObserversForAddedDeviceBindings:(OEDevicePlayerBindings *)aHandler;
|
|
{
|
|
NSUInteger playerNumber = [aHandler playerNumber];
|
|
OEDeviceHandler *deviceHandler = [aHandler deviceHandler];
|
|
|
|
[[aHandler bindingEvents] enumerateKeysAndObjectsUsingBlock:
|
|
^(id key, OEControlValueDescription *obj, BOOL *stop)
|
|
{
|
|
for(id<OESystemBindingsObserver> observer in _bindingsObservers)
|
|
[observer systemBindings:self didSetEvent:[[obj event] OE_eventWithDeviceHandler:deviceHandler] forBinding:key playerNumber:playerNumber];
|
|
}];
|
|
}
|
|
|
|
- (void)OE_notifyObserversForRemovedDeviceBindings:(OEDevicePlayerBindings *)aHandler;
|
|
{
|
|
NSUInteger playerNumber = [aHandler playerNumber];
|
|
OEDeviceHandler *deviceHandler = [aHandler deviceHandler];
|
|
|
|
// Tell the controllers that the bindings are not used anymore
|
|
[[aHandler bindingEvents] enumerateKeysAndObjectsUsingBlock:
|
|
^(id key, OEControlValueDescription *obj, BOOL *stop)
|
|
{
|
|
for(id<OESystemBindingsObserver> observer in _bindingsObservers)
|
|
[observer systemBindings:self didUnsetEvent:[[obj event] OE_eventWithDeviceHandler:deviceHandler] forBinding:key playerNumber:playerNumber];
|
|
}];
|
|
}
|
|
|
|
- (OEDevicePlayerBindings *)OE_deviceBindingsForDeviceHandler:(OEDeviceHandler *)aHandler;
|
|
{
|
|
OEDevicePlayerBindings *controller = [_deviceHandlersToBindings objectForKey:aHandler];
|
|
|
|
// The device was already registered with the system controller
|
|
if(controller != nil) return controller;
|
|
|
|
OEDeviceDescription *deviceDescription = [aHandler deviceDescription];
|
|
OEControllerDescription *controllerDescription = [deviceDescription controllerDescription];
|
|
NSMutableArray *manuBindings = _parsedManufacturerBindings[deviceDescription];
|
|
|
|
// Allocate a new array to countain OEDevicePlayerBindings objects for the given device type
|
|
if(manuBindings == nil)
|
|
{
|
|
[self OE_parseManufacturerControlValuesForDeviceDescription:deviceDescription];
|
|
manuBindings = _parsedManufacturerBindings[deviceDescription];
|
|
}
|
|
|
|
for(OEDevicePlayerBindings *ctrl in manuBindings)
|
|
if([ctrl deviceHandler] == nil)
|
|
{
|
|
controller = ctrl;
|
|
[controller OE_setDeviceHandler:aHandler];
|
|
break;
|
|
}
|
|
|
|
// No available slots in the known configurations, look for defaults
|
|
if(controller == nil)
|
|
{
|
|
OEDevicePlayerBindings *ctrl = _defaultDeviceBindings[controllerDescription];
|
|
controller = [ctrl OE_playerBindingsWithDeviceHandler:aHandler playerNumber:0];
|
|
}
|
|
|
|
// No defaults, duplicate the first manufacturer device
|
|
if(controller == nil && [manuBindings count] > 0)
|
|
controller = [manuBindings[0] OE_playerBindingsWithDeviceHandler:aHandler playerNumber:0];
|
|
|
|
// Still nothing, create a completely empty controller
|
|
if(controller == nil)
|
|
{
|
|
// This handler is the first of its kind for the application
|
|
controller = [_emptyConfiguration OE_playerBindingsWithDeviceHandler:aHandler playerNumber:0];
|
|
}
|
|
|
|
// Keep track of device handlers
|
|
_deviceHandlersToBindings[aHandler] = controller;
|
|
|
|
// Add it to the player list
|
|
[self OE_addDeviceBindings:controller];
|
|
|
|
return controller;
|
|
}
|
|
|
|
- (NSUInteger)OE_addDeviceBindings:(OEDevicePlayerBindings *)controller;
|
|
{
|
|
// Find the first free slot.
|
|
NSUInteger playerNumber = 1;
|
|
while(_devicePlayerBindings[@(playerNumber)] != nil)
|
|
playerNumber++;
|
|
|
|
[self willChangeValueForKey:@"devicePlayerBindings"];
|
|
_devicePlayerBindings[@(playerNumber)] = controller;
|
|
[self didChangeValueForKey:@"devicePlayerBindings"];
|
|
|
|
[controller OE_setPlayerNumber:playerNumber];
|
|
|
|
return playerNumber;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Bindings Observers
|
|
|
|
- (void)OE_notifyObserversDidSetEvent:(OEHIDEvent *)anEvent forBindingKey:(__kindof OEBindingDescription *)bindingKey playerNumber:(NSUInteger)playerNumber;
|
|
{
|
|
for(id<OESystemBindingsObserver> observer in _bindingsObservers)
|
|
[observer systemBindings:self didSetEvent:anEvent forBinding:bindingKey playerNumber:playerNumber];
|
|
}
|
|
|
|
- (void)OE_notifyObserversDidUnsetEvent:(OEHIDEvent *)anEvent forBindingKey:(__kindof OEBindingDescription *)bindingKey playerNumber:(NSUInteger)playerNumber;
|
|
{
|
|
for(id<OESystemBindingsObserver> observer in _bindingsObservers)
|
|
[observer systemBindings:self didUnsetEvent:anEvent forBinding:bindingKey playerNumber:playerNumber];
|
|
}
|
|
|
|
- (void)OE_notifyExistingBindings:(OEPlayerBindings *)bindings toObserver:(id<OESystemBindingsObserver>)observer;
|
|
{
|
|
if(bindings == nil) return;
|
|
|
|
OEDevicePlayerBindings *deviceBindings = nil;
|
|
if([bindings isKindOfClass:[OEDevicePlayerBindings class]])
|
|
deviceBindings = (OEDevicePlayerBindings *)bindings;
|
|
|
|
NSUInteger playerNumber = [bindings playerNumber];
|
|
|
|
[[bindings bindingEvents] enumerateKeysAndObjectsUsingBlock:
|
|
^(id key, id event, BOOL *stop)
|
|
{
|
|
NSUInteger player = playerNumber;
|
|
// playerNumber for system-wide keys should always be 0
|
|
if(_systemController.systemKeyBindingsDescriptions != nil &&
|
|
[key isKindOfClass:[OEKeyBindingDescription class]] &&
|
|
[key isSystemWide])
|
|
player = 0;
|
|
|
|
if(deviceBindings != nil) event = [[(OEControlValueDescription *)event event] OE_eventWithDeviceHandler:[deviceBindings deviceHandler]];
|
|
[observer systemBindings:self didSetEvent:event forBinding:key playerNumber:player];
|
|
}];
|
|
}
|
|
|
|
- (void)OE_notifyExistingBindingsInArray:(NSArray *)bindingsArray toObserver:(id<OESystemBindingsObserver>)observer
|
|
{
|
|
for(OEPlayerBindings *ctrl in bindingsArray)
|
|
[self OE_notifyExistingBindings:ctrl toObserver:observer];
|
|
}
|
|
|
|
- (void)addBindingsObserver:(id<OESystemBindingsObserver>)observer
|
|
{
|
|
if([_bindingsObservers containsObject:observer]) return;
|
|
|
|
[self OE_notifyExistingBindingsInArray:[_keyboardPlayerBindings allValues] toObserver:observer];
|
|
[self OE_notifyExistingBindingsInArray:[_devicePlayerBindings allValues] toObserver:observer];
|
|
|
|
[_bindingsObservers addObject:observer];
|
|
}
|
|
|
|
- (void)removeBindingsObserver:(id<OESystemBindingsObserver>)observer
|
|
{
|
|
// No need to tell it to unset everything
|
|
[_bindingsObservers removeObject:observer];
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|