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",