diff --git a/VoidLink/Input/AbsoluteTouchHandler.h b/VoidLink/Input/AbsoluteTouchHandler.h index 003fdd25..fe6c3010 100644 --- a/VoidLink/Input/AbsoluteTouchHandler.h +++ b/VoidLink/Input/AbsoluteTouchHandler.h @@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN @interface AbsoluteTouchHandler : UIResponder -@property (assign,nonatomic) int mouseButtonForCursorMove; +@property (class, nonatomic, assign) int mouseButtonForCursorMove; - (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings; - (void)pauseLeftButtonDrag; diff --git a/VoidLink/Input/AbsoluteTouchHandler.m b/VoidLink/Input/AbsoluteTouchHandler.m index ddd7ebd7..1efa1949 100644 --- a/VoidLink/Input/AbsoluteTouchHandler.m +++ b/VoidLink/Input/AbsoluteTouchHandler.m @@ -26,6 +26,8 @@ // How far the finger can move before it can override the double tap deadzone #define DOUBLE_TAP_DEAD_ZONE_DELTA 0.025f +static int mouseButtonForCursorMove = BUTTON_LEFT; + @implementation AbsoluteTouchHandler { StreamView* streamView; @@ -79,9 +81,7 @@ slideGestureVerticalThreshold = CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.4; screenWidthWithThreshold = CGRectGetWidth([[UIScreen mainScreen] bounds]) - _edgeTolerance; self->touchPointSpawnedAtUpperScreenEdge = false; - - _mouseButtonForCursorMove = BUTTON_LEFT; - + leftClickDelay = ((CGFloat)settings.leftClickDelayMs.intValue)/1000; return self; @@ -94,7 +94,7 @@ if([self touchDidntMoveOnScreen:movingTouchLocation]){ if(_delayMouseLeftClick){ LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); - if(_mouseButtonForCursorMove!=BUTTON_LEFT) LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, _mouseButtonForCursorMove); + if(mouseButtonForCursorMove!=BUTTON_LEFT) LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, mouseButtonForCursorMove); } LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT); dispatch_time_t delayShort = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)); @@ -166,7 +166,7 @@ LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)); dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, self->_mouseButtonForCursorMove); + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, mouseButtonForCursorMove); }); } } @@ -185,7 +185,7 @@ currentTouchesCount = currentTouches.count; if(currentTouchesCount == 2){ - if(_mouseButtonForCursorMove!=BUTTON_LEFT) LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, _mouseButtonForCursorMove); + if(mouseButtonForCursorMove!=BUTTON_LEFT) LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, mouseButtonForCursorMove); if(passthroughGestures) [TouchPadGestureHandler handleGestureIn:streamView with:event]; } @@ -201,10 +201,10 @@ [longPressTimer invalidate]; longPressTimer = nil; - NSTimeInterval dragDelay = _mouseButtonForCursorMove == BUTTON_LEFT ? leftClickTimeThreshold : 0; + NSTimeInterval dragDelay = mouseButtonForCursorMove == BUTTON_LEFT ? leftClickTimeThreshold : 0; if(_delayMouseLeftClick && (CACurrentMediaTime()-touchBeganTimeStamp>dragDelay) && !dragButtonDown){ - LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, _mouseButtonForCursorMove); + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, mouseButtonForCursorMove); dragButtonDown = true; } } @@ -244,7 +244,7 @@ } else if(!rightButtonClicked){ LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); - if(_mouseButtonForCursorMove!=BUTTON_LEFT) LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, _mouseButtonForCursorMove); + if(mouseButtonForCursorMove!=BUTTON_LEFT) LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, mouseButtonForCursorMove); LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT); } } @@ -289,6 +289,13 @@ return isAdjacent; } ++ (int)mouseButtonForCursorMove { + return mouseButtonForCursorMove; +} + ++ (void)setMouseButtonForCursorMove:(int)button { + mouseButtonForCursorMove = button; +} @end diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.m b/VoidLink/Input/CustomOSC/OSCProfilesManager.m index c0f165fe..c7458bc7 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.m +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.m @@ -406,7 +406,8 @@ static CGRect layoutViewBounds; buttonState.yawFactor = widgetView.yawFactor; buttonState.pitchFactor = widgetView.pitchFactor; buttonState.rollFactor = widgetView.rollFactor; - buttonState.decelerationRate = widgetView.decelerationRate; + buttonState.decelerationRateX = widgetView.decelerationRateX; + buttonState.decelerationRateY = widgetView.decelerationRateY; buttonState.stickIndicatorOffset = widgetView.stickIndicatorOffset; buttonState.widgetShape = widgetView.shape; buttonState.minStickOffset = widgetView.minStickOffset; diff --git a/VoidLink/Input/CustomOSC/OnScreenButtonState.h b/VoidLink/Input/CustomOSC/OnScreenButtonState.h index 59589c94..62447e84 100644 --- a/VoidLink/Input/CustomOSC/OnScreenButtonState.h +++ b/VoidLink/Input/CustomOSC/OnScreenButtonState.h @@ -40,7 +40,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) CGFloat yawFactor; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat pitchFactor; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat rollFactor; // for OnScreenWidgetView -@property (nonatomic, assign) CGFloat decelerationRate; // for OnScreenWidgetView +@property (nonatomic, assign) CGFloat decelerationRateX; // for OnScreenWidgetView +@property (nonatomic, assign) CGFloat decelerationRateY; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat stickIndicatorOffset; // for OnScreenWidgetView @property (nonatomic, assign) CGFloat minStickOffset; // for OnScreenWidgetView @property NSString* widgetShape; // for OnScreenWidgetView diff --git a/VoidLink/Input/CustomOSC/OnScreenButtonState.m b/VoidLink/Input/CustomOSC/OnScreenButtonState.m index 65e0e0d0..a3bf13f1 100644 --- a/VoidLink/Input/CustomOSC/OnScreenButtonState.m +++ b/VoidLink/Input/CustomOSC/OnScreenButtonState.m @@ -50,7 +50,8 @@ [encoder encodeFloat:self.yawFactor forKey:@"yawFactor"]; [encoder encodeFloat:self.pitchFactor forKey:@"pitchFactor"]; [encoder encodeFloat:self.rollFactor forKey:@"rollFactor"]; - [encoder encodeFloat:self.decelerationRate forKey:@"decelerationRate"]; + [encoder encodeFloat:self.decelerationRateX forKey:@"decelerationRateX"]; + [encoder encodeFloat:self.decelerationRateY forKey:@"decelerationRateY"]; [encoder encodeFloat:self.stickIndicatorOffset forKey:@"stickIndicatorOffset"]; [encoder encodeFloat:self.oscLayerSizeFactor forKey:@"oscLayerSizeFactor"]; [encoder encodeFloat:self.backgroundAlpha forKey:@"backgroundAlpha"]; @@ -83,7 +84,8 @@ self.yawFactor = [decoder containsValueForKey:@"yawFactor"] ? [decoder decodeFloatForKey:@"yawFactor"] : 1.0; self.pitchFactor = [decoder containsValueForKey:@"pitchFactor"] ? [decoder decodeFloatForKey:@"pitchFactor"] : 1.0; self.rollFactor = [decoder containsValueForKey:@"rollFactor"] ? [decoder decodeFloatForKey:@"rollFactor"] : 1.0; - self.decelerationRate = [decoder decodeFloatForKey:@"decelerationRate"]; + self.decelerationRateX = [decoder containsValueForKey:@"decelerationRateX"] ? [decoder decodeFloatForKey:@"decelerationRateX"] : 0.5; + self.decelerationRateY = [decoder containsValueForKey:@"decelerationRateY"] ? [decoder decodeFloatForKey:@"decelerationRateY"] : 0.5; self.stickIndicatorOffset = [decoder decodeFloatForKey:@"stickIndicatorOffset"]; self.oscLayerSizeFactor = [decoder decodeFloatForKey:@"oscLayerSizeFactor"]; self.backgroundAlpha = [decoder containsValueForKey:@"backgroundAlpha"] ? [decoder decodeFloatForKey:@"backgroundAlpha"] : 0.5; diff --git a/VoidLink/Input/InertialScroller.swift b/VoidLink/Input/InertialScroller.swift index 659c0970..8f037679 100644 --- a/VoidLink/Input/InertialScroller.swift +++ b/VoidLink/Input/InertialScroller.swift @@ -7,15 +7,15 @@ // class InertialScroller { - public var decelerationRate: CGFloat - + public var decelerationRateX: CGFloat + public var decelerationRateY: CGFloat + public lazy var timer: SafeTimer? = { SafeTimer(interval: 1/displayLinkRate) { [weak self] in guard let self = self, handler != nil else {return} - let deceratedDeltaX = self.vector.dx * decelerationRate - let deceratedDeltaY = self.vector.dy * decelerationRate - self.vector = CGVector(dx: deceratedDeltaX, dy: deceratedDeltaY) - if abs(deceratedDeltaX) < self.timerSuspendThreshold && abs(deceratedDeltaY) < self.timerSuspendThreshold { + self.vector.dx = self.vector.dx * decelerationRateX + self.vector.dy = self.vector.dy * decelerationRateY + if abs(self.vector.dx) < self.timerSuspendThreshold && abs(self.vector.dy) < self.timerSuspendThreshold { self.timer?.pause() } (self.handler ?? {})() @@ -28,7 +28,8 @@ class InertialScroller { public var handler: (() -> Void)? init(decelerationRate: CGFloat = 0.93, displayLinkRate: CGFloat = 60, handler: (() -> Void)? = nil) { - self.decelerationRate = decelerationRate + self.decelerationRateX = decelerationRate + self.decelerationRateY = decelerationRate self.displayLinkRate = displayLinkRate self.handler = handler } diff --git a/VoidLink/Input/MicHandler.swift b/VoidLink/Input/MicHandler.swift index c6824111..8bb33ca0 100644 --- a/VoidLink/Input/MicHandler.swift +++ b/VoidLink/Input/MicHandler.swift @@ -335,6 +335,10 @@ public class MicHandler: NSObject { try engine.start() } + @objc public func clean() { + self.timer?.clean() + } + private func notify(_ error: Error) { delegate?.micHandler?(self, didFailWithError: error as NSError) } diff --git a/VoidLink/Input/MotionHandler.swift b/VoidLink/Input/MotionHandler.swift index 82e0e2e4..26f09765 100644 --- a/VoidLink/Input/MotionHandler.swift +++ b/VoidLink/Input/MotionHandler.swift @@ -111,6 +111,7 @@ import CoreMotion private var rightStickPhysicalInputX:Double = 0 private var rightStickPhysicalInputY:Double = 0 + public var gyroToStickOffset:CGVector = CGVector(dx: 0, dy: 0) private var leftStickPhysicalInputX:Double = 0 private var leftStickPhysicalInputY:Double = 0 @@ -302,10 +303,12 @@ import CoreMotion if oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick { if oscProfile.yawPitchToRightStick { - yaw = rightStickTouchInputX + rightStickPhysicalInputX + gyroInputToStickInput(input:yawSource*sensitvityYaw*widgetYawFactor*10) + gyroToStickOffset.dx = gyroInputToStickInput(input:yawSource*sensitvityYaw*widgetYawFactor*10) + yaw = rightStickTouchInputX + rightStickPhysicalInputX + gyroToStickOffset.dx yaw = self.clampStickInput(input: yaw) - pitch = rightStickTouchInputY + rightStickPhysicalInputY - gyroInputToStickInput(input:pitchSource*sensitvityPitch*widgetPitchFactor*10) + gyroToStickOffset.dy = -gyroInputToStickInput(input:pitchSource*sensitvityPitch*widgetPitchFactor*10) + pitch = rightStickTouchInputY + rightStickPhysicalInputY + gyroToStickOffset.dy pitch = self.clampStickInput(input: pitch) let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: yaw, dy: pitch), withMinOffset: gyroToStickMinOffset) diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 85e4798d..907b0fe1 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -146,8 +146,9 @@ import UIKit @objc public var deltaX: CGFloat @objc public var deltaY: CGFloat - @objc private var weightedDeltaX: Int = 0 - @objc private var weightedDeltaY: Int = 0 + private let VectorStickFactor = 1.5167 + private var weightedDeltaX: Int = 0 + private var weightedDeltaY: Int = 0 // for LSPAD, RSPAD @objc public var offSetX: CGFloat @@ -217,7 +218,8 @@ import UIKit // trackball private var trackballVelocity: CGPoint = .zero private var trackballDecelerationTimer: Timer? - @objc public var decelerationRate: CGFloat = 0.93 + @objc public var decelerationRateX: CGFloat = 0.5 + @objc public var decelerationRateY: CGFloat = 0.5 private let trackballVelocityThreshold: CGFloat = 0.1 @objc public var slideThreshold: CGFloat = 6.0 @@ -411,7 +413,8 @@ import UIKit @objc public func setupInertialScroller() { if self.hasInertia { - self.inertialScroller = InertialScroller(decelerationRate: self.decelerationRate, displayLinkRate: CGFloat(self.tempSettings.framerate.intValue)) + self.inertialScroller = InertialScroller(decelerationRate: self.decelerationRateX, displayLinkRate: CGFloat(self.tempSettings.framerate.intValue)) + self.inertialScroller.decelerationRateY = self.decelerationRateY } } @@ -1292,11 +1295,15 @@ import UIKit //=========================================send on screen controller stick/trigger events - private func touchInputToStickInput(input: CGFloat) -> CGFloat{ + private func weightedTouchInputToStickOffset(input: CGFloat) -> CGFloat{ let target = stickMaxOffset * input / stickInputScale return fmax(fmin(target, stickMaxOffset),-stickMaxOffset) } + private func stickOffsetToWeightedTouchInput(offset: CGFloat) -> CGFloat { + return offset * stickInputScale / stickMaxOffset + } + private func touchInputToStickBallCoord(input: CGFloat) -> CGFloat { if input > stickInputScale { return stickBallMaxOffset @@ -1307,9 +1314,9 @@ import UIKit return input * (18/stickInputScale) } - private func sendRightStickTouchPadEvent(inputX: CGFloat, inputY: CGFloat){ - let targetX = self.touchInputToStickInput(input: inputX) - let targetY = -self.touchInputToStickInput(input: inputY) + private func sendRightStickTouchPadEvent(weightedTouchX: CGFloat, weightedTouchY: CGFloat){ + let targetX = self.weightedTouchInputToStickOffset(input: weightedTouchX) + let targetY = -self.weightedTouchInputToStickOffset(input: weightedTouchY) let mixRightStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick && oscProfile.yawPitchToRightStick) @@ -1321,9 +1328,9 @@ import UIKit self.motionHandler.mixOnScreenRightStickAndGyroInput(x: targetX, y: targetY) } - private func sendLeftStickTouchPadEvent(inputX: CGFloat, inputY: CGFloat){ - let targetX = self.touchInputToStickInput(input: inputX) - let targetY = -self.touchInputToStickInput(input: inputY) + private func sendLeftStickTouchPadEvent(weightedTouchX: CGFloat, weightedTouchY: CGFloat){ + let targetX = self.weightedTouchInputToStickOffset(input: weightedTouchX) + let targetY = -self.weightedTouchInputToStickOffset(input: weightedTouchY) let mixLeftStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick && oscProfile.rollToLeftStick) @@ -1782,32 +1789,32 @@ import UIKit case "LSPAD": DispatchQueue.global(qos: .userInteractive).async { self.weightedDeltaX = 1 - self.sendLeftStickTouchPadEvent(inputX: self.offSetX * self.sensitivityFactorX, inputY: self.offSetY * self.sensitivityFactorY) + self.sendLeftStickTouchPadEvent(weightedTouchX: self.offSetX * self.sensitivityFactorX, weightedTouchY: self.offSetY * self.sensitivityFactorY) } if widgetType == WidgetTypeEnum.touchPad {updateStickIndicator()} self.updateTouchLocation(touch: touches.first!) case "RSPAD": DispatchQueue.global(qos: .userInteractive).async { self.weightedDeltaX = 1 - self.sendRightStickTouchPadEvent(inputX: self.offSetX * self.sensitivityFactorX, inputY: self.offSetY * self.sensitivityFactorY) + self.sendRightStickTouchPadEvent(weightedTouchX: self.offSetX * self.sensitivityFactorX, weightedTouchY: self.offSetY * self.sensitivityFactorY) } if widgetType == WidgetTypeEnum.touchPad {updateStickIndicator()} self.updateTouchLocation(touch: touches.first!) case "LSVPAD": DispatchQueue.global(qos: .userInteractive).async { - self.weightedDeltaX = Int(self.deltaX*1.5167*self.sensitivityFactorX) - self.weightedDeltaY = Int(self.deltaY*1.5167*self.sensitivityFactorY) + self.weightedDeltaX = Int(self.deltaX*self.VectorStickFactor*self.sensitivityFactorX) + self.weightedDeltaY = Int(self.deltaY*self.VectorStickFactor*self.sensitivityFactorY) if self.firstTouchMoved { - self.sendLeftStickTouchPadEvent(inputX: CGFloat(self.weightedDeltaX), inputY: CGFloat(self.weightedDeltaY)) + self.sendLeftStickTouchPadEvent(weightedTouchX: CGFloat(self.weightedDeltaX), weightedTouchY: CGFloat(self.weightedDeltaY)) } self.updateTouchLocation(touch: touches.first!) } case "RSVPAD": DispatchQueue.global(qos: .userInteractive).async { - self.weightedDeltaX = Int(self.deltaX*1.5167*self.sensitivityFactorX) - self.weightedDeltaY = Int(self.deltaY*1.5167*self.sensitivityFactorY) + self.weightedDeltaX = Int(self.deltaX*self.VectorStickFactor*self.sensitivityFactorX) + self.weightedDeltaY = Int(self.deltaY*self.VectorStickFactor*self.sensitivityFactorY) if self.firstTouchMoved { - self.sendRightStickTouchPadEvent(inputX: CGFloat(self.weightedDeltaX), inputY: CGFloat(self.weightedDeltaY)) + self.sendRightStickTouchPadEvent(weightedTouchX: CGFloat(self.weightedDeltaX), weightedTouchY: CGFloat(self.weightedDeltaY)) } self.updateTouchLocation(touch: touches.first!) } @@ -1973,7 +1980,6 @@ import UIKit case "SOFTKEYBOARD": self.functionalButtonDelegate?.bringUpSoftKeyboard() case "ABSTCHDRAG": - // self.functionalButtonDelegate?.alterAbsTouchDragWith(mouseButton:CommandManager.mouseButtonMappings[self.comboButtonStrings.first ?? "MLEFT"] ?? BUTTON_LEFT) self.functionalButtonDelegate?.alterAbsTouchDragWith(mouseButton:BUTTON_LEFT) case "PENCILHOVER": self.functionalButtonDelegate?.disablePencilHover() @@ -2005,6 +2011,10 @@ import UIKit touchesEnded(touches, with: event) } + private func inertiaEnabled() -> Bool { + return self.decelerationRateX > 0.50001 || self.decelerationRateY > 0.50001 + } + override func touchesEnded(_ touches: Set, with event: UIEvent?) { self.touchBegan = false super.touchesEnded(touches, with: event) @@ -2022,18 +2032,7 @@ import UIKit if !OnScreenWidgetView.editMode && self.widgetType == WidgetTypeEnum.touchPad && CommandManager.mousePadWithButtonActions.contains(self.touchPadString) && allSpawnedTouchesCount == 1 && !twoTouchesDetected { self.handleMousePadButtonActionUp() } - - /* - if !OnScreenWidgetView.editMode && self.widgetType == WidgetTypeEnum.touchPad && self.touchPadString == "TRACKBALL" && allSpawnedTouchesCount == 1 && !twoTouchesDetected { - if(mousePointerMoved){ - self.startTrackballMomentum() - mousePointerMoved = false //reset flag - } - else{ - self.stopTrackballMomentum() - } - }*/ - + if !OnScreenWidgetView.editMode && self.widgetType == WidgetTypeEnum.touchPad && CommandManager.mousePadWithButtonActions.contains(self.touchPadString) && twoTouchesDetected && touches.count == allSpawnedTouchesCount { // need to enable multi-touch first // touches.count == allCapturedTouchesCount means allfingers are lifting if(self.mouseButtonAction == MouseButtonAction.hovering) {self.sendMouseRightButtonClickEvent()} @@ -2051,33 +2050,47 @@ import UIKit self.clearRightStickTouchPadFlag() if widgetType == WidgetTypeEnum.touchPad {self.resetStickBallPositionAndHideIndicator()} case "LSVPAD": - if self.decelerationRate > 0.50001 { + if self.inertiaEnabled() { if(!firstTouchMoved) {self.inertialScroller.vector = CGVector(dx: 0, dy: 0)} if self.inertialScroller.handler == nil { self.inertialScroller.handler = { - let weightedDeltaX = self.inertialScroller.vector.dx*1.5167*self.sensitivityFactorX - let weightedDeltaY = self.inertialScroller.vector.dy*1.5167*self.sensitivityFactorY - self.sendLeftStickTouchPadEvent(inputX: weightedDeltaX, inputY: weightedDeltaY) + let weightedDeltaX = self.inertialScroller.vector.dx*self.VectorStickFactor*self.sensitivityFactorX + let weightedDeltaY = self.inertialScroller.vector.dy*self.VectorStickFactor*self.sensitivityFactorY + self.sendLeftStickTouchPadEvent(weightedTouchX: weightedDeltaX, weightedTouchY: weightedDeltaY) } } self.inertialScroller.timer?.restart() } else {self.clearLeftStickTouchPadFlag()} case "RSVPAD": - if self.decelerationRate > 0.50001 { + if self.inertiaEnabled() { if(!firstTouchMoved) {self.inertialScroller.vector = CGVector(dx: 0, dy: 0)} if self.inertialScroller.handler == nil { - self.inertialScroller.handler = { - let weightedDeltaX = self.inertialScroller.vector.dx*1.5167*self.sensitivityFactorX - let weightedDeltaY = self.inertialScroller.vector.dy*1.5167*self.sensitivityFactorY - self.sendRightStickTouchPadEvent(inputX: weightedDeltaX, inputY: weightedDeltaY) + var synthesizedDeltaX:CGFloat = 0 + var synthesizedDeltaY:CGFloat = 0 + self.inertialScroller.handler = { [self] in + let weightedScrollerVector = CGVector(dx: self.inertialScroller.vector.dx*self.VectorStickFactor*self.sensitivityFactorX, dy: self.inertialScroller.vector.dy*self.VectorStickFactor*self.sensitivityFactorY) + + if self.motionHandler.gyroMixInputStarted() { + let normailizedGyroVector = CGVector(dx: self.stickOffsetToWeightedTouchInput(offset: self.motionHandler.gyroToStickOffset.dx), dy: -self.stickOffsetToWeightedTouchInput(offset: self.motionHandler.gyroToStickOffset.dy)) + + let xConvergent = normailizedGyroVector.dx.sign != weightedScrollerVector.dx.sign && abs(normailizedGyroVector.dx) <= abs(weightedScrollerVector.dx) + let yConvergent = normailizedGyroVector.dy.sign != weightedScrollerVector.dy.sign && abs(normailizedGyroVector.dy) <= abs(weightedScrollerVector.dy) + + synthesizedDeltaX = xConvergent ? normailizedGyroVector.dx + weightedScrollerVector.dx : weightedScrollerVector.dx + synthesizedDeltaY = yConvergent ? normailizedGyroVector.dy + weightedScrollerVector.dy : weightedScrollerVector.dy + + self.inertialScroller.vector.dx = synthesizedDeltaX/(self.VectorStickFactor*self.sensitivityFactorX) + self.inertialScroller.vector.dy = synthesizedDeltaY/(self.VectorStickFactor*self.sensitivityFactorY) + } + self.sendRightStickTouchPadEvent(weightedTouchX: weightedScrollerVector.dx, weightedTouchY: weightedScrollerVector.dy) } } self.inertialScroller.timer?.restart() } else {self.clearRightStickTouchPadFlag()} case "TRACKBALL": - if allSpawnedTouchesCount == 1, self.decelerationRate > 0 { + if allSpawnedTouchesCount == 1, self.inertiaEnabled() { if(mousePointerMoved){ self.startTrackballMomentum() mousePointerMoved = false //reset flag @@ -2264,7 +2277,13 @@ import UIKit } if self.motionControlButtonString == "ACCEL" {} if self.motionControlButtonString == "MOTION" {} + + if self.widgetType == WidgetTypeEnum.button && !OnScreenWidgetView.editMode { + self.sendComboButtonsUpEvent(comboStrings: self.comboButtonStrings) + self.functionalButtonDelegate?.alterAbsTouchDragWith(mouseButton:BUTTON_LEFT) + } buttonDownVisualEffectLayer.removeFromSuperlayer() + crossMarkLayer.removeFromSuperlayer() lrudIndicatorBall.removeFromSuperlayer() l3r3Indicator.removeFromSuperlayer() diff --git a/VoidLink/Input/StreamView.m b/VoidLink/Input/StreamView.m index 0b4f0a45..e0e96160 100644 --- a/VoidLink/Input/StreamView.m +++ b/VoidLink/Input/StreamView.m @@ -572,7 +572,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; widgetView.yawFactor = buttonState.yawFactor; widgetView.pitchFactor = buttonState.pitchFactor; widgetView.rollFactor = buttonState.rollFactor; - widgetView.decelerationRate = buttonState.decelerationRate; + widgetView.decelerationRateX = buttonState.decelerationRateX; + widgetView.decelerationRateY = buttonState.decelerationRateY; widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset; widgetView.minStickOffset = buttonState.minStickOffset; widgetView.buttonMode = buttonState.buttonMode; @@ -1413,7 +1414,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; - (void)alterAbsTouchDragWith:(int32_t)mouseButton{ if([touchHandler isKindOfClass:[AbsoluteTouchHandler class]]){ AbsoluteTouchHandler* handler = (AbsoluteTouchHandler* )touchHandler; - handler.mouseButtonForCursorMove = mouseButton; + AbsoluteTouchHandler.mouseButtonForCursorMove = mouseButton; [handler pauseLeftButtonDrag]; } else return; diff --git a/VoidLink/Input/TouchPadGestureHandler.swift b/VoidLink/Input/TouchPadGestureHandler.swift index 10496391..4f64fe76 100644 --- a/VoidLink/Input/TouchPadGestureHandler.swift +++ b/VoidLink/Input/TouchPadGestureHandler.swift @@ -18,7 +18,7 @@ import UIKit @objc public static var pinchSensitivity:CGFloat = 1.0 @objc public static var displayLinkRate:CGFloat = 60 - private static var inertialScroller: InertialScroller = InertialScroller(decelerationRate: 0.915, displayLinkRate: displayLinkRate) { + private static var inertialScroller: InertialScroller = InertialScroller(decelerationRate: displayLinkRate > 110 ? 0.96 : 0.9, displayLinkRate: displayLinkRate) { if ctrlDown {return} LiSendHighResScrollEvent(Int16(inertialScroller.vector.dy*7*scrollSensitivity)) if TouchPadGestureHandler.enableHorizontalScroll {LiSendHighResHScrollEvent(Int16(-inertialScroller.vector.dx*7*scrollSensitivity))} diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index 37108fbf..313e8beb 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -79,7 +79,7 @@ - + diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 7e9661d1..eab2bfca 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -801,13 +801,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "透明度:%.2f" + "value" : "透明度: %.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "透明度:%.2f" + "value" : "透明度: %.2f" } } } @@ -1129,13 +1129,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "邊框寬度:%.2f" + "value" : "邊框寬度: %.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "邊框寬度:%.2f" + "value" : "邊框寬度: %.2f" } } } @@ -1711,13 +1711,36 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "減速率:%.3f " + "value" : "減速率: %.3f " } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "減速率:%.3f " + "value" : "減速率: %.3f " + } + } + } + }, + "DecelerationRateY: %.3f " : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "减速率Y: %.3f " + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "減速率Y: %.3f " + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "減速率Y: %.3f " } } } @@ -3042,13 +3065,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "指示器偏移:%.0f" + "value" : "指示器偏移: %.0f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "指示器偏移:%.0f" + "value" : "指示器偏移: %.0f" } } } @@ -5970,13 +5993,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "靈敏度X:%.2f" + "value" : "靈敏度X: %.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "靈敏度X:%.2f" + "value" : "靈敏度X: %.2f" } } } @@ -5993,13 +6016,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "靈敏度Y:%.2f" + "value" : "靈敏度Y: %.2f" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "靈敏度Y:%.2f" + "value" : "靈敏度Y: %.2f" } } } diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index aa33cbea..4937791b 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -34,6 +34,12 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { BorderWidthSliderModeCount }; +typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) { + decelerationRate2D, + decelerationRateY, + DecelerationRateSliderModeCount +}; + @end @@ -43,6 +49,7 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { OnScreenWidgetView* selectedWidgetView; AlphaSliderMode alphaSliderMode; BorderWidthSliderMode borderWidthSliderMode; + DecelerationRateSliderMode decelerationRateSliderMode; CALayer* selectedControllerLayer; CGRect controllerLoadedBounds; bool widgetViewSelected; @@ -157,7 +164,8 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { widgetView.yawFactor = buttonState.yawFactor; widgetView.pitchFactor = buttonState.pitchFactor; widgetView.rollFactor = buttonState.rollFactor; - widgetView.decelerationRate = buttonState.decelerationRate; + widgetView.decelerationRateX = buttonState.decelerationRateX; + widgetView.decelerationRateY = buttonState.decelerationRateY; widgetView.stickIndicatorOffset = buttonState.stickIndicatorOffset; widgetView.minStickOffset = buttonState.minStickOffset; widgetView.buttonMode = buttonState.buttonMode; @@ -353,7 +361,7 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { } } -- (void)switchAlphaSlider:(UISwipeGestureRecognizer *)sender { +- (void)switchAlphaSlider:(UITapGestureRecognizer *)sender { if(!widgetViewSelected) return; alphaSliderMode = (alphaSliderMode + 1) % AlphaSliderModeCount; [self loadWidgetAlphas]; @@ -381,7 +389,13 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { } } -- (void)switchBorderWidthSlider:(UISwipeGestureRecognizer *)sender { +- (void)switchDecelerationRateSlider:(UITapGestureRecognizer *)sender { + if(!widgetViewSelected) return; + decelerationRateSliderMode = (decelerationRateSliderMode + 1) % DecelerationRateSliderModeCount; + [self loadDecelerationRates]; +} + +- (void)switchBorderWidthSlider:(UITapGestureRecognizer *)sender { if(!widgetViewSelected) return; borderWidthSliderMode = (borderWidthSliderMode + 1) % BorderWidthSliderModeCount; OnScreenWidgetView.isTweakingHighlightSize = borderWidthSliderMode == hightlightSize; @@ -731,7 +745,8 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { newWidget.yawFactor = widget.yawFactor; newWidget.pitchFactor = widget.pitchFactor; newWidget.rollFactor = widget.rollFactor; - newWidget.decelerationRate = widget.decelerationRate; + newWidget.decelerationRateX = widget.decelerationRateX; + newWidget.decelerationRateY = widget.decelerationRateY; newWidget.stickIndicatorOffset = widget.stickIndicatorOffset; newWidget.minStickOffset = widget.minStickOffset; [newWidget setVibrationWithStyle:widget.vibrationStyle]; @@ -978,9 +993,9 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { [self autoFitLabel:self.autoTapLabel]; self.decelerationRateStack.hidden = !selectedWidgetView.hasInertia; - [self.decelerationRateSlider setValue:selectedWidgetView.decelerationRate]; + [self.decelerationRateSlider setValue:selectedWidgetView.decelerationRateX]; [self autoFitLabel:self.decelerationRateLabel]; - [self decelerationRateSliderMoved:self.decelerationRateSlider]; + [self loadDecelerationRates]; self.mouseDownButtonStack.hidden = !selectedWidgetView.isMousePadWithButtonActions; self.mouseButtonDownSelector.selectedSegmentIndex = selectedWidgetView.mouseButtonAction; @@ -1208,9 +1223,23 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { return; } +- (void)loadDecelerationRates { + self.decelerationRateSlider.value = decelerationRateSliderMode == decelerationRate2D ? selectedWidgetView.decelerationRateX : selectedWidgetView.decelerationRateY; + NSString* labelText = [LocalizationHelper localizedStringForKey:decelerationRateSliderMode == decelerationRate2D ? @"Deceleration Rate: %.3f " : @"DecelerationRateY: %.3f ", self.decelerationRateSlider.value]; + [self.decelerationRateLabel setText: labelText]; +} + - (void)decelerationRateSliderMoved:(UISlider* )sender{ - [self.decelerationRateLabel setText:[LocalizationHelper localizedStringForKey:@"Deceleration Rate: %.3f ", sender.value]]; - if(self->selectedWidgetView != nil && self->widgetViewSelected) self->selectedWidgetView.decelerationRate = sender.value; + if(self->selectedWidgetView != nil && self->widgetViewSelected){ + if(decelerationRateSliderMode == decelerationRate2D){ + self->selectedWidgetView.decelerationRateX = sender.value; + self->selectedWidgetView.decelerationRateY = sender.value; + } + else{ + self->selectedWidgetView.decelerationRateY = sender.value; + } + } + [self loadDecelerationRates]; return; } @@ -1361,6 +1390,11 @@ typedef NS_ENUM(NSUInteger, BorderWidthSliderMode) { self.rollFactorLabel.text = [LocalizationHelper localizedStringForKey:@"Roll Factor"]; self.rollFactorStack.hidden = YES; + UITapGestureRecognizer *decelerationRateSliderTapGesture = + [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(switchDecelerationRateSlider:)]; + [self.decelerationRateLabel addGestureRecognizer:decelerationRateSliderTapGesture]; + self.decelerationRateLabel.userInteractionEnabled = true; [self.decelerationRateSlider addTarget:self action:@selector(decelerationRateSliderMoved:) forControlEvents:(UIControlEventValueChanged)]; self.decelerationRateLabel.text = [LocalizationHelper localizedStringForKey:@"Deceleration Rate"]; self.decelerationRateStack.hidden = YES; diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 164c4d8f..cd1cb8b9 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -342,11 +342,11 @@ - (void)reConfigStreamViewRealtime { //if(!viewJustLoaded) [self handleViewResize]; - [self reConfigStreamViewRealtimeAndReloadSettings:YES]; + [self reConfigStreamViewRealtimeAndReloadSettings:YES reloadOnscreenWidgets:NO]; } // key implementation of reconfiguring streamview after realtime setting menu is closed. -- (void)reConfigStreamViewRealtimeAndReloadSettings:(BOOL)reloadSettings{ +- (void)reConfigStreamViewRealtimeAndReloadSettings:(BOOL)reloadSettings reloadOnscreenWidgets:(BOOL)reloadOnscreenWidgets{ //[self.view removeGestureRecognizer:] //first, remove all gesture recognizers: for (UIGestureRecognizer *recognizer in _streamView.gestureRecognizers) { @@ -384,7 +384,7 @@ // we got self.view passed to streamView class as the topLayerView, will be useful in many cases [self->_streamView reloadOnScreenControlsRealtimeWith:(ControllerSupport*)_controllerSupport andConfig:(StreamConfiguration*)_streamConfig]; //reload OSC here. - [self->_streamView reloadOnScreenWidgetViews]; //reload keyboard buttons here. the keyboard widget view will be added to the streamframe view instead streamview, the highest layer, which saves a lot of reengineering + if(viewJustLoaded||reloadOnscreenWidgets) [self->_streamView reloadOnScreenWidgetViews]; //reload keyboard buttons here. the keyboard widget view will be added to the streamframe view instead streamview, the highest layer, which saves a lot of reengineering [self reloadAirPlayConfig]; [self mousePresenceChanged]; @@ -929,6 +929,7 @@ - (void) returnToMainFrame { [_streamView clearOnScreenWidgets]; + if(micHandler) [micHandler clean]; // Reset display mode back to default [self updatePreferredDisplayMode:NO]; @@ -983,7 +984,7 @@ if (_streamVideoRenderView && _streamView) { [_streamView insertSubview:_streamVideoRenderView atIndex:0]; [self handleViewResize]; // Adjust frames as needed - [self reConfigStreamViewRealtimeAndReloadSettings:YES]; + [self reConfigStreamViewRealtimeAndReloadSettings:YES reloadOnscreenWidgets:YES]; } } NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; @@ -1517,7 +1518,7 @@ // _settings.enableGraphs = _settings.statsOverlayEnabled; // Reconfigure the UI using the current in-memory settings, without reloading from disk - [self reConfigStreamViewRealtimeAndReloadSettings:NO]; + [self reConfigStreamViewRealtimeAndReloadSettings:NO reloadOnscreenWidgets:NO]; } - (void)toggleMouseCapture{ @@ -1651,7 +1652,7 @@ } dispatch_block_t block = dispatch_block_create(0, ^{ [self handleViewResize]; - [self reConfigStreamViewRealtimeAndReloadSettings:YES]; + [self reConfigStreamViewRealtimeAndReloadSettings:YES reloadOnscreenWidgets:YES]; }); _delayedRemoveExtScreen = block; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));