diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index 957fd9b1..01cc03c8 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -1721,7 +1721,7 @@ @@ -2614,6 +2639,8 @@ + + @@ -2756,7 +2783,7 @@ - + diff --git a/VoidLink/Database/DataManager.h b/VoidLink/Database/DataManager.h index d49d42c8..b7ce2a2e 100644 --- a/VoidLink/Database/DataManager.h +++ b/VoidLink/Database/DataManager.h @@ -21,6 +21,12 @@ typedef NS_ENUM(NSUInteger, UINavigationBarHeight) { UINavigationBarHeightIPhone = 44 }; +typedef NS_ENUM(NSInteger, ControllerGyroSwitchMode) { + ControllerGyroSwitchDisabled, + ControllerGyroSwitchPressToToggle, + ControllerGyroSwitchHoldDown +}; + typedef NS_ENUM(NSInteger, AudioEngine) { SystemAudioEngine, SDLAudioEngine @@ -145,6 +151,7 @@ typedef NS_ENUM(NSInteger, WidgetSizeTransition) { mapControllerToMouse:(BOOL)mapControllerToMouse controllerMousePointerVelocity:(CGFloat)controllerMousePointerVelocity controllerMouseExpo:(CGFloat)controllerMouseExpo + controllerGyroSwitchMode:(NSInteger)controllerGyroSwitchMode backgroundSessionTimer:(NSInteger)backgroundSessionTimer; - (NSArray*) getHosts; diff --git a/VoidLink/Database/DataManager.m b/VoidLink/Database/DataManager.m index 29345d19..87e47fbb 100644 --- a/VoidLink/Database/DataManager.m +++ b/VoidLink/Database/DataManager.m @@ -123,6 +123,7 @@ mapControllerToMouse:(BOOL)mapControllerToMouse controllerMousePointerVelocity:(CGFloat)controllerMousePointerVelocity controllerMouseExpo:(CGFloat)controllerMouseExpo + controllerGyroSwitchMode:(NSInteger)controllerGyroSwitchMode backgroundSessionTimer:(NSInteger)backgroundSessionTimer{ [_managedObjectContext performBlockAndWait:^{ @@ -196,6 +197,7 @@ settingsToSave.mapControllerToMouse = mapControllerToMouse; settingsToSave.controllerMousePointerVelocity = [NSNumber numberWithFloat:controllerMousePointerVelocity]; settingsToSave.controllerMouseExpo = [NSNumber numberWithFloat:controllerMouseExpo]; + settingsToSave.controllerGyroSwitchMode = [NSNumber numberWithInteger:controllerGyroSwitchMode]; settingsToSave.rememberFoldState = rememberFoldState; [self saveData]; }]; diff --git a/VoidLink/Database/TemporarySettings.h b/VoidLink/Database/TemporarySettings.h index 3cb49b8e..86a8cd0d 100644 --- a/VoidLink/Database/TemporarySettings.h +++ b/VoidLink/Database/TemporarySettings.h @@ -102,6 +102,9 @@ @property (nonatomic) BOOL mapControllerToMouse; @property (nonatomic, retain) NSNumber * controllerMousePointerVelocity; @property (nonatomic, retain) NSNumber * controllerMouseExpo; +@property (nonatomic, retain) NSNumber * controllerGyroSwitchToggle; +@property (nonatomic, retain) NSNumber * controllerGyroSwitchHold; +@property (nonatomic, retain) NSNumber * controllerGyroSwitchMode; - (id) initFromSettings:(Settings*)settings; diff --git a/VoidLink/Database/TemporarySettings.m b/VoidLink/Database/TemporarySettings.m index 2ba9712d..79e4dc42 100644 --- a/VoidLink/Database/TemporarySettings.m +++ b/VoidLink/Database/TemporarySettings.m @@ -158,6 +158,9 @@ self.controllerMouseStick = settings.controllerMouseStick; self.controllerMousePointerVelocity = settings.controllerMousePointerVelocity; self.controllerMouseExpo = settings.controllerMouseExpo; + self.controllerGyroSwitchToggle = settings.controllerGyroSwitchToggle; + self.controllerGyroSwitchHold = settings.controllerGyroSwitchHold; + self.controllerGyroSwitchMode = settings.controllerGyroSwitchMode; #endif diff --git a/VoidLink/Input/ControllerSupport.m b/VoidLink/Input/ControllerSupport.m index 3b14b192..3453b306 100644 --- a/VoidLink/Input/ControllerSupport.m +++ b/VoidLink/Input/ControllerSupport.m @@ -72,7 +72,9 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; VoidController *_oscController; NSMutableSet* _activeGCControllers; TemporarySettings* tempSettings; - + OSCProfilesManager* oscProfileMan; + OSCProfile* oscProfile; + #define EMULATING_SELECT 0x1 #define EMULATING_SPECIAL 0x2 @@ -83,6 +85,16 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; int _gyroMode; CGFloat _gyroSensitivity; bool _captureMouse; + + bool _startGyroByController; + bool _gyroEnabledFlag; + int _controllerGyroSwitchToggle; + int _controllerGyroSwitchHold; + bool _controllerGyroSwitchTogglePressed; + bool _controllerGyroSwitchHoldPressed; + ControllerGyroSwitchMode _gyroSwitchMode; + + MotionHandler* motionHandler; } // UPDATE_BUTTON_FLAG(controller, flag, pressed) @@ -243,30 +255,68 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; voidController.lastDeviceGyroSample = deviceGyroSample; // Convert rad/s to deg/s - //NSLog(@"sending built-in gyro data, accelSample data 00: %f, playerIndex: %d",deviceGyroSample.x, voidController.playerIndex); + + UIInterfaceOrientation interfaceOrientation = UIInterfaceOrientationUnknown; if (@available(iOS 13.0, *)) { - if(UIApplication.sharedApplication.windows.firstObject.windowScene.interfaceOrientation == 4){//check for landscape left or landscape right + interfaceOrientation = UIApplication.sharedApplication.windows.firstObject.windowScene.interfaceOrientation; + } else { + interfaceOrientation = UIApplication.sharedApplication.statusBarOrientation; + } + + switch (interfaceOrientation) { + case UIInterfaceOrientationLandscapeLeft: LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber, LI_MOTION_TYPE_GYRO, - deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity, - deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity, - deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity); - } - else{ + self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0); + break; + case UIInterfaceOrientationLandscapeRight: LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber, LI_MOTION_TYPE_GYRO, - deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity, - deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity, - deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity); - } + self->_gyroEnabledFlag ? deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity : 0); + break; + case UIInterfaceOrientationPortrait: + LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber, + LI_MOTION_TYPE_GYRO, + self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity : 0); + break; + case UIInterfaceOrientationPortraitUpsideDown: + LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber, + LI_MOTION_TYPE_GYRO, + self->_gyroEnabledFlag ? deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0); + break; + default: + LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber, + LI_MOTION_TYPE_GYRO, + self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0); + break; + } + + /* + if(UIApplication.sharedApplication.windows.firstObject.windowScene.interfaceOrientation == 4){//check for landscape left or landscape right + LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber, + LI_MOTION_TYPE_GYRO, + self->_gyroEnabledFlag ? deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity : 0); } else{ LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber, LI_MOTION_TYPE_GYRO, - deviceGyroSample.y * 57.2957795f * self->_gyroSensitivity, - deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity, - deviceGyroSample.x * 57.2957795f * self->_gyroSensitivity); + self->_gyroEnabledFlag ? deviceGyroSample.y * -57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.z * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? deviceGyroSample.x * -57.2957795f * self->_gyroSensitivity : 0); } + */ }]; break; } @@ -335,9 +385,9 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; // NSLog(@"sending controller gyro data, gyroSample data 00: %f, playerIndex: %ld, obj: %@",gyroSample.x, (long)voidController.gamepad.playerIndex, voidController); LiSendControllerMotionEvent((uint8_t)voidController.controllerNumber, LI_MOTION_TYPE_GYRO, - gyroSample.x * 57.2957795f * self->_gyroSensitivity, - gyroSample.z * 57.2957795f * self->_gyroSensitivity, - gyroSample.y * -57.2957795f * self->_gyroSensitivity); + self->_gyroEnabledFlag ? gyroSample.x * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? gyroSample.z * 57.2957795f * self->_gyroSensitivity : 0, + self->_gyroEnabledFlag ? gyroSample.y * -57.2957795f * self->_gyroSensitivity : 0); }]; // }); });} @@ -992,6 +1042,11 @@ double rc_expo(double x, double expo) { }); } +- (bool)useMotionHandler{ + return self->tempSettings.emulatedControllerType.intValue!=LI_CTYPE_PS + ||self->tempSettings.gyroMode.intValue==GyroModeOff; +} + -(void) registerControllerCallbacks:(GCController*) controller { if (controller != NULL) { @@ -1081,54 +1136,70 @@ double rc_expo(double x, double expo) { for(NSNumber* buttonFlagId in buttonDict){ GCControllerButtonInput * button = (GCControllerButtonInput *)buttonDict[buttonFlagId]; if(self->_mapControllerToMouse){ - if(button.pressed){ if(buttonFlagId.intValue == self->_controllerMouseSwitch){ self->_mouseSwitchButtonPressed = true; self->mouseSwitchDownTimestamp = CACurrentMediaTime(); } } - else{ if(buttonFlagId.intValue == self->_controllerMouseSwitch && self->_mouseSwitchButtonPressed){ if(CACurrentMediaTime()-self->mouseSwitchDownTimestamp>1){ self->_controllerMouseEnabledFlag = !self->_controllerMouseEnabledFlag; + [self updateLeftStick:voidController x:0 y:0]; + [self updateRightStick:voidController x:0 y:0]; } else [self sendControllerMouseSwitchClick:voidController]; self->_mouseSwitchButtonPressed = false; self->mouseSwitchDownTimestamp = 0; } } - - - - /* if(buttonFlagId.intValue == self->_controllerMouseSwitch){ - if(button.pressed){ - self->_mouseSwitchButtonPressed = true; - self->mouseSwitchDownTimestamp = CACurrentMediaTime(); - } - else if(self->_mouseSwitchButtonPressed){ - if(CACurrentMediaTime()-self->mouseSwitchDownTimestamp>1){ - self->_controllerMouseEnabledFlag = !self->_controllerMouseEnabledFlag; - self->_mouseSwitchButtonPressed = false; - self->mouseSwitchDownTimestamp = 0; - } - else [self sendControllerMouseSwitchClick:voidController]; - } - else self->_mouseSwitchButtonPressed = false; - } */ } else self->_controllerMouseEnabledFlag = false; - + + if(self->_startGyroByController){ + if(button.pressed){ + if (buttonFlagId.intValue == self->_controllerGyroSwitchToggle + && !self->_controllerGyroSwitchTogglePressed) { + self->_controllerGyroSwitchTogglePressed = true; + self->_gyroEnabledFlag = !self->_gyroEnabledFlag; + self->_gyroEnabledFlag = self->_gyroEnabledFlag && self->_gyroSwitchMode != ControllerGyroSwitchDisabled; + if([self useMotionHandler]){ + if(self->_gyroEnabledFlag) [self->motionHandler startGyroByControllerButton]; + else [self->motionHandler stopGyroUpdateWithInterruptNoneGyroInput:false resetLeftStick:true]; + } + } + if (buttonFlagId.intValue == self->_controllerGyroSwitchHold + && !self->_controllerGyroSwitchHoldPressed) { + self->_controllerGyroSwitchHoldPressed = true; + self->_gyroEnabledFlag = true; + self->_gyroEnabledFlag = self->_gyroEnabledFlag && self->_gyroSwitchMode != ControllerGyroSwitchDisabled; + if([self useMotionHandler] && self->_gyroEnabledFlag) [self->motionHandler startGyroByControllerButton]; + } + } + else{ + if (buttonFlagId.intValue == self->_controllerGyroSwitchToggle + && self->_controllerGyroSwitchTogglePressed) { + self->_controllerGyroSwitchTogglePressed = false; + } + if (buttonFlagId.intValue == self->_controllerGyroSwitchHold + && self->_controllerGyroSwitchHoldPressed) { + self->_controllerGyroSwitchHoldPressed = false; + self->_gyroEnabledFlag = false; + if([self useMotionHandler]) [self->motionHandler stopGyroUpdateWithInterruptNoneGyroInput:false resetLeftStick:true]; + } + } + } + if(self->_controllerMouseEnabledFlag){ if(buttonFlagId.intValue == self->_controllerMouseLeftButton || buttonFlagId.intValue == self->_controllerMouseRightButton){ if(buttonFlagId.intValue == self->_controllerMouseLeftButton) LiSendMouseButtonEvent(button.pressed ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, BUTTON_LEFT); if(buttonFlagId.intValue == self->_controllerMouseRightButton) LiSendMouseButtonEvent(button.pressed ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, BUTTON_RIGHT); UPDATE_BUTTON_FLAG(voidController, buttonFlagId.intValue, NO); } - else if(buttonFlagId.intValue!=self->_controllerMouseSwitch) UPDATE_BUTTON_FLAG(voidController, buttonFlagId.intValue, button.pressed); + else if(buttonFlagId.intValue!=self->_controllerMouseSwitch || !self->_mapControllerToMouse) UPDATE_BUTTON_FLAG(voidController, buttonFlagId.intValue, button.pressed); } - else if(buttonFlagId.intValue!=self->_controllerMouseSwitch) UPDATE_BUTTON_FLAG(voidController, buttonFlagId.intValue, button.pressed); + else if(buttonFlagId.intValue!=self->_controllerMouseSwitch || !self->_mapControllerToMouse) UPDATE_BUTTON_FLAG(voidController, buttonFlagId.intValue, button.pressed); } leftStickX = self->_controllerMouseEnabledFlag ? 0 :gamepad.leftThumbstick.xAxis.value * 0x7FFE; @@ -1137,6 +1208,7 @@ double rc_expo(double x, double expo) { rightStickX = self->_controllerMouseEnabledFlag ? 0 : gamepad.rightThumbstick.xAxis.value * 0x7FFE; rightStickY = self->_controllerMouseEnabledFlag ? 0 : gamepad.rightThumbstick.yAxis.value * 0x7FFE; + if(self->_controllerMouseEnabledFlag){ self->stickToMouseInputX = self->_controllerMouseStick == LeftStickToMouse ? gamepad.leftThumbstick.xAxis.value : gamepad.rightThumbstick.xAxis.value; self->stickToMouseInputY = self->_controllerMouseStick == LeftStickToMouse ? gamepad.leftThumbstick.yAxis.value : gamepad.rightThumbstick.yAxis.value; @@ -1148,13 +1220,30 @@ double rc_expo(double x, double expo) { self->stickToMouseInputX = 0; self->stickToMouseInputY = 0; } - + + /* + if(self->oscProfile.mapGyroTo!=mapGyroToControllerStick + ||!self->oscProfile.rollToLeftStick) [self updateLeftStick:voidController x:leftStickX y:leftStickY]; + */ + + if([self useMotionHandler] + && self->oscProfile.mapGyroTo==mapGyroToControllerStick + && self->oscProfile.yawPitchToRightStick + && self->_gyroEnabledFlag + ) [self->motionHandler mixPhysicalRightStickAndGyroInputWithX:rightStickX y:rightStickY]; + else [self updateRightStick: voidController.playerIndex==0?self->_oscController:voidController x:rightStickX y:rightStickY]; + + if([self useMotionHandler] + && self->oscProfile.mapGyroTo==mapGyroToControllerStick + && self->oscProfile.rollToLeftStick + && self->_gyroEnabledFlag + ) [self->motionHandler mixPhysicalLeftStickAndGyroInputWithX:leftStickX y:leftStickY]; + else [self updateLeftStick: voidController.playerIndex==0?self->_oscController:voidController x:leftStickX y:leftStickY]; + leftTrigger = gamepad.leftTrigger.value * 0xFF; rightTrigger = gamepad.rightTrigger.value * 0xFF; - - [self updateLeftStick:voidController x:leftStickX y:leftStickY]; - [self updateRightStick:voidController x:rightStickX y:rightStickY]; [self updateTriggers:voidController left:leftTrigger right:rightTrigger]; + [self updateFinished:voidController]; if (@available(iOS 14.0, *)) { @@ -1612,6 +1701,7 @@ double rc_expo(double x, double expo) { _oscController.playerIndex = 0; + oscProfile = [oscProfileMan getSelectedProfile]; DataManager* dataMan = [[DataManager alloc] init]; tempSettings = [dataMan getSettings]; @@ -1642,6 +1732,16 @@ double rc_expo(double x, double expo) { } [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } + + _startGyroByController = true; + _gyroSwitchMode = tempSettings.controllerGyroSwitchMode.intValue; + // _gyroSwitchMode = ControllerGyroSwitchHoldDownToActivate; + _controllerGyroSwitchToggle = tempSettings.controllerGyroSwitchToggle.intValue; + _controllerGyroSwitchHold = tempSettings.controllerGyroSwitchHold.intValue; + _controllerGyroSwitchTogglePressed = false; + _controllerGyroSwitchHoldPressed = false; + + if(![self useMotionHandler]) [self->motionHandler stopGyroUpdateWithInterruptNoneGyroInput:false resetLeftStick:true]; } - (void)resetGyroInputForController:(VoidController* )voidController{ @@ -1835,6 +1935,11 @@ double rc_expo(double x, double expo) { _gyroMode = AlwaysDevice; _controllerMouseEnabledFlag = false; + + _gyroEnabledFlag = false; + motionHandler = [MotionHandler sharedInstance]; + oscProfileMan = [OSCProfilesManager sharedManager:CGRectZero]; + [self updateCommonConfig:streamConfig]; for(VoidController* voidController in _voidControllers.allValues){ @@ -1848,6 +1953,7 @@ double rc_expo(double x, double expo) { _shallDisableGyroHotSwitch = streamConfig.gyroMode == GyroModeOff && _voidControllers.allValues.count == 0; NSLog(@"shallDisableGyroHotSwitch %d", _shallDisableGyroHotSwitch); + return self; } diff --git a/VoidLink/Input/ControllerUtil.swift b/VoidLink/Input/ControllerUtil.swift index ef219438..aecd169c 100644 --- a/VoidLink/Input/ControllerUtil.swift +++ b/VoidLink/Input/ControllerUtil.swift @@ -40,11 +40,13 @@ import GameController case paddle2 = 0x020000 case paddle3 = 0x040000 case paddle4 = 0x080000 - case touchpad = 0x100000 + case touchpadButton = 0x100000 case misc = 0x200000 case leftTrigger = 0x400000 case rightTrigger = 0x800000 + + case null = 0xFFFFFF } @@ -139,7 +141,7 @@ import GameController result[.paddle4] = paddle4 } if let touchpadBtn = profile.buttons[GCInputDualShockTouchpadButton] { - result[.touchpad] = touchpadBtn + result[.touchpadButton] = touchpadBtn } if #available(iOS 15.0, tvOS 15.0, *) { if let share = profile.buttons[GCInputButtonShare] { @@ -149,23 +151,42 @@ import GameController } } } - - /* - if let micro = controller.microGamepad { - if #available(iOS 13.0, *) { - result[.menu] = micro.buttonMenu - result[.a] = micro.buttonA - result[.x] = micro.buttonX - result[.dpadUp] = micro.dpad.up - result[.dpadDown] = micro.dpad.down - result[.dpadLeft] = micro.dpad.left - result[.dpadRight] = micro.dpad.right - result[.menu] = micro.buttonMenu - } - }*/ - return result } - + @objc static func string(for button: ControllerButton) -> String { + switch button { + case .a: return "A" + case .b: return "B" + case .x: return "X" + case .y: return "Y" + + case .dpadUp: return SwiftLocalizationHelper.localizedString(forKey: "Up") + case .dpadDown: return SwiftLocalizationHelper.localizedString(forKey: "Down") + case .dpadLeft: return SwiftLocalizationHelper.localizedString(forKey: "Left") + case .dpadRight: return SwiftLocalizationHelper.localizedString(forKey: "Right") + + case .leftShoulder: return "LB" + case .rightShoulder: return "RB" + + case .leftStickButton: return "LS" + case .rightStickButton: return "RS" + + case .menu: return "Menu" + case .back: return "Back" + case .special: return "Home" + + case .paddle1: return SwiftLocalizationHelper.localizedString(forKey: "Paddle1") + case .paddle2: return SwiftLocalizationHelper.localizedString(forKey: "Paddle2") + case .paddle3: return SwiftLocalizationHelper.localizedString(forKey: "Paddle3") + case .paddle4: return SwiftLocalizationHelper.localizedString(forKey: "Paddle4") + case .touchpadButton: return SwiftLocalizationHelper.localizedString(forKey: "Touch button") + case .misc: return "Misc" + + case .leftTrigger: return "LT" + case .rightTrigger: return "RT" + + case .null: return SwiftLocalizationHelper.localizedString(forKey: "Null") + } + } } diff --git a/VoidLink/Input/MotionHandler.swift b/VoidLink/Input/MotionHandler.swift index 92df7433..19f93ea6 100644 --- a/VoidLink/Input/MotionHandler.swift +++ b/VoidLink/Input/MotionHandler.swift @@ -10,23 +10,34 @@ import Foundation import CoreMotion @objc public protocol OnScreenWidgetStickMixedInputDelegate: AnyObject { - func mixRightStickAndGyroInput(x: CGFloat, y: CGFloat) - func mixLeftStickAndGyroInput(x: CGFloat, y: CGFloat) + func mixOnScreenRightStickAndGyroInput(x: CGFloat, y: CGFloat) + func mixOnScreenLeftStickAndGyroInput(x: CGFloat, y: CGFloat) func gyroMixInputStarted() -> Bool } @objc class MotionHandler: NSObject, OscInstanceReceiverDelegate, OnScreenWidgetStickMixedInputDelegate{ - public func mixRightStickAndGyroInput(x: CGFloat, y: CGFloat) { + public func mixOnScreenRightStickAndGyroInput(x: CGFloat, y: CGFloat) { rightStickTouchInputX = x rightStickTouchInputY = y } - public func mixLeftStickAndGyroInput(x: CGFloat, y: CGFloat) { + public func mixOnScreenLeftStickAndGyroInput(x: CGFloat, y: CGFloat) { // rollIntegral = 0 leftStickTouchInputX = x leftStickTouchInputY = y } + @objc public func mixPhysicalRightStickAndGyroInput(x: CGFloat, y: CGFloat) { + rightStickPhysicalInputX = x + rightStickPhysicalInputY = y + } + + @objc public func mixPhysicalLeftStickAndGyroInput(x: CGFloat, y: CGFloat) { + // rollIntegral = 0 + leftStickPhysicalInputX = x + leftStickPhysicalInputY = y + } + public func gyroMixInputStarted() -> Bool { return gyroIsWorking } @@ -71,7 +82,7 @@ import CoreMotion public var gyroStarter:Any? private var windowScene: Any? - private var onScreenControls: OnScreenControls = OnScreenControls.init() + @objc public var onScreenControls: OnScreenControls = OnScreenControls.init() public let stickMaxOffset: CGFloat = 0x7FFE private var stickInputScale: CGFloat = 35 @@ -97,6 +108,12 @@ import CoreMotion private var leftStickTouchInputX:Double = 0 private var leftStickTouchInputY:Double = 0 + + private var rightStickPhysicalInputX:Double = 0 + private var rightStickPhysicalInputY:Double = 0 + + private var leftStickPhysicalInputX:Double = 0 + private var leftStickPhysicalInputY:Double = 0 var updateInterval: TimeInterval = 1.0 / 120.0 { didSet { @@ -136,6 +153,16 @@ import CoreMotion } } + @objc public func startGyroByControllerButton(){ + self.previousWidgetYawFactor = self.widgetYawFactor + self.previousWidgetPitchFactor = self.widgetPitchFactor + self.previousWidgetRollFactor = self.widgetRollFactor + self.widgetYawFactor = 1 + self.widgetPitchFactor = 1 + self.widgetRollFactor = 1 + self.startGyroUpdate() + } + /// 启动传感器数据更新 @objc public func startGyroUpdate() { if !gyroControlStarted { @@ -165,15 +192,15 @@ import CoreMotion } /// 停止更新 - private var interruptTouchInput:Bool = false + private var interruptNoneGyroInput:Bool = false - @objc public func stopGyroUpdate(interruptTouchInput:Bool=false, resetLeftStick:Bool=false) { + @objc public func stopGyroUpdate(interruptNoneGyroInput:Bool=false, resetLeftStick:Bool=false) { gyroControlStarted = false gyroIsWorking = false if motionManager.isGyroActive{ motionManager.stopGyroUpdates() } - self.clearGyroInput(interruptTouchInput:interruptTouchInput, resetLeftStick:resetLeftStick) + self.clearGyroInput(interruptNonGyroInput:interruptNoneGyroInput, resetLeftStick:resetLeftStick) } @objc public func stopAccelUpdate() { @@ -260,7 +287,7 @@ import CoreMotion } if !gyroControlStarted { - self.clearGyroInput(interruptTouchInput: interruptTouchInput) + self.clearGyroInput(interruptNonGyroInput: interruptNoneGyroInput) print("Gyro: stopped") return } @@ -275,11 +302,11 @@ import CoreMotion if oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick { if oscProfile.yawPitchToRightStick { - yaw = rightStickTouchInputX + gyroInputToStickInput(input:yawSource*sensitvityYaw*widgetYawFactor*10) + yaw = rightStickTouchInputX + rightStickPhysicalInputX + gyroInputToStickInput(input:yawSource*sensitvityYaw*widgetYawFactor*10) yaw = self.clampStickInput(input: yaw) yaw = (yaw >= 0 ? 1.0 : -1.0) * gyroToStickMinOffset + (self.stickMaxOffset - gyroToStickMinOffset) * (yaw/self.stickMaxOffset) - pitch = rightStickTouchInputY - gyroInputToStickInput(input:pitchSource*sensitvityPitch*widgetPitchFactor*10) + pitch = rightStickTouchInputY + rightStickPhysicalInputY - gyroInputToStickInput(input:pitchSource*sensitvityPitch*widgetPitchFactor*10) pitch = self.clampStickInput(input: pitch) pitch = (pitch >= 0 ? 1.0 : -1.0) * gyroToStickMinOffset + (self.stickMaxOffset - gyroToStickMinOffset) * (pitch/self.stickMaxOffset) @@ -288,25 +315,25 @@ import CoreMotion if oscProfile.rollToLeftStick { roll = gyroInputToStickInput(input:rollSource*sensitvityRoll*widgetRollFactor*0.2) rollIntegral = rollIntegral + roll - var mixedLeftStickOffset = self.clampStickInput(input: rollIntegral+leftStickTouchInputX) + var mixedLeftStickOffset = self.clampStickInput(input: rollIntegral+leftStickTouchInputX+leftStickPhysicalInputX) mixedLeftStickOffset = (mixedLeftStickOffset >= 0 ? 1.0 : -1.0) * gyroToStickMinOffset + (self.stickMaxOffset - gyroToStickMinOffset) * (mixedLeftStickOffset/self.stickMaxOffset) - self.onScreenControls.sendLeftStickTouchPadEvent(mixedLeftStickOffset, leftStickTouchInputY) + self.onScreenControls.sendLeftStickTouchPadEvent(mixedLeftStickOffset, leftStickTouchInputY+leftStickPhysicalInputY) } } } - private func clearGyroInput(interruptTouchInput:Bool, resetLeftStick:Bool=false){ + private func clearGyroInput(interruptNonGyroInput:Bool, resetLeftStick:Bool=false){ if oscProfile.yawPitchToRightStick{ - self.onScreenControls.sendRightStickTouchPadEvent(rightStickTouchInputX-yawBias, rightStickTouchInputY-pitchBias) + self.onScreenControls.sendRightStickTouchPadEvent(rightStickPhysicalInputX+rightStickTouchInputX-yawBias, rightStickPhysicalInputY+rightStickTouchInputY-pitchBias) } if oscProfile.rollToLeftStick{ if resetLeftStick { rollIntegral = 0 - self.onScreenControls.sendLeftStickTouchPadEvent(leftStickTouchInputX-rollBias, leftStickTouchInputY) + self.onScreenControls.sendLeftStickTouchPadEvent(leftStickPhysicalInputX+leftStickTouchInputX-rollBias,leftStickPhysicalInputY+leftStickTouchInputY) } } - if(interruptTouchInput){ + if(interruptNonGyroInput){ self.onScreenControls.clearLeftStickTouchPadFlag() self.onScreenControls.clearRightStickTouchPadFlag() } diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 8ec95a3b..4f87780b 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -1301,7 +1301,7 @@ import UIKit targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset) self.onScreenControls.sendRightStickTouchPadEvent(targetX, targetY) } - self.motionHandler.mixRightStickAndGyroInput(x: targetX, y: targetY) + self.motionHandler.mixOnScreenRightStickAndGyroInput(x: targetX, y: targetY) } private func sendLeftStickTouchPadEvent(inputX: CGFloat, inputY: CGFloat){ @@ -1315,7 +1315,7 @@ import UIKit if !mixLeftStickInputToGyro || (self.motionHandler.gyroMixInputStarted() != true) { self.onScreenControls.sendLeftStickTouchPadEvent(targetX, targetY) } - self.motionHandler.mixLeftStickAndGyroInput(x: targetX, y: targetY) + self.motionHandler.mixOnScreenLeftStickAndGyroInput(x: targetX, y: targetY) } private func sendLeftTriggerTouchPadEvent(inputY: CGFloat){ @@ -1846,7 +1846,7 @@ import UIKit case "GYRO": self.motionHandler.startGyroByOnScreenButton(self, yawFactor: yawFactor, pitchFactor: pitchFactor, rollFactor: rollFactor) case "GYROPAUSE": - self.motionHandler.stopGyroUpdate(interruptTouchInput:false) + self.motionHandler.stopGyroUpdate(interruptNoneGyroInput:false) break case "ACCEL": break @@ -1868,7 +1868,7 @@ import UIKit widget.tapToToggleFlag = !widget.tapToToggleFlag } } - self.motionHandler.stopGyroUpdate(interruptTouchInput: false, resetLeftStick: true) + self.motionHandler.stopGyroUpdate(interruptNoneGyroInput: false, resetLeftStick: true) self.motionHandler.gyroStarter = nil } else { @@ -1931,7 +1931,7 @@ import UIKit if !mixRightStickInputToGyro || self.motionHandler.gyroMixInputStarted() != true { self.onScreenControls.clearRightStickTouchPadFlag() } - self.motionHandler.mixRightStickAndGyroInput(x: 0, y: 0) + self.motionHandler.mixOnScreenRightStickAndGyroInput(x: 0, y: 0) } private func clearLeftStickTouchPadFlag(){ @@ -1940,7 +1940,7 @@ import UIKit if !mixLeftStickInputToGyro || self.motionHandler.gyroMixInputStarted() != true { self.onScreenControls.clearLeftStickTouchPadFlag() } - self.motionHandler.mixLeftStickAndGyroInput(x: 0, y: 0) + self.motionHandler.mixOnScreenLeftStickAndGyroInput(x: 0, y: 0) } override func touchesCancelled(_ touches: Set, with event: UIEvent?) { @@ -2168,7 +2168,7 @@ import UIKit super.didMoveToSuperview() if superview == nil { if self.motionControlButtonString == "GYRO" { - self.motionHandler.stopGyroUpdate(interruptTouchInput: true) + self.motionHandler.stopGyroUpdate(interruptNoneGyroInput: true) self.motionHandler.gyroStarter = nil } if self.motionControlButtonString == "ACCEL" {} diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index 038aaa24..e97afbba 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -29,6 +29,9 @@ + + + diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 04e62fbe..fd4865ba 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -254,6 +254,52 @@ } } }, + " both set " : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : " 双键开关 " + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : " 雙鍵開關 " + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : " 雙鍵開關 " + } + } + } + }, + " duplicated ! " : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : " 按键重复 ! " + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : " 按鍵重复 ! " + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : " 按鍵重复 !" + } + } + } + }, "!ML_TEST_RESULT_INCONCLUSIVE" : { "extractionState" : "manual", "localizations" : { @@ -1445,6 +1491,35 @@ } } }, + "controllerGyroSwitchButtonStackTip" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gyro toggle button: %@\nGyro hold-down button: %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "陀螺仪切换按键: %@\n陀螺仪保持按键: %@" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "陀螺儀切換按鍵: %@\n陀螺儀保持按鍵: %@" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "陀螺儀切換按鍵: %@\n陀螺儀保持按鍵: %@" + } + } + } + }, "controllerMouseExpoStackTip" : { "extractionState" : "manual", "localizations" : { @@ -1474,6 +1549,35 @@ } } }, + "controllerToMouseStackTip" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Long press to switch to mouse mode: %@\nStick for moving mouse pointer: %@\nMouse left button: %@\nMouse right button: %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "长按切换鼠标模式: %@\n控制鼠标的摇杆: %@\n鼠标左键: %@\n鼠标右键: %@" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "長按切換滑鼠模式: %@\n控制滑鼠的搖桿: %@\n滑鼠左鍵: %@\n滑鼠右鍵: %@" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "長按切換滑鼠模式: %@\n控制滑鼠的搖桿: %@\n滑鼠左鍵: %@\n滑鼠右鍵: %@" + } + } + } + }, "Create New" : { "extractionState" : "manual", "localizations" : { @@ -1739,6 +1843,52 @@ } } }, + "Disable hold-down button" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "禁用保持键" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "禁用保持鍵" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "禁用保持鍵" + } + } + } + }, + "Disable press-toggle button" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "禁用切换键" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "禁用切換鍵" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "禁用切換鍵" + } + } + } + }, "Disconnect" : { "extractionState" : "manual", "localizations" : { @@ -1785,6 +1935,29 @@ } } }, + "Down" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "下" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "下" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "下" + } + } + } + }, "Drag and drop buttons onto this trash can to remove them from the interface" : { "extractionState" : "manual", "localizations" : { @@ -2563,6 +2736,29 @@ } } }, + "Gyro button on controller" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手柄陀螺仪按键" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "手柄陀螺儀按鍵" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "手柄陀螺儀按鍵" + } + } + } + }, "gyroModeStackDoc" : { "extractionState" : "manual", "localizations" : { @@ -3064,6 +3260,52 @@ } } }, + "Left" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "左" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "左" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "左" + } + } + } + }, + "Left stick" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "左摇杆" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "左搖桿" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "左搖桿" + } + } + } + }, "Load" : { "extractionState" : "manual", "localizations" : { @@ -3687,6 +3929,29 @@ } } }, + "Null" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "無" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "無" + } + } + } + }, "NVIDIA GameStream End-of-Service" : { "extractionState" : "manual", "localizations" : { @@ -4073,6 +4338,98 @@ } } }, + "Paddle1" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "背键1" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "背鍵1" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "背鍵1" + } + } + } + }, + "Paddle2" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "背键2" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "背鍵2" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "背鍵2" + } + } + } + }, + "Paddle3" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "背键3" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "背鍵3" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "背鍵3" + } + } + } + }, + "Paddle4" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "背键4" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "背鍵4" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "背鍵4" + } + } + } + }, "Pairing" : { "extractionState" : "manual", "localizations" : { @@ -4483,6 +4840,52 @@ } } }, + "Press a button for keeping gyro active by holding down.\nYou can have 2 buttons for both press-toggle & hold-down switch." : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下保持陀螺仪模式的手柄按键。\n你可以为陀螺仪切换和保持各设置一个按键。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下保持陀螺儀模式的手把按鍵。\n您可以為陀螺儀切換和保持各設置一個按鍵。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下保持陀螺儀模式的手把按鍵。\n您可以為陀螺儀切換和保持各設置一個按鍵。" + } + } + } + }, + "Press a button for switching gyro on & off with a press.\nYou can have 2 buttons for both press-toggle & hold-down switch." : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切换陀螺仪模式的手柄按键。\n你可以为陀螺仪切换和保持各设置一个按键。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切換陀螺儀模式的手把按鍵。\n您可以為陀螺儀切換和保持各設置一個按鍵。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切換陀螺儀模式的手把按鍵。\n您可以為陀螺儀切換和保持各設置一個按鍵。" + } + } + } + }, "Press a button for switching to controller mouse mode (long press for 1 second during streaming)" : { "extractionState" : "manual", "localizations" : { @@ -5174,6 +5577,52 @@ } } }, + "Right" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "右" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "右" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "右" + } + } + } + }, + "Right stick" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "右摇杆" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "右搖桿" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "右搖桿" + } + } + } + }, "Roll factor: %.2f" : { "extractionState" : "manual", "localizations" : { @@ -6292,6 +6741,29 @@ } } }, + "Touchpad button" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "触摸板按键" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "触摸板按键" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "触摸板按键" + } + } + } + }, "Undo" : { "extractionState" : "manual", "localizations" : { @@ -6367,6 +6839,29 @@ } } }, + "Up" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "上" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "上" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "上" + } + } + } + }, "useBuiltinMicStackTip" : { "extractionState" : "manual", "localizations" : { diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h index 59a41af0..f8ac045f 100644 --- a/VoidLink/ViewControllers/SettingsViewController.h +++ b/VoidLink/ViewControllers/SettingsViewController.h @@ -223,6 +223,10 @@ @property (strong, nonatomic) IBOutlet UIStackView *controllerMouseExpoStack; @property (strong, nonatomic) IBOutlet UISlider *controllerMouseExpoSlider; +@property (strong, nonatomic) IBOutlet UIStackView *controllerGyroSwitchButtonStack; +@property (strong, nonatomic) IBOutlet UISegmentedControl *controllerGyroSwitchButtonSetter; + + @property (strong, nonatomic) IBOutlet UIStackView *testStack; @property (nonatomic, strong) MicHandler *micHandler; diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 620dea33..c96e5cda 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -763,9 +763,10 @@ BOOL isCustomResolution(int resolutionSelected) { [self addSetting:self.emulatedControllerTypeStack ofId:@"emulatedControllerTypeStack" withInfoTag:YES withDynamicLabel:NO to:controllerSection]; [self addSetting:self.gyroModeStack ofId:@"gyroModeStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection]; [self addSetting:self.gyroSensitivityStack ofId:@"gyroSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:controllerSection]; - [self addSetting:self.controllerToMouseStack ofId:@"controllerToMouseStack" withInfoTag:NO withDynamicLabel:YES to:controllerSection]; + [self addSetting:self.controllerToMouseStack ofId:@"controllerToMouseStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection]; [self addSetting:self.controllerMouseVelocityStack ofId:@"controllerMouseVelocityStack" withInfoTag:NO withDynamicLabel:YES to:controllerSection]; [self addSetting:self.controllerMouseExpoStack ofId:@"controllerMouseExpoStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection]; + [self addSetting:self.controllerGyroSwitchButtonStack ofId:@"controllerGyroSwitchButtonStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection]; [controllerSection addToParentStack:_parentStack]; motionControlSection = [[MenuSectionView alloc] init]; @@ -1349,7 +1350,17 @@ BOOL isCustomResolution(int resolutionSelected) { tipText = [LocalizationHelper localizedStringForKey:@"controllerMouseExpoStackTip"]; showOnlineDocAction = false; } - + if([sender.superview.accessibilityIdentifier isEqualToString: @"controllerGyroSwitchButtonStack"]){ + tempSettings = [dataMan getSettings]; + tipText = [LocalizationHelper localizedStringForKey:@"controllerGyroSwitchButtonStackTip", [ControllerUtil stringFor:tempSettings.controllerGyroSwitchToggle.intValue], [ControllerUtil stringFor:tempSettings.controllerGyroSwitchHold.intValue]]; + showOnlineDocAction = false; + } + if([sender.superview.accessibilityIdentifier isEqualToString: @"controllerToMouseStack"]){ + tempSettings = [dataMan getSettings]; + tipText = [LocalizationHelper localizedStringForKey:@"controllerToMouseStackTip", [ControllerUtil stringFor:tempSettings.controllerMouseSwitch.intValue], [LocalizationHelper localizedStringForKey:tempSettings.controllerGyroSwitchHold.intValue == LeftStickToMouse ? @"Left stick" : @"Right stick"], [ControllerUtil stringFor:tempSettings.controllerMouseLeftButton.intValue], [ControllerUtil stringFor:tempSettings.controllerMouseRightButton.intValue]]; + showOnlineDocAction = false; + } + UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message:tipText preferredStyle:UIAlertControllerStyleAlert]; @@ -1978,6 +1989,13 @@ BOOL isCustomResolution(int resolutionSelected) { [self.controllerToMouseSwitch addTarget:self action:@selector(controllerToMouseSwitchFlipped:) forControlEvents:UIControlEventValueChanged]; [self controllerToMouseSwitchFlipped:self.controllerToMouseSwitch]; + self.controllerGyroSwitchButtonSetter.selectedSegmentIndex = self->tempSettings.controllerGyroSwitchMode.intValue; + [self.controllerGyroSwitchButtonSetter addTarget:self action:@selector(controllerGyroSwitchModeChanged:) forControlEvents:UIControlEventValueChanged]; + if(self.controllerGyroSwitchButtonSetter.selectedSegmentIndex != ControllerGyroSwitchDisabled){ + bool bothButtonsSet = self->tempSettings.controllerGyroSwitchHold.intValue != ControllerButtonNull && self->tempSettings.controllerGyroSwitchToggle.intValue != ControllerButtonNull; + [self findDynamicLabelFromStack:self.controllerGyroSwitchButtonStack].text = bothButtonsSet ? [LocalizationHelper localizedStringForKey:@" both set "] : @""; + } + [self.controllerMouseVelocitySlider setValue:self->tempSettings.controllerMousePointerVelocity.floatValue]; [self.controllerMouseVelocitySlider addTarget:self action:@selector(controllerMouseVelocitySliderMoved:) forControlEvents:UIControlEventValueChanged]; [self controllerMouseVelocitySliderMoved:self.controllerMouseVelocitySlider]; @@ -2244,6 +2262,93 @@ BOOL isCustomResolution(int resolutionSelected) { } } +- (void)controllerGyroSwitchModeChanged:(UISegmentedControl* )sender{ + UILabel* tipLabel = [self findDynamicLabelFromStack:self.controllerGyroSwitchButtonStack]; + if(sender.selectedSegmentIndex == ControllerGyroSwitchDisabled){ + tipLabel.text = @""; + } + else{ + __block bool switchButtonCaptured = false; + Settings* currentSettings = [dataMan retrieveSettings]; + + [AlertControllerUtil showAlertIn:self + title:[LocalizationHelper localizedStringForKey:@"Gyro button on controller"] + message:sender.selectedSegmentIndex == ControllerGyroSwitchPressToToggle ? [LocalizationHelper localizedStringForKey:@"Press a button for switching gyro on & off with a press.\nYou can have 2 buttons for both press-toggle & hold-down switch."] : [LocalizationHelper localizedStringForKey:@"Press a button for keeping gyro active by holding down.\nYou can have 2 buttons for both press-toggle & hold-down switch."] + withCancel:NO + buttonTitle:sender.selectedSegmentIndex == ControllerGyroSwitchPressToToggle ? [LocalizationHelper localizedStringForKey:@"Disable press-toggle button"] : [LocalizationHelper localizedStringForKey:@"Disable hold-down button"] + countdown:0 + action:^{ + + UIAlertAction* confirmAction = AlertControllerUtil.alertController.actions.firstObject; + + self->capturedController = [GCController controllers].firstObject; + if(self->capturedController){ + __weak typeof(self) weakSelf = self; + + [ControllerUtil listenWithController:self->capturedController swapABXY:false handler:^(NSDictionary * buttonDict, GCExtendedGamepad * gamepad, GCControllerElement * element) { + __strong typeof(weakSelf) self = weakSelf; + if (!self) return; + for(NSNumber* buttonFlagId in buttonDict){ + GCControllerButtonInput * button = (GCControllerButtonInput *)buttonDict[buttonFlagId]; + if(button.isPressed){ + if(sender.selectedSegmentIndex == ControllerGyroSwitchPressToToggle) currentSettings.controllerGyroSwitchToggle = buttonFlagId; + if(sender.selectedSegmentIndex == ControllerGyroSwitchHoldDown) currentSettings.controllerGyroSwitchHold = buttonFlagId; + + switchButtonCaptured = true; + AlertControllerUtil.alertController.message = [LocalizationHelper localizedStringForKey:@"Finished"]; + gamepad.valueChangedHandler = nil; + if(confirmAction) [confirmAction setValue:[LocalizationHelper localizedStringForKey:@"OK"] forKey:@"title"]; + [self->dataMan saveData]; + } + } + }]; + } + else{ + [AlertControllerUtil.alertController dismissViewControllerAnimated:NO completion:^{ + [AlertControllerUtil showAlertIn:self + title:[LocalizationHelper localizedStringForKey:@""] + message:[LocalizationHelper localizedStringForKey:@"Waiting for controller..."] + withCancel:YES + buttonTitle:[LocalizationHelper localizedStringForKey:@"Continue"] + countdown:3 + action:^{} + completion:^{ + if(AlertControllerUtil.actionCancelled){ + self.controllerGyroSwitchButtonSetter.selectedSegmentIndex = ControllerGyroSwitchDisabled; + return; + } + [AlertControllerUtil.alertController dismissViewControllerAnimated:NO completion:^{}]; + [self controllerGyroSwitchModeChanged:sender]; + }]; + }]; + } + } + completion:^{ + if(!switchButtonCaptured){ + if(sender.selectedSegmentIndex == ControllerGyroSwitchPressToToggle) currentSettings.controllerGyroSwitchToggle = @(ControllerButtonNull); + if(sender.selectedSegmentIndex == ControllerGyroSwitchHoldDown) currentSettings.controllerGyroSwitchHold = @(ControllerButtonNull); + } + + bool noSwitchButtonSet = currentSettings.controllerGyroSwitchHold.intValue == ControllerButtonNull && currentSettings.controllerGyroSwitchToggle.intValue == ControllerButtonNull; + bool duplicatedButtons = currentSettings.controllerGyroSwitchHold.intValue == currentSettings.controllerGyroSwitchToggle.intValue && currentSettings.controllerGyroSwitchToggle.intValue != ControllerButtonNull; + bool bothButtonsSet = currentSettings.controllerGyroSwitchHold.intValue != ControllerButtonNull && currentSettings.controllerGyroSwitchToggle.intValue != ControllerButtonNull; + tipLabel.text = @""; + if(bothButtonsSet) tipLabel.text = [LocalizationHelper localizedStringForKey:@" both set "]; + if(duplicatedButtons){ + sender.selectedSegmentIndex = ControllerGyroSwitchDisabled; + tipLabel.text = [LocalizationHelper localizedStringForKey:@" duplicated ! "]; + } + if(noSwitchButtonSet) sender.selectedSegmentIndex = ControllerGyroSwitchDisabled; + if(!noSwitchButtonSet && !bothButtonsSet){ + sender.selectedSegmentIndex = currentSettings.controllerGyroSwitchToggle.intValue != ControllerButtonNull ? ControllerGyroSwitchPressToToggle : ControllerGyroSwitchHoldDown; + } + + [self->dataMan saveData]; + if(self->capturedController && self->capturedController.extendedGamepad) self->capturedController.extendedGamepad.valueChangedHandler = nil; + }]; + } +} + - (void)controllerToMouseSwitchFlipped:(UISwitch* )sender{ [self setHidden:!sender.isOn forStack:_controllerMouseVelocityStack]; [self setHidden:!sender.isOn forStack:_controllerMouseExpoStack]; @@ -2334,7 +2439,7 @@ BOOL isCustomResolution(int resolutionSelected) { message:[LocalizationHelper localizedStringForKey:@"Waiting for controller..."] withCancel:YES buttonTitle:[LocalizationHelper localizedStringForKey:@"Continue"] - countdown:2 + countdown:3 action:^{} completion:^{ if(AlertControllerUtil.actionCancelled){ @@ -3162,6 +3267,7 @@ BOOL isCustomResolution(int resolutionSelected) { BOOL mapControllerToMouse = self.controllerToMouseSwitch.isOn; CGFloat controllerMousePointerVelocity = self.controllerMouseVelocitySlider.value; CGFloat controllerMouseExpo = self.controllerMouseExpoSlider.value; + NSInteger controllerGyroSwitchMode = self.controllerGyroSwitchButtonSetter.selectedSegmentIndex; NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value; [dataMan saveSettingsWithBitrate:_bitrate @@ -3231,6 +3337,7 @@ BOOL isCustomResolution(int resolutionSelected) { mapControllerToMouse:mapControllerToMouse controllerMousePointerVelocity:controllerMousePointerVelocity controllerMouseExpo:controllerMouseExpo + controllerGyroSwitchMode:controllerGyroSwitchMode backgroundSessionTimer:backgroundSessionTimer]; } diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index cc945ed1..e2db3407 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -1582,8 +1582,8 @@ [_motionHandler startAccelUpdate]; } -- (void)stopGyroUpdateWithInterruptTouchInput:(BOOL)interruption{ - [_motionHandler stopGyroUpdateWithInterruptTouchInput:interruption resetLeftStick:false]; +- (void)stopGyroUpdateWithInterruptNoneGyroInput:(BOOL)interruption{ + [_motionHandler stopGyroUpdateWithInterruptNoneGyroInput:interruption resetLeftStick:false]; } - (void)stopAccelUpdate{ diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings index 9514cada..83a50ca6 100644 --- a/VoidLink/mul.lproj/iPad.xcstrings +++ b/VoidLink/mul.lproj/iPad.xcstrings @@ -619,6 +619,36 @@ } } }, + "26O-xW-5EH.text" : { + "comment" : "Class = \"UILabel\"; text = \"Switch Gyro by Controller\"; ObjectID = \"26O-xW-5EH\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Switch Gyro by Controller" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手柄开关陀螺仪" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "手把開關陀螺儀" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "手把開關陀螺儀" + } + } + } + }, "53H-ST-OtY.segmentTitles[0]" : { "comment" : "Class = \"UISegmentedControl\"; 53H-ST-OtY.segmentTitles[0] = \"Balanced\"; ObjectID = \"53H-ST-OtY\";", "extractionState" : "extracted_with_value", @@ -4009,6 +4039,96 @@ } } }, + "OWG-zd-e3I.segmentTitles[0]" : { + "comment" : "Class = \"UISegmentedControl\"; OWG-zd-e3I.segmentTitles[0] = \"Disabled\"; ObjectID = \"OWG-zd-e3I\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Disabled" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "关闭" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + } + } + }, + "OWG-zd-e3I.segmentTitles[1]" : { + "comment" : "Class = \"UISegmentedControl\"; OWG-zd-e3I.segmentTitles[1] = \"Press-toggle\"; ObjectID = \"OWG-zd-e3I\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Press-toggle" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切换" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切換" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切換" + } + } + } + }, + "OWG-zd-e3I.segmentTitles[2]" : { + "comment" : "Class = \"UISegmentedControl\"; OWG-zd-e3I.segmentTitles[2] = \"Hold down\"; ObjectID = \"OWG-zd-e3I\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Hold down" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按住保持" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "按住保持" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "按住保持" + } + } + } + }, "OXy-y4-yO4.text" : { "comment" : "Class = \"UILabel\"; text = \"Swap A/B X/Y Buttons\"; ObjectID = \"OXy-y4-yO4\";", "extractionState" : "extracted_with_value", diff --git a/VoidLink/mul.lproj/iPhone.xcstrings b/VoidLink/mul.lproj/iPhone.xcstrings index 9226f71b..57b08b4f 100644 --- a/VoidLink/mul.lproj/iPhone.xcstrings +++ b/VoidLink/mul.lproj/iPhone.xcstrings @@ -1309,6 +1309,18 @@ } } }, + "bAZ-bm-h3I.text" : { + "comment" : "Class = \"UILabel\"; text = \"Switch Gyro by Controller\"; ObjectID = \"bAZ-bm-h3I\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Switch Gyro by Controller" + } + } + } + }, "Bda-3L-Wtg.segmentTitles[0]" : { "comment" : "Class = \"UISegmentedControl\"; Bda-3L-Wtg.segmentTitles[0] = \"Off\"; ObjectID = \"Bda-3L-Wtg\";", "extractionState" : "extracted_with_value", @@ -2851,6 +2863,96 @@ } } }, + "mpa-8g-1Ug.segmentTitles[0]" : { + "comment" : "Class = \"UISegmentedControl\"; mpa-8g-1Ug.segmentTitles[0] = \"Disabled\"; ObjectID = \"mpa-8g-1Ug\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Disabled" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "关闭" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + } + } + }, + "mpa-8g-1Ug.segmentTitles[1]" : { + "comment" : "Class = \"UISegmentedControl\"; mpa-8g-1Ug.segmentTitles[1] = \"Press-toggle\"; ObjectID = \"mpa-8g-1Ug\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Press-toggle" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切换" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切換" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下切換" + } + } + } + }, + "mpa-8g-1Ug.segmentTitles[2]" : { + "comment" : "Class = \"UISegmentedControl\"; mpa-8g-1Ug.segmentTitles[2] = \"Hold down\"; ObjectID = \"mpa-8g-1Ug\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Hold down" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按住保持" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "按住保持" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "按住保持" + } + } + } + }, "msE-2o-R3Q.segmentTitles[0]" : { "comment" : "Class = \"UISegmentedControl\"; msE-2o-R3Q.segmentTitles[0] = \"No\"; ObjectID = \"msE-2o-R3Q\";", "extractionState" : "extracted_with_value",