diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index 40d4b31e..80ddac45 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -1360,16 +1360,16 @@ - + - + @@ -1474,6 +1474,26 @@ + + + + + + + + + + + + + + + @@ -1481,6 +1501,8 @@ + + @@ -2377,7 +2399,7 @@ - + @@ -2386,13 +2408,13 @@ - + - + - + diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard index a1419f41..1dc675dd 100644 --- a/VoidLink/Base.lproj/iPhone.storyboard +++ b/VoidLink/Base.lproj/iPhone.storyboard @@ -2214,16 +2214,16 @@ - + - + @@ -2257,6 +2257,26 @@ + + + + + + + + + + + + + + + @@ -2264,6 +2284,8 @@ + + diff --git a/VoidLink/Database/DataManager.h b/VoidLink/Database/DataManager.h index 2b636aeb..3c2efe89 100644 --- a/VoidLink/Database/DataManager.h +++ b/VoidLink/Database/DataManager.h @@ -126,6 +126,7 @@ typedef NS_ENUM(NSInteger, WidgetSizeTransition) { hapticEngine:(NSInteger)hapticEngine edgeSlidingSensitivity:(CGFloat)edgeSlidingSensitivity audioEngine:(NSInteger)audioEngine + appendLeftClick:(BOOL)appendLeftClick backgroundSessionTimer:(NSInteger)backgroundSessionTimer; - (NSArray*) getHosts; diff --git a/VoidLink/Database/DataManager.m b/VoidLink/Database/DataManager.m index 8d3c8711..a1c03f0a 100644 --- a/VoidLink/Database/DataManager.m +++ b/VoidLink/Database/DataManager.m @@ -109,6 +109,7 @@ hapticEngine:(NSInteger)hapticEngine edgeSlidingSensitivity:(CGFloat)edgeSlidingSensitivity audioEngine:(NSInteger)audioEngine + appendLeftClick:(BOOL)appendLeftClick backgroundSessionTimer:(NSInteger)backgroundSessionTimer{ [_managedObjectContext performBlockAndWait:^{ @@ -168,6 +169,7 @@ settingsToSave.hapticEngine = [NSNumber numberWithInteger:hapticEngine]; settingsToSave.edgeSlidingSensitivity = [NSNumber numberWithFloat:edgeSlidingSensitivity]; settingsToSave.audioEngine = [NSNumber numberWithInteger:audioEngine]; + settingsToSave.appendLeftClick = appendLeftClick; settingsToSave.rememberFoldState = rememberFoldState; [self saveData]; }]; diff --git a/VoidLink/Database/TemporarySettings.h b/VoidLink/Database/TemporarySettings.h index eff564bd..300c7221 100644 --- a/VoidLink/Database/TemporarySettings.h +++ b/VoidLink/Database/TemporarySettings.h @@ -83,6 +83,7 @@ @property (nonatomic, retain) NSNumber * appTheme; @property (nonatomic, retain) NSNumber * edgeSlidingSensitivity; @property (nonatomic, retain) NSNumber * audioEngine; +@property (nonatomic) BOOL appendLeftClick; @property (nonatomic, retain) NSNumber * framePacingMode; - (id) initFromSettings:(Settings*)settings; diff --git a/VoidLink/Database/TemporarySettings.m b/VoidLink/Database/TemporarySettings.m index 900813f7..005e863a 100644 --- a/VoidLink/Database/TemporarySettings.m +++ b/VoidLink/Database/TemporarySettings.m @@ -140,6 +140,7 @@ self.hapticEngine = settings.hapticEngine; self.uniqueId = settings.uniqueId; self.audioEngine = settings.audioEngine; + self.appendLeftClick = settings.appendLeftClick; #endif return self; diff --git a/VoidLink/Input/AbsoluteTouchHandler.m b/VoidLink/Input/AbsoluteTouchHandler.m index c356387d..841b2f6a 100644 --- a/VoidLink/Input/AbsoluteTouchHandler.m +++ b/VoidLink/Input/AbsoluteTouchHandler.m @@ -39,12 +39,16 @@ CGFloat slideGestureVerticalThreshold; CGFloat screenWidthWithThreshold; CGFloat _edgeTolerance; + + bool _appendMouseLeftClick; } - (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings { self = [self init]; self->streamView = view; + _appendMouseLeftClick = settings.appendLeftClick; + // upper screen check _edgeTolerance = settings.edgeSlidingSensitivity.floatValue; slideGestureVerticalThreshold = CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.4; @@ -140,6 +144,8 @@ // Remember this last touch for touch-down deadzoning lastTouchUp = [touches anyObject]; lastTouchUpLocation = [lastTouchUp locationInView:streamView]; + + if(_appendMouseLeftClick && [self touchDidntMoveOnScreen:lastTouchUp]) [self sendShortMouseLeftButtonClickEvent]; } } @@ -148,4 +154,25 @@ [self touchesEnded:touches withEvent:event]; } +- (void)sendShortMouseLeftButtonClickEvent{ + dispatch_time_t delayShort = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)); + dispatch_time_t delayLong = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.03 * NSEC_PER_SEC)); + dispatch_after(delayShort, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT); + dispatch_after(delayLong, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); + }); + }); +} + +- (bool)touchDidntMoveOnScreen:(UITouch* )touch{ + return [self isAdjacentPoints:[touch locationInView:streamView] from:lastTouchDownLocation tolerance:3]; +} + +- (BOOL)isAdjacentPoints:(CGPoint)currentPoint from:(CGPoint)originalPoint tolerance:(CGFloat)tolerance { + bool isAdjacent = hypotf(originalPoint.x - currentPoint.x, originalPoint.y - currentPoint.y) <= hypot(tolerance, tolerance); + return isAdjacent; +} + + @end diff --git a/VoidLink/Input/NativeTouchHandler.m b/VoidLink/Input/NativeTouchHandler.m index b7aaf31a..757919e7 100644 --- a/VoidLink/Input/NativeTouchHandler.m +++ b/VoidLink/Input/NativeTouchHandler.m @@ -17,7 +17,6 @@ @implementation NativeTouchHandler { StreamView* streamView; TemporarySettings* currentSettings; - bool activateCoordSelector; CGFloat pointerVelocityDividerLocationByPoints; bool asyncNativeTouch; @@ -43,13 +42,14 @@ CGRect streamViewBounds; int64_t moveEventIntervalNSec; + + bool _appendMouseLeftClick; } - (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings{ self = [super init]; self->streamView = view; self->currentSettings = settings; - self->activateCoordSelector = currentSettings.pointerVelocityModeDivider.floatValue != 1.0; self->moveEventIntervalNSec = (int64_t)(currentSettings.touchMoveEventInterval.intValue * 1000);; self->streamViewBounds = view.bounds; @@ -60,7 +60,9 @@ } self->activePointerIds = [NSMutableSet set]; self->blacklistedTouches = [NSMutableSet set]; - + + _appendMouseLeftClick = settings.appendLeftClick; + self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff; switch(settings.asyncNativeTouchPriority.intValue){ @@ -195,7 +197,7 @@ //NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved); // NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]); - targetCoords = activateCoordSelector ? [self selectCoordsFor:touch] : [touch locationInView:streamView]; + targetCoords = [self selectCoordsFor:touch]; CGPoint location = [self adjustCoordinatesForVideoArea:targetCoords]; CGSize videoSize = [self getVideoAreaSize]; @@ -215,7 +217,7 @@ // continue to the next loop if current touch is already captured by OSC. works only in regular native touch if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue; [self handleTouchDown:touch]; //generate & populate pointerId - if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch]; + [self populatePointerObjIntoDict:touch]; [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN]; } }); @@ -224,7 +226,7 @@ // continue to the next loop if current touch is already captured by OSC. works only in regular native touch if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue; [self handleTouchDown:touch]; //generate & populate pointerId - if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch]; + [self populatePointerObjIntoDict:touch]; [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN]; } } @@ -234,7 +236,7 @@ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, moveEventIntervalNSec), dispatch_get_main_queue(), ^{ for (UITouch* touch in touches){ if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue; - if(self->activateCoordSelector) [self updatePointerObjInDict:touch]; + [self updatePointerObjInDict:touch]; [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE]; [[self getPointerObjFromDict:touch] doesNeedResetCoords]; } @@ -251,8 +253,11 @@ } [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId [self removePointerId:touch]; //then remove pointerId - if(self->activateCoordSelector) [self removePointerObjFromDict:touch]; - [self->blacklistedTouches removeObject:@((uintptr_t)touch)]; + + if(self->_appendMouseLeftClick && [self touchDidntMoveOnScreen:touch]) [self sendShortMouseLeftButtonClickEvent]; + [self removePointerObjFromDict:touch]; + + [self->blacklistedTouches removeObject:touchAddrObj]; } }); } @@ -261,7 +266,6 @@ [self touchesEnded:touches withEvent:event]; } - - (void)populatePointerObjIntoDict:(UITouch*)touch{ NativeTouchPointer* pointer = [[NativeTouchPointer alloc] initWithTouch:touch]; pointer.useRelativeCoords = pointer.initialPoint.x > pointerVelocityDividerLocationByPoints; @@ -290,4 +294,25 @@ return pointer.useRelativeCoords ? pointer.latestRelativePoint : pointer.latestPoint; } +- (void)sendShortMouseLeftButtonClickEvent{ + dispatch_time_t delayShort = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)); + dispatch_time_t delayLong = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.03 * NSEC_PER_SEC)); + dispatch_after(delayShort, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT); + dispatch_after(delayLong, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); + }); + }); +} + +- (bool)touchDidntMoveOnScreen:(UITouch* )touch{ + NativeTouchPointer *pointer = [pointerObjDict objectForKey:@((uintptr_t)touch)]; + return [self isAdjacentPoints:[touch locationInView:streamView] from:pointer.initialPoint tolerance:3]; +} + +- (BOOL)isAdjacentPoints:(CGPoint)currentPoint from:(CGPoint)originalPoint tolerance:(CGFloat)tolerance { + bool isAdjacent = hypotf(originalPoint.x - currentPoint.x, originalPoint.y - currentPoint.y) <= hypot(tolerance, tolerance); + return isAdjacent; +} + @end diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index 5358b8b0..72711279 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -21,6 +21,7 @@ + diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index a7835276..6689dd82 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -766,6 +766,35 @@ } } }, + "appendLeftClickStackTip" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "This works for games like “Old School RuneScape” that requires double tap on touch screen" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "对需要在触摸屏上双击的游戏, 如 OSRS (Old School RuneScape) 有效" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "對需要在觸控螢幕上雙擊的遊戲, 如 OSRS(Old School RuneScape)有效" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "對需要在觸控螢幕上雙擊的遊戲, 如 OSRS(Old School RuneScape)有效" + } + } + } + }, "Applications" : { "extractionState" : "manual", "localizations" : { diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h index b11fafe7..6c62d381 100644 --- a/VoidLink/ViewControllers/SettingsViewController.h +++ b/VoidLink/ViewControllers/SettingsViewController.h @@ -181,6 +181,8 @@ @property (strong, nonatomic) IBOutlet UIStackView *audioEngineStack; @property (strong, nonatomic) IBOutlet UISegmentedControl *audioEngineSelector; +@property (strong, nonatomic) IBOutlet UIStackView *appendLeftClickStack; +@property (strong, nonatomic) IBOutlet UISwitch *appendLeftClickSwitch; @property (strong, nonatomic) IBOutlet UIStackView *testStack; diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 93e67b5d..37afc950 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -718,6 +718,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self addSetting:self.singleTapSensitivityStack ofId:@"singleTapSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:touchAndControlSection]; [self addSetting:self.onScreenWidgetStack ofId:@"onScreenWidgetStack" withInfoTag:YES withDynamicLabel:YES to:touchAndControlSection]; [self addSetting:self.buttonVisualFeedbackStack ofId:@"buttonVisualFeedbackStack" withInfoTag:NO withDynamicLabel:NO to:touchAndControlSection]; + [self addSetting:self.appendLeftClickStack ofId:@"appendLeftClickStack" withInfoTag:YES withDynamicLabel:NO to:touchAndControlSection]; [self addSetting:self.swapAbxyStack ofId:@"swapAbaxyStack" withInfoTag:NO withDynamicLabel:NO to:touchAndControlSection]; [self addSetting:self.hapticEngineStack ofId:@"hapticEngineStack" withInfoTag:NO withDynamicLabel:NO to:touchAndControlSection]; [self addSetting:self.emulatedControllerTypeStack ofId:@"emulatedControllerTypeStack" withInfoTag:YES withDynamicLabel:NO to:touchAndControlSection]; @@ -1281,6 +1282,10 @@ BOOL isCustomResolution(int resolutionSelected) { tipText = [LocalizationHelper localizedStringForKey:@"useBuiltinMicStackTip"]; showOnlineDocAction = false; } + if([sender.superview.accessibilityIdentifier isEqualToString: @"appendLeftClickStack"]){ + tipText = [LocalizationHelper localizedStringForKey:@"appendLeftClickStackTip"]; + showOnlineDocAction = false; + } UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message:tipText preferredStyle:UIAlertControllerStyleAlert]; @@ -1673,7 +1678,8 @@ BOOL isCustomResolution(int resolutionSelected) { [self.multiControllerSwitch setOn:self->tempSettings.multiController]; [self.swapAbxySwitch setOn:self->tempSettings.swapABXYButtons]; [self.buttonVisualFeedbackSwitch setOn:self->tempSettings.buttonVisualFeedback]; - + [self.appendLeftClickSwitch setOn:self->tempSettings.appendLeftClick]; + [self.hapticEngineSelector setSelectedSegmentIndex:self->tempSettings.hapticEngine.intValue]; bool hideHapticEngineStack = false; if(@available(iOS 13.0, tvOS 13.0, *)) hideHapticEngineStack = false; @@ -2324,6 +2330,8 @@ BOOL isCustomResolution(int resolutionSelected) { [self setHidden:!(sender.selectedSegmentIndex == RelativeTouch) forStack:self.singleTapSensitivityStack]; [self setHidden:![self isNotNativeTouchOnly] forStack:self.onScreenWidgetStack]; [self setHidden:![self isNotNativeTouchOnly] forStack:self.buttonVisualFeedbackStack]; + [self setHidden:!(([self isNotNativeTouchOnly] && sender.selectedSegmentIndex == NativeTouch) + || sender.selectedSegmentIndex == AbsoluteTouch) forStack:self.appendLeftClickStack]; [self handleOswGestureChange]; } @@ -2392,6 +2400,7 @@ BOOL isCustomResolution(int resolutionSelected) { - (void)enableOswForNativeTouchSwitchFlipped:(UISwitch *)sender{ [self setHidden:!sender.isOn forStack:self.onScreenWidgetStack]; [self setHidden:!sender.isOn forStack:self.buttonVisualFeedbackStack]; + [self setHidden:!sender.isOn forStack:self.appendLeftClickStack]; [self handleOswGestureChange]; } @@ -2861,6 +2870,7 @@ BOOL isCustomResolution(int resolutionSelected) { NSInteger hapticEngine = self.hapticEngineSelector.selectedSegmentIndex; CGFloat edgeSlidingSensitivity = self.edgeSlidingSensitivitySlider.value; NSInteger audioEngine = self.audioEngineSelector.selectedSegmentIndex; + BOOL appendLeftClick = self.appendLeftClickSwitch.isOn; NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value; [dataMan saveSettingsWithBitrate:_bitrate @@ -2916,6 +2926,7 @@ BOOL isCustomResolution(int resolutionSelected) { hapticEngine:hapticEngine edgeSlidingSensitivity:edgeSlidingSensitivity audioEngine:audioEngine + appendLeftClick:appendLeftClick backgroundSessionTimer:backgroundSessionTimer]; } diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings index 98f0b360..1d29bc0a 100644 --- a/VoidLink/mul.lproj/iPad.xcstrings +++ b/VoidLink/mul.lproj/iPad.xcstrings @@ -559,6 +559,36 @@ } } }, + "9Yx-H8-E3N.text" : { + "comment" : "Class = \"UILabel\"; text = \"Additional Left Click\"; ObjectID = \"9Yx-H8-E3N\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Additional Left Click" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "追加鼠标左单击" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "追加滑鼠左單擊" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "追加滑鼠左單擊" + } + } + } + }, "53H-ST-OtY.segmentTitles[0]" : { "comment" : "Class = \"UISegmentedControl\"; 53H-ST-OtY.segmentTitles[0] = \"Balanced\"; ObjectID = \"53H-ST-OtY\";", "extractionState" : "extracted_with_value", diff --git a/VoidLink/mul.lproj/iPhone.xcstrings b/VoidLink/mul.lproj/iPhone.xcstrings index 8865b2f6..e2cb18e0 100644 --- a/VoidLink/mul.lproj/iPhone.xcstrings +++ b/VoidLink/mul.lproj/iPhone.xcstrings @@ -3481,6 +3481,36 @@ } } }, + "qOa-te-sPj.text" : { + "comment" : "Class = \"UILabel\"; text = \"Additional Left Click\"; ObjectID = \"qOa-te-sPj\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Additional Left Click" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "追加鼠标左单击" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "追加滑鼠左單擊" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "追加滑鼠左單擊" + } + } + } + }, "qPQ-U2-aSg.text" : { "comment" : "Class = \"UILabel\"; text = \"Alpha\"; ObjectID = \"qPQ-U2-aSg\";", "extractionState" : "extracted_with_value",