diff --git a/VoidLink/Input/ControllerSupport.h b/VoidLink/Input/ControllerSupport.h index 6a26b31f..bde2d8e2 100644 --- a/VoidLink/Input/ControllerSupport.h +++ b/VoidLink/Input/ControllerSupport.h @@ -21,6 +21,8 @@ @interface ControllerSupport : NSObject +@property (readonly) bool shallDisableGyroHotSwitch; + -(id) initWithConfig:(StreamConfiguration*)streamConfig delegate:(id)delegate; -(void) connectionEstablished; diff --git a/VoidLink/Input/ControllerSupport.m b/VoidLink/Input/ControllerSupport.m index 71ef5af5..7a5bd8d6 100644 --- a/VoidLink/Input/ControllerSupport.m +++ b/VoidLink/Input/ControllerSupport.m @@ -25,6 +25,13 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; +@interface ControllerSupport() + +@property (assign,nonatomic) bool shallDisableGyroHotSwitch; + +@end + + @implementation ControllerSupport { id _controllerConnectObserver; id _controllerDisconnectObserver; @@ -768,17 +775,31 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; if (controller.battery) { capabilities |= LI_CCAP_BATTERY_STATE; } + + bool controllerLacksGyro = (capabilities & LI_CCAP_GYRO) == 0; + if ((_gyroMode == AlwaysDevice) || (_gyroMode == GyroModeAuto && controllerLacksGyro)) + { + type = LI_CTYPE_PS; + capabilities |= LI_CCAP_GYRO | LI_CCAP_ACCEL; + } + } + } + else { + // This is a virtual controller corresponding to our OSC + + if (_streamConfig.gyroMode != GyroModeOff) { + type = LI_CTYPE_PS; + capabilities = LI_CCAP_GYRO | LI_CCAP_ACCEL; } else { - // This is a virtual controller corresponding to our OSC - - // TODO: Support various layouts and button labels on the OSC type = LI_CTYPE_XBOX; capabilities = 0; - supportedButtonFlags = - PLAY_FLAG | BACK_FLAG | UP_FLAG | DOWN_FLAG | LEFT_FLAG | RIGHT_FLAG | - LB_FLAG | RB_FLAG | LS_CLK_FLAG | RS_CLK_FLAG | A_FLAG | B_FLAG | X_FLAG | Y_FLAG; } + + // Set the standard supported buttons for the virtual controller. + supportedButtonFlags = + PLAY_FLAG | BACK_FLAG | UP_FLAG | DOWN_FLAG | LEFT_FLAG | RIGHT_FLAG | + LB_FLAG | RB_FLAG | LS_CLK_FLAG | RS_CLK_FLAG | A_FLAG | B_FLAG | X_FLAG | Y_FLAG; } // Report the new controller to the host @@ -1165,9 +1186,11 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; voidController.reportRateHz = 120; } if (@available(iOS 14.0, *)) { - if(voidController.gamepad.motion.hasRotationRate) [voidController.motionTypes addObject:@(LI_MOTION_TYPE_GYRO)]; - voidController.hasGyroscope = YES; - voidController.reportRateHz = 120; + if(voidController.gamepad.motion.hasRotationRate) { + [voidController.motionTypes addObject:@(LI_MOTION_TYPE_GYRO)]; + voidController.hasGyroscope = YES; + voidController.reportRateHz = 120; + } } // If this is player 0, it shares state with the OSC @@ -1194,7 +1217,6 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; [self initializeControllerHaptics:voidController]; } - -(VoidController* )assignController:(GCController*)controller { NSLog(@"run assignController"); @@ -1318,9 +1340,8 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; _oscController.playerIndex = 0; DataManager* dataMan = [[DataManager alloc] init]; - //_oscEnabled = (OnScreenControlsLevel)[[dataMan getSettings].onscreenControls integerValue] != OnScreenControlsLevelOff; TemporarySettings* currentSettings = [dataMan getSettings]; - _oscEnabled = true; + _oscEnabled = _oscEnabled || (OnScreenControlsLevel)[currentSettings.onscreenControls integerValue] != OnScreenControlsLevelOff || streamConfig.gyroMode != GyroModeOff; _gyroSensitivity = currentSettings.gyroSensitivity.floatValue; } @@ -1349,46 +1370,17 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; Log(LOG_I, @"Number of supported controllers connected: %d", [ControllerSupport getGamepadCount]); Log(LOG_I, @"Multi-controller: %d", _multiController); - - if (@available(iOS 14.0, tvOS 14.0, *)) { - switch(_gyroMode){ - case GyroModeOff: - [self stopTimerForAllControllers]; - break; - case GyroModeAuto: - [self stopTimerForAllControllers]; - [_voidControllers removeAllObjects]; - [self assignControllers]; - //if(_voidControllers.count == 0) [self assignControllers]; - if(_voidControllers.count == 0 || ![self externalControllersHaveGyro]) [self updateTimerStateForController:_oscController]; - else for(VoidController* controller in _voidControllers.allValues) [self updateTimerStateForController:controller]; - // to be filled - break; - case AlwaysDevice: - //[self stopTimerForController:_oscController]; - for(VoidController* controller in _voidControllers.allValues){ - [self stopTimerForController:controller]; - } - [self updateTimerStateForController:_oscController]; - break; - case AlwaysController: - [self stopTimerForAllControllers]; - for(VoidController* controller in _voidControllers.allValues) [self updateTimerStateForController:controller]; - break; - } - } - - + [self applyGyroModeSetting]; [self assignControllers]; - - NSLog(@"controllerNumbers: %d", _controllerNumbers); for(VoidController* controller in _voidControllers.allValues){ NSLog(@"controller obj in dict: %@", controller); } + + [self updateFinished:_oscController]; } -(id)initWithConfig:(StreamConfiguration*)streamConfig delegate:(id)delegate @@ -1403,7 +1395,6 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; _activeGCControllers = [[NSMutableSet alloc] init]; _controllerNumbers = 0; - _captureMouse = (streamConfig.localMousePointerMode == 0); if (@available(iOS 14.0, tvOS 14.0, *)) { for (GCMouse* mouse in [GCMouse mice]) { @@ -1553,8 +1544,8 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; [self assignControllers]; - //[self updateConfig:streamConfig delegate:delegate]; - + _shallDisableGyroHotSwitch = streamConfig.gyroMode == GyroModeOff && _voidControllers.allValues.count == 0; + NSLog(@"shallDisableGyroHotSwitch %d", _shallDisableGyroHotSwitch); return self; } @@ -1566,40 +1557,24 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; return false; } --(void)connectionEstablished{ - [self updateTimerStateForController:self->_oscController]; - [self setButtonFlag:self->_oscController flags:A_FLAG]; - [self updateFinished:self->_oscController]; - [self clearButtonFlag:self->_oscController flags:A_FLAG]; - [self updateFinished:self->_oscController]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), + +-(void)connectionEstablished { + for (VoidController* voidController in _voidControllers.allValues) { + [self updateFinished:voidController]; + } + + if (_oscEnabled) { + [self setButtonFlag:self->_oscController flags:A_FLAG]; + [self updateFinished:self->_oscController]; + [self clearButtonFlag:self->_oscController flags:A_FLAG]; + [self updateFinished:self->_oscController]; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self updateTimerStateForController:self->_oscController]; + self->_gyroMode = self->_streamConfig.gyroMode; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - self->_gyroMode = self->_streamConfig.gyroMode; - - switch (self->_gyroMode) { - case AlwaysController: - [self stopTimerForAllControllers]; - for(VoidController* voidController in self->_voidControllers.allValues) [self updateTimerStateForController:voidController]; - break; - case GyroModeAuto: - if(self->_voidControllers.count == 0) [self assignControllers]; - if(self->_voidControllers.count == 0 || ![self externalControllersHaveGyro]) nil; - else{ - [self stopTimerForAllControllers]; - for(VoidController* voidController in self->_voidControllers.allValues) [self updateTimerStateForController:voidController]; - } - break; - case GyroModeOff: - [self stopTimerForAllControllers]; - break; - default: - break; - } - }); + [self applyGyroModeSetting]; }); } @@ -1618,6 +1593,42 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; } } +- (void)applyGyroModeSetting { + // Stop all timers to ensure a clean slate before applying the new setting. + [self stopTimerForAllControllers]; + + switch(_gyroMode) { + case AlwaysController: + // Activate timers only for physical controllers. + for (VoidController* voidController in _voidControllers.allValues) { + [self updateTimerStateForController:voidController]; + } + break; + + case GyroModeAuto: + // Prefer physical controller gyros if they exist. + if ([self externalControllersHaveGyro]) { + for (VoidController* voidController in _voidControllers.allValues) { + [self updateTimerStateForController:voidController]; + } + } else { + // Otherwise, fall back to the device gyro. + [self updateTimerStateForController:self->_oscController]; + } + break; + + case AlwaysDevice: + // Always start the device gyro in this mode. + [self updateTimerStateForController:self->_oscController]; + break; + + case GyroModeOff: + // Do nothing, all timers are already stopped. + break; + } +} + + -(void) cleanup { [[NSNotificationCenter defaultCenter] removeObserver:_controllerConnectObserver]; diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 8d1fc166..b7fbc4dc 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1135,10 +1135,12 @@ static NSMutableSet* hostList; [settingsViewController.codecSelector setEnabled:!_settingsExpandedInStreamView]; [settingsViewController.yuv444Switch setEnabled:!_settingsExpandedInStreamView]; [settingsViewController.hdrSwitch setEnabled:!_settingsExpandedInStreamView]; + [settingsViewController.gyroModeSelector setEnabled:!_settingsExpandedInStreamView || ![streamFrameViewController shallDisableGyroHotSwitch]]; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.framepacingStack]; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.citrixX1MouseStack]; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.externalDisplayModeStack]; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.audioConfigStack]; + } - (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position { diff --git a/VoidLink/ViewControllers/StreamFrameViewController.h b/VoidLink/ViewControllers/StreamFrameViewController.h index a71c0d64..47c6c06c 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.h +++ b/VoidLink/ViewControllers/StreamFrameViewController.h @@ -32,5 +32,6 @@ -(void)updatePreferredDisplayMode:(BOOL)streamActive; -(void)reConfigStreamViewRealtime; - (void)setUserInteractionEnabledForStreamView:(bool)enabled; +- (bool)shallDisableGyroHotSwitch; @end diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 6b471386..85188a3c 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -670,6 +670,10 @@ } } +- (bool)shallDisableGyroHotSwitch{ + return _controllerSupport.shallDisableGyroHotSwitch; +} + - (BOOL) isAirPlaying{ return _extWindow != nil && _extWindow.hidden == NO; } @@ -1156,8 +1160,7 @@ } - (BOOL)prefersHomeIndicatorAutoHidden { - if ([_controllerSupport getConnectedGamepadCount] > 0 && - [_streamView getCurrentOscState] == OnScreenControlsLevelOff && + if ( [_streamView getCurrentOscState] == OnScreenControlsLevelOff && _userIsInteracting == NO) { // Autohide the home bar when a gamepad is connected // and the on-screen controls are disabled. We can't