diff --git a/Cocoa/GBView.m b/Cocoa/GBView.m index 6139a1ed7..14b66e1d5 100644 --- a/Cocoa/GBView.m +++ b/Cocoa/GBView.m @@ -117,6 +117,8 @@ @implementation GBView JOYController *lastController; bool _turbo; bool _mouseControlEnabled; + NSMutableDictionary<NSNumber *, JOYController *> *_controllerMapping; + unsigned _lastPlayerCount; } + (instancetype)alloc @@ -143,6 +145,9 @@ - (void) _init [self observeStandardDefaultsKey:@"GBAspectRatioUnkept" withBlock:^(id newValue) { [weakSelf setFrame:weakSelf.superview.frame]; }]; + [self observeStandardDefaultsKey:@"JoyKitDefaultControllers" withBlock:^(id newValue) { + [weakSelf reassignControllers]; + }]; tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){} options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseMoved owner:self @@ -154,6 +159,84 @@ - (void) _init self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; [JOYController registerListener:self]; _mouseControlEnabled = true; + [self reassignControllers]; +} + +- (void)controllerConnected:(JOYController *)controller +{ + [self reassignControllers]; +} + +- (void)controllerDisconnected:(JOYController *)controller +{ + [self reassignControllers]; +} + +- (unsigned)playerCount +{ + if (self.document.partner) { + return 2; + } + if (!_gb) { + return 1; + } + return GB_get_player_count(_gb); +} + +- (void)reassignControllers +{ + unsigned playerCount = self.playerCount; + /* Don't assign controlelrs if there's only one player, allow all controllers. */ + if (playerCount == 1) { + _controllerMapping = [NSMutableDictionary dictionary]; + return; + } + + if (!_controllerMapping) { + _controllerMapping = [NSMutableDictionary dictionary]; + } + + for (NSNumber *player in [_controllerMapping copy]) { + if (player.unsignedIntValue >= playerCount || !_controllerMapping[player].connected) { + [_controllerMapping removeObjectForKey:player]; + } + } + + _lastPlayerCount = playerCount; + for (unsigned i = 0; i < playerCount; i++) { + NSString *preferredJoypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"] + objectForKey:n2s(i)]; + for (JOYController *controller in [JOYController allControllers]) { + if (!controller.connected) continue; + if ([controller.uniqueID isEqual:preferredJoypad]) { + _controllerMapping[@(i)] = controller; + break; + } + } + } +} + +- (void)tryAssigningController:(JOYController *)controller +{ + unsigned playerCount = self.playerCount; + if (playerCount == 1) return; + if (_controllerMapping.count == playerCount) return; + if ([_controllerMapping.allValues containsObject:controller]) return; + for (unsigned i = 0; i < playerCount; i++) { + if (!_controllerMapping[@(i)]) { + _controllerMapping[@(i)] = controller; + return; + } + } +} + +- (NSDictionary<NSNumber *, JOYController *> *)controllerMapping +{ + if (_lastPlayerCount != self.playerCount) { + [self reassignControllers]; + } + + return _controllerMapping; } - (void)screenSizeChanged @@ -486,6 +569,19 @@ - (void)controller:(JOYController *)controller movedAxes3D:(JOYAxes3D *)axes } } } + + NSDictionary<NSNumber *, JOYController *> *controllerMapping = [self controllerMapping]; + GB_gameboy_t *effectiveGB = _gb; + + if (self.document.partner) { + if (controllerMapping[@1] == controller) { + effectiveGB = self.document.partner.gb; + } + if (controllerMapping[@0] != controller) { + return; + } + + } if (axes.usage == JOYAxes3DUsageOrientation) { for (JOYAxes3D *axes in controller.axes3D) { @@ -495,11 +591,11 @@ - (void)controller:(JOYController *)controller movedAxes3D:(JOYAxes3D *)axes } } JOYPoint3D point = axes.normalizedValue; - GB_set_accelerometer_values(_gb, point.x, point.z); + GB_set_accelerometer_values(effectiveGB, point.x, point.z); } else if (axes.usage == JOYAxes3DUsageAcceleration) { JOYPoint3D point = axes.gUnitsValue; - GB_set_accelerometer_values(_gb, point.x, point.z); + GB_set_accelerometer_values(effectiveGB, point.x, point.z); } } @@ -510,22 +606,20 @@ - (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)b _mouseControlEnabled = false; if (button.type == JOYButtonTypeAxes2DEmulated && [self shouldControllerUseJoystickForMotion:controller]) return; - unsigned player_count = GB_get_player_count(_gb); - if (self.document.partner) { - player_count = 2; - } + [self tryAssigningController:controller]; + + unsigned playerCount = self.playerCount; IOPMAssertionID assertionID; IOPMAssertionDeclareUserActivity(CFSTR(""), kIOPMUserActiveLocal, &assertionID); - for (unsigned player = 0; player < player_count; player++) { - NSString *preferred_joypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"] - objectForKey:n2s(player)]; - if (player_count != 1 && // Single player, accpet inputs from all joypads - !(player == 0 && !preferred_joypad) && // Multiplayer, but player 1 has no joypad configured, so it takes inputs from all joypads - ![preferred_joypad isEqualToString:controller.uniqueID]) { - continue; - } + + NSDictionary<NSNumber *, JOYController *> *controllerMapping = [self controllerMapping]; + for (unsigned player = 0; player < playerCount; player++) { + JOYController *preferredJoypad = controllerMapping[@(player)]; + if (preferredJoypad && preferredJoypad != controller) continue; // The player has a different assigned controller + if (!preferredJoypad && playerCount != 1) continue; // The player has no assigned controller in multiplayer mode, prevent controller inputs + dispatch_async(dispatch_get_main_queue(), ^{ [controller setPlayerLEDs:[controller LEDMaskForPlayer:player]]; }); diff --git a/JoyKit/JOYController.m b/JoyKit/JOYController.m index ab8f56959..21d7565e6 100644 --- a/JoyKit/JOYController.m +++ b/JoyKit/JOYController.m @@ -367,7 +367,7 @@ -(void)createInputForElement:(JOYElement *)element if (!other) goto single; if (other.usage >= element.usage) goto single; if (other.reportID != element.reportID) goto single; - if (![axisGroups[@(other.usage)] isEqualTo: axisGroups[@(element.usage)]]) goto single; + if (![axisGroups[@(other.usage)] isEqual: axisGroups[@(element.usage)]]) goto single; if (other.parentID != element.parentID) goto single; JOYAxes2D *axes = nil;