From 074a31606c330433b188b4b2bdbc82370ea7fde0 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 08:19:50 +0800 Subject: [PATCH 01/18] revert edge detection for touchHandler --- VoidLink/Input/PureNativeTouchHandler.m | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/VoidLink/Input/PureNativeTouchHandler.m b/VoidLink/Input/PureNativeTouchHandler.m index 3b029709..27c58962 100644 --- a/VoidLink/Input/PureNativeTouchHandler.m +++ b/VoidLink/Input/PureNativeTouchHandler.m @@ -34,6 +34,7 @@ NSMutableSet *pointerIdPool; //pre-defined pool of pointerIds. NSMutableSet *unassignedPointerIds; NSMutableSet *blacklistedTouches; + bool doNotBlockTouchEvents; NSMutableDictionary *pointerObjDict; @@ -59,6 +60,7 @@ } self->activePointerIds = [NSMutableSet set]; self->blacklistedTouches = [NSMutableSet set]; + doNotBlockTouchEvents = true; self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff; @@ -164,7 +166,8 @@ //check if touch point is spawned on the left or right upper half screen edges, event to remote PC. this is for better handling in-stream slide gesture CGPoint initialPoint = [touch locationInView:self->streamView]; if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) { - [blacklistedTouches addObject:touchAddrObj]; + doNotBlockTouchEvents = false; + // [blacklistedTouches addObject:touchAddrObj]; } } @@ -188,8 +191,8 @@ - (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{ //if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash - if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return; - + // if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return; + CGPoint targetCoords; //NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved); // NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]); @@ -211,6 +214,7 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchDownQos, 0), ^{ for (UITouch* touch in touches){ + // continue to the next loop if current touch is already captured by OSC. works only in regular native touch [self handleTouchDown:touch]; //generate & populate pointerId if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch]; [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN]; @@ -218,6 +222,7 @@ }); else{ for (UITouch* touch in touches){ + // continue to the next loop if current touch is already captured by OSC. works only in regular native touch [self handleTouchDown:touch]; //generate & populate pointerId if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch]; [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN]; @@ -228,14 +233,16 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { if (asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchMoveQos, 0), ^{ for (UITouch* touch in touches){ + // continue to the next loop if current touch is already captured by OSC. works only in regular native touch if(self->activateCoordSelector) [self updatePointerObjInDict:touch]; - [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE]; + if(self->doNotBlockTouchEvents) [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE]; [[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service) usleep(self->touchMoveEventIntervalUs); } }); else { for (UITouch* touch in touches){ + // continue to the next loop if current touch is already captured by OSC. works only in regular native touch if(self->activateCoordSelector) [self updatePointerObjInDict:touch]; [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE]; [[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service) @@ -248,14 +255,18 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { if(asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchEndQos, 0), ^{ for (UITouch* touch in touches){ + // continue to the next loop if current touch is already captured by OSC. works only in regular native touch [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)]; + //[self->blacklistedTouches removeObject:@((uintptr_t)touch)]; } + self->doNotBlockTouchEvents = true; + //if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) }); else{ for (UITouch* touch in touches){ + // continue to the next loop if current touch is already captured by OSC. works only in regular native touch [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]; @@ -293,7 +304,6 @@ - (CGPoint)selectCoordsFor:(UITouch *)touch{ NativeTouchPointer *pointer = [pointerObjDict objectForKey:@((uintptr_t)touch)]; if(pointer == nil) return CGPointMake(0, 0); - // NSLog(@"using relative Coords: intialX %f, divider loc %f", pointer.initialPoint.x, pointerVelocityDividerLocationByPoints); return pointer.useRelativeCoords ? pointer.latestRelativePoint : pointer.latestPoint; } From 80f9118c0d3d844026e0dd2c23cdfcca8894f802 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 09:27:33 +0800 Subject: [PATCH 02/18] fix osw tool outbounds issue again --- VoidLink/Input/OnScreenWidgetView.swift | 4 ++-- VoidLink/Localization/Localizable.xcstrings | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index cca8b716..59f8f46f 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -1138,8 +1138,8 @@ import UIKit let offsetX = currentLocation.x - latestTouchLocation.x; let offsetY = currentLocation.y - latestTouchLocation.y; - let outOfBoundsX = center.x+offsetX > (self.superview?.bounds.width)! || center.x+offsetX < 0 - let outOfBoundsY = center.y+offsetY > (self.superview?.bounds.height)! || center.y+offsetY < 0 + let outOfBoundsX = center.x+offsetX >= (self.superview?.bounds.width)! || center.x+offsetX < 0 + let outOfBoundsY = center.y+offsetY >= (self.superview?.bounds.height)! || center.y+offsetY < 0 center = CGPoint(x: outOfBoundsX ? center.x : center.x+offsetX, y: outOfBoundsY ? center.y : center.y+offsetY) diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index d5129bae..fee9f464 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -295,13 +295,13 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "[ 開啟螢幕小工具 ]" + "value" : "[ 調整螢幕小工具版面配置 ]" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "[ 開啟螢幕小工具 ]" + "value" : "[ 調整螢幕小工具版面配置 ]" } } } From 323613e4db9d70728f8a8fcf7b113c906fa8c278 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 10:42:11 +0800 Subject: [PATCH 03/18] minor tweaks on UI --- VoidLink/Localization/Localizable.xcstrings | 8 ++++---- VoidLink/ViewControllers/MainFrameViewController.m | 4 ++-- VoidLink/ViewControllers/SettingsViewController.h | 1 + VoidLink/ViewControllers/SettingsViewController.m | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index fee9f464..d763de87 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -4603,25 +4603,25 @@ } } }, - "Touch & Control" : { + "Touch & Controller" : { "extractionState" : "manual", "localizations" : { "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "触屏与控制" + "value" : "触屏与手柄" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "觸控與控制" + "value" : "觸控與控制器" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "觸控與控制" + "value" : "觸控與控制器" } } } diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 3041ea50..84c53759 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1145,7 +1145,7 @@ static NSMutableSet* hostList; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.audioOnPcStack]; [settingsViewController.codecSelector setEnabled:!_settingsExpandedInStreamView]; [settingsViewController.yuv444Switch setEnabled:!_settingsExpandedInStreamView]; - [settingsViewController.hdrSwitch setEnabled:!_settingsExpandedInStreamView]; + [settingsViewController.hdrSwitch setEnabled:!_settingsExpandedInStreamView && [settingsViewController hdrSupported]]; [settingsViewController.gyroModeSelector setEnabled:!_settingsExpandedInStreamView || ![streamFrameViewController shallDisableGyroHotSwitch]]; [settingsViewController.emulatedControllerTypeSelector setEnabled:!_settingsExpandedInStreamView]; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.framepacingStack]; @@ -1471,7 +1471,7 @@ static NSMutableSet* hostList; if (@available(iOS 13.0, *)) { [_upButton setTitle:@""]; - UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:23 weight:UIImageSymbolWeightMedium ]; + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:21.5 weight:UIImageSymbolWeightMedium ]; UIImage *image = [[UIImage systemImageNamed:@"tv" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [_upButton setImage:image]; _upButton.imageInsets = UIEdgeInsetsMake(25, 20, 0, 15); diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h index 6e39d57a..e0c35611 100644 --- a/VoidLink/ViewControllers/SettingsViewController.h +++ b/VoidLink/ViewControllers/SettingsViewController.h @@ -122,6 +122,7 @@ #pragma clang diagnostic pop +- (bool)hdrSupported; - (void)saveSettings; + (bool)isLandscapeNow; - (void)updateResolutionTable; diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index cf6b85ef..6903890c 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -528,7 +528,7 @@ BOOL isCustomResolution(CGSize res) { touchAndControlSection = [[MenuSectionView alloc] init]; touchAndControlSection.delegate = self; - touchAndControlSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Touch & Control"]; + touchAndControlSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Touch & Controller"]; if (@available(iOS 13.0, *)) { [touchAndControlSection setSectionWithIcon:[UIImage imageNamed:@"arcade.stick.console"] andSize:20.5]; } @@ -1324,7 +1324,7 @@ BOOL isCustomResolution(CGSize res) { if (![self hdrSupported]) { [self.hdrSwitch setOn:NO]; - [self widget:self.hdrSwitch setEnabled:NO]; + [self.hdrSwitch setEnabled:NO]; } else { [self.hdrSwitch setOn:currentSettings.enableHdr]; From 2b0fb3844b901a4235d80a0dc504e64b9f0f9c20 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 11:21:51 +0800 Subject: [PATCH 04/18] load default favorite setting stacks if persisted data is not available --- .../VoidLink v1.0.xcdatamodel/contents | 2 +- .../ViewControllers/SettingsViewController.m | 40 ++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index f59b2133..c49f6af2 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -40,7 +40,7 @@ - + diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 6903890c..88a5fee8 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -389,7 +389,7 @@ BOOL isCustomResolution(CGSize res) { _parentStack.translatesAutoresizingMaskIntoConstraints = NO; [self.scrollView addSubview:_parentStack]; [NSLayoutConstraint activateConstraints:@[ - [_parentStack.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor constant: currentSettingsMenuMode == AllSettings ? [self getStandardNavBarHeight] : [self getStandardNavBarHeight]+20], + [_parentStack.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor constant: currentSettingsMenuMode == AllSettings ? [self getStandardNavBarHeight] : [self getStandardNavBarHeight]+10], [_parentStack.bottomAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.bottomAnchor constant:-20], [_parentStack.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant: 0], //mark: settingMenuLayout [_parentStack.widthAnchor constraintEqualToAnchor:self.view.widthAnchor constant:-20] // section width adjusted here //mark: settingMenuLayout @@ -1166,6 +1166,20 @@ BOOL isCustomResolution(CGSize res) { } } +/* +- (BOOL)isFirstLaunch { + NSString *key = @"appHasLaunchedBefore"; + BOOL launchedBefore = [[NSUserDefaults standardUserDefaults] boolForKey:key]; + + if (!launchedBefore) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:key]; + [[NSUserDefaults standardUserDefaults] synchronize]; // iOS 12+ 可省略 + return YES; + } + return NO; +} +*/ + - (void)saveFavoriteSettingStackIdentifiers { if(currentSettingsMenuMode != AllSettings){ @@ -1175,18 +1189,40 @@ BOOL isCustomResolution(CGSize res) { [_favoriteSettingStackIdentifiers addObject:_parentStack.arrangedSubviews[i].accessibilityIdentifier]; } } - [[NSUserDefaults standardUserDefaults] setObject:_favoriteSettingStackIdentifiers forKey:@"FavoriteSettingStackIdentifiers"]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (void)loadFavoriteSettingStackIdentifiers { NSArray *savedArray = [[NSUserDefaults standardUserDefaults] objectForKey:@"FavoriteSettingStackIdentifiers"]; + if ([savedArray isKindOfClass:[NSArray class]]) { _favoriteSettingStackIdentifiers = [savedArray mutableCopy]; } else { _favoriteSettingStackIdentifiers = [NSMutableArray array]; + [_favoriteSettingStackIdentifiers addObject:@"resolutionStack"]; + [_favoriteSettingStackIdentifiers addObject:@"fpsStack"]; + [_favoriteSettingStackIdentifiers addObject:@"bitrateStack"]; + [_favoriteSettingStackIdentifiers addObject:@"framepacingStack"]; + [_favoriteSettingStackIdentifiers addObject:@"codecStack"]; + [_favoriteSettingStackIdentifiers addObject:@"hdrStack"]; + [_favoriteSettingStackIdentifiers addObject:@"yuv444Stack"]; + [_favoriteSettingStackIdentifiers addObject:@"pipStack"]; + [_favoriteSettingStackIdentifiers addObject:@"touchModeStack"]; + [_favoriteSettingStackIdentifiers addObject:@"pointerVelocityDividerStack"]; + [_favoriteSettingStackIdentifiers addObject:@"pointerVelocityFactorStack"]; + [_favoriteSettingStackIdentifiers addObject:@"mousePointerVelocityStack"]; + [_favoriteSettingStackIdentifiers addObject:@"onScreenWidgetStack"]; + [_favoriteSettingStackIdentifiers addObject:@"pipStack"]; + [_favoriteSettingStackIdentifiers addObject:@"backgroundSessionTimerStack"]; + [_favoriteSettingStackIdentifiers addObject:@"statsOverlayStack"]; + [_favoriteSettingStackIdentifiers addObject:@"softKeyboardGestureStack"]; + [_favoriteSettingStackIdentifiers addObject:@"slideToSettingsScreenEdgeStack"]; + [_favoriteSettingStackIdentifiers addObject:@"slideToToolboxScreenEdgeStack"]; + [_favoriteSettingStackIdentifiers addObject:@"slideToSettingsDistanceStack"]; + [_favoriteSettingStackIdentifiers addObject:@"unlockDisplayOrientationStack"]; } + /* for(NSString* str in _favoriteSettingStackIdentifiers){ NSLog(@"favarite setting loaded: %@", str); From e51701ef0016ebe4f5fa751fda4723ac08532c5b Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 11:51:18 +0800 Subject: [PATCH 05/18] background info update tweak --- VoidLink/HostCardView.m | 4 ++-- VoidLink/UIAppView.m | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VoidLink/HostCardView.m b/VoidLink/HostCardView.m index 9081bb2b..ff4852e2 100644 --- a/VoidLink/HostCardView.m +++ b/VoidLink/HostCardView.m @@ -505,7 +505,7 @@ static const float REFRESH_CYCLE = 2.0f; - (void) updateLoop { // Stop immediately if the view has been detached - if (self.superview == nil) { + if (self.superview == nil || [self.delegate isStreaming]) { return; } @@ -514,7 +514,7 @@ static const float REFRESH_CYCLE = 2.0f; [self updateContentsForHost:_host]; // Queue the next refresh cycle - if(![self.delegate isStreaming]) [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; + [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; } - (void) updateContentsForHost:(TemporaryHost*)host { diff --git a/VoidLink/UIAppView.m b/VoidLink/UIAppView.m index b02538b8..54d9793b 100644 --- a/VoidLink/UIAppView.m +++ b/VoidLink/UIAppView.m @@ -260,7 +260,7 @@ static UIImage* noImage; - (void) updateLoop { // Stop immediately if the view has been detached - if (self.superview == nil) { + if (self.superview == nil || ![self.updateLoopDelegate isInAppView]) { return; } @@ -282,7 +282,7 @@ static UIImage* noImage; [self setAlpha:_app.hidden ? 0.4 : 1.0]; // Queue the next refresh cycle - if([self.updateLoopDelegate isInAppView]) [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; + [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; } @end From ed1efdd1022f268fe0c721ec75c038cc43f710b3 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 11:53:58 +0800 Subject: [PATCH 06/18] remove codes of deprecated views --- VoidLink.xcodeproj/project.pbxproj | 12 - VoidLink/ComputerScrollView.h | 13 - VoidLink/ComputerScrollView.m | 28 -- VoidLink/UIComputerView.h | 29 --- VoidLink/UIComputerView.m | 244 ------------------ .../ViewControllers/MainFrameViewController.h | 3 +- .../ViewControllers/MainFrameViewController.m | 2 - 7 files changed, 1 insertion(+), 330 deletions(-) delete mode 100644 VoidLink/ComputerScrollView.h delete mode 100644 VoidLink/ComputerScrollView.m delete mode 100644 VoidLink/UIComputerView.h delete mode 100644 VoidLink/UIComputerView.m diff --git a/VoidLink.xcodeproj/project.pbxproj b/VoidLink.xcodeproj/project.pbxproj index f7e9d4fc..62b899ee 100644 --- a/VoidLink.xcodeproj/project.pbxproj +++ b/VoidLink.xcodeproj/project.pbxproj @@ -59,7 +59,6 @@ DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1F5A06206436B20037755F /* ConnectionHelper.m */; }; F74BEF9C2C1A705600224667 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = F74BEF9B2C1A705600224667 /* OpenSSL */; }; FB1A674D2131E65900507771 /* KeyboardSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1A674C2131E65900507771 /* KeyboardSupport.m */; }; - FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */; }; FB1D599A1BBCCD7E00F482CA /* AppCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */; }; FB290CF219B2C406004C83CF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF119B2C406004C83CF /* Foundation.framework */; }; FB290CF419B2C406004C83CF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF319B2C406004C83CF /* CoreGraphics.framework */; }; @@ -104,7 +103,6 @@ FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494F19FF2174002D2A60 /* SettingsViewController.m */; }; FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */; }; FBD349621A0089F6002D2A60 /* DataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD349611A0089F6002D2A60 /* DataManager.m */; }; - FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86DF19F7A837001C18A8 /* UIComputerView.m */; }; FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86E519F82297001C18A8 /* UIAppView.m */; }; /* End PBXBuildFile section */ @@ -233,8 +231,6 @@ FB1A67642132419A00507771 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; FB1A67E421324A1F00507771 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; FB1A67E921324DF300507771 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; }; - FB1D59951BBCCB6400F482CA /* ComputerScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComputerScrollView.h; sourceTree = ""; }; - FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComputerScrollView.m; sourceTree = ""; }; FB1D59981BBCCD7E00F482CA /* AppCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppCollectionView.h; sourceTree = ""; }; FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppCollectionView.m; sourceTree = ""; }; FB290CEE19B2C406004C83CF /* VoidLink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoidLink.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -319,8 +315,6 @@ FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWRevealViewController.m; sourceTree = ""; }; FBD349601A0089F6002D2A60 /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataManager.h; path = Database/DataManager.h; sourceTree = ""; }; FBD349611A0089F6002D2A60 /* DataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DataManager.m; path = Database/DataManager.m; sourceTree = ""; }; - FBDE86DE19F7A837001C18A8 /* UIComputerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIComputerView.h; sourceTree = ""; }; - FBDE86DF19F7A837001C18A8 /* UIComputerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIComputerView.m; sourceTree = ""; }; FBDE86E419F82297001C18A8 /* UIAppView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIAppView.h; sourceTree = ""; }; FBDE86E519F82297001C18A8 /* UIAppView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAppView.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -513,8 +507,6 @@ 841BAC912DE35D6B00DA639E /* ThemeManager.m */, 847D2FD82DDA00C800937A62 /* MenuSectionView.h */, 847D2FDA2DDA015F00937A62 /* MenuSectionView.m */, - FBDE86DE19F7A837001C18A8 /* UIComputerView.h */, - FBDE86DF19F7A837001C18A8 /* UIComputerView.m */, FBDE86E419F82297001C18A8 /* UIAppView.h */, FBDE86E519F82297001C18A8 /* UIAppView.m */, 1888C1902E24C2C800108730 /* AboutView.swift */, @@ -522,8 +514,6 @@ FB89463719F6473800339C8A /* Launch Screen.xib */, FB290D0819B2C406004C83CF /* Images.xcassets */, FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */, - FB1D59951BBCCB6400F482CA /* ComputerScrollView.h */, - FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */, FB1D59981BBCCD7E00F482CA /* AppCollectionView.h */, FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */, 843E93BD2C41528700B1651F /* OrientationHelper.swift */, @@ -889,12 +879,10 @@ 84351B112C29ACCB00A77989 /* OSCProfilesManager.m in Sources */, FB89462919F646E200339C8A /* mkcert.c in Sources */, FB9AFD281A7C84ED00872C98 /* HttpResponse.m in Sources */, - FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */, FB89463019F646E200339C8A /* StreamConfiguration.m in Sources */, FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */, 84203F232C5F24C00032EEED /* OnScreenWidgetView.swift in Sources */, 84FD0B512C31338D006B5BE5 /* LocalizationHelper.m in Sources */, - FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */, FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */, 843CC7F02C1E757D00BBC475 /* NativeTouchPointer.m in Sources */, FB89462C19F646E200339C8A /* HttpManager.m in Sources */, diff --git a/VoidLink/ComputerScrollView.h b/VoidLink/ComputerScrollView.h deleted file mode 100644 index 16a93cef..00000000 --- a/VoidLink/ComputerScrollView.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// ComputerScrollView.h -// Moonlight -// -// Created by Diego Waxemberg on 9/30/15. -// Copyright © 2015 Moonlight Stream. All rights reserved. -// - -#import - -@interface ComputerScrollView : UIScrollView - -@end diff --git a/VoidLink/ComputerScrollView.m b/VoidLink/ComputerScrollView.m deleted file mode 100644 index c9610170..00000000 --- a/VoidLink/ComputerScrollView.m +++ /dev/null @@ -1,28 +0,0 @@ -// -// ComputerScrollView.m -// Moonlight -// -// Created by Diego Waxemberg on 9/30/15. -// Copyright © 2015 Moonlight Stream. All rights reserved. -// - -#import "ComputerScrollView.h" - -@implementation ComputerScrollView - -- (BOOL)touchesShouldCancelInContentView:(UIView *)view { - if ([view isKindOfClass:[UIButton class]]) { - return YES; - } - return [super touchesShouldCancelInContentView:view]; -} - -/* -// Only override drawRect: if you perform custom drawing. -// An empty implementation adversely affects performance during animation. -- (void)drawRect:(CGRect)rect { - // Drawing code -} -*/ - -@end diff --git a/VoidLink/UIComputerView.h b/VoidLink/UIComputerView.h deleted file mode 100644 index 1e5c6a1a..00000000 --- a/VoidLink/UIComputerView.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// UIComputerView.h -// Moonlight -// -// Created by Diego Waxemberg on 10/22/14. -// Copyright (c) 2014 Moonlight Stream. All rights reserved. -// - -#import -#import "TemporaryHost.h" - -@protocol HostCallback - -- (void) noneUserInitiatedHostAction:(TemporaryHost*)host view:(UIView*)view; -- (void) hostCardLongPressed:(TemporaryHost*)host view:(UIView*)view; -- (void) addHostTapped; - -@end - -#if !TARGET_OS_TV -@interface UIComputerView : UIButton -#else -@interface UIComputerView : UIButton -#endif - -- (id) initWithComputer:(TemporaryHost*)host andCallback:(id)callback; -- (id) initForAddWithCallback:(id)callback; - -@end diff --git a/VoidLink/UIComputerView.m b/VoidLink/UIComputerView.m deleted file mode 100644 index e3f26ef5..00000000 --- a/VoidLink/UIComputerView.m +++ /dev/null @@ -1,244 +0,0 @@ -// -// UIComputerView.m -// Moonlight -// -// Created by Diego Waxemberg on 10/22/14. -// Copyright (c) 2014 Moonlight Stream. All rights reserved. -// -// Modified by True砖家 since 2024.6.30 -// Copyright © 2024 True砖家 @ Bilibili. All rights reserved. -// -// - -#import "UIComputerView.h" -#import "LocalizationHelper.h" - -@implementation UIComputerView { - TemporaryHost* _host; - UIImageView* _hostIcon; - UILabel* _hostLabel; - UIImageView* _hostOverlay; - UIActivityIndicatorView* _hostSpinner; - id _callback; - CGSize _labelSize; -} -static const float REFRESH_CYCLE = 2.0f; - -#if TARGET_OS_TV -static const int ITEM_PADDING = 50; -static const int LABEL_DY = 40; -#else -static const int ITEM_PADDING = 0; -static const int LABEL_DY = 20; -#endif - -- (id) init { - self = [super init]; - -#if TARGET_OS_TV - self.frame = CGRectMake(0, 0, 400, 400); -#else - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - self.frame = CGRectMake(0, 0, 200, 200); - } else { - self.frame = CGRectMake(0, 0, 100, 100); - } -#endif - - _hostIcon = [[UIImageView alloc] initWithFrame:self.frame]; - [_hostIcon setImage:[UIImage imageNamed:@"Computer"]]; - - self.layer.shadowColor = [[UIColor blackColor] CGColor]; - self.layer.shadowOffset = CGSizeMake(5,8); - self.layer.shadowOpacity = 0.3; - - [self addTarget:self action:@selector(hostButtonSelected:) forControlEvents:UIControlEventTouchDown]; - [self addTarget:self action:@selector(hostButtonDeselected:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchDragExit]; - - _hostLabel = [[UILabel alloc] init]; - _hostLabel.textColor = [UIColor whiteColor]; - - _hostOverlay = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width / 3, _hostIcon.frame.size.height / 4, _hostIcon.frame.size.width / 3, self.frame.size.height / 3)]; - _hostSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; - [_hostSpinner setFrame:_hostOverlay.frame]; - _hostSpinner.userInteractionEnabled = NO; - _hostSpinner.hidesWhenStopped = YES; - - [self addSubview:_hostLabel]; - [self addSubview:_hostIcon]; - -#if TARGET_OS_TV - _hostIcon.clipsToBounds = NO; - _hostIcon.adjustsImageWhenAncestorFocused = YES; - _hostIcon.masksFocusEffectToContents = YES; - - self.adjustsImageWhenHighlighted = NO; - - _hostOverlay.masksFocusEffectToContents = YES; - _hostOverlay.adjustsImageWhenAncestorFocused = NO; - - [_hostIcon.overlayContentView addSubview:_hostOverlay]; - [_hostIcon.overlayContentView addSubview:_hostSpinner]; -#else - [self addSubview:_hostOverlay]; - [self addSubview:_hostSpinner]; - - if (@available(iOS 13.4.1, *)) { - // Allow the button style to change when moused over - self.pointerInteractionEnabled = YES; - } -#endif - - return self; -} - -- (void) hostButtonSelected:(id)sender { - _hostIcon.layer.opacity = 0.5f; - _hostSpinner.layer.opacity = 0.5f; - _hostOverlay.layer.opacity = 0.5f; -} -- (void) hostButtonDeselected:(id)sender { - _hostIcon.layer.opacity = 1.0f; - _hostSpinner.layer.opacity = 1.0f; - _hostOverlay.layer.opacity = 1.0f; -} - -- (id) initForAddWithCallback:(id)callback { - self = [self init]; - _callback = callback; - - [self addTarget:self action:@selector(addClicked) forControlEvents:UIControlEventPrimaryActionTriggered]; - - [_hostLabel setText:[LocalizationHelper localizedStringForKey:@"Add Host Manually"]]; - [_hostLabel sizeToFit]; - - [_hostOverlay setImage:[UIImage imageNamed:@"AddOverlayIcon"]]; - - [self updateBounds]; - - return self; -} - -- (id) initWithComputer:(TemporaryHost*)host andCallback:(id)callback { - self = [self init]; - _host = host; - _callback = callback; - - // Use UIContextMenuInteraction on iOS 13.0+ and a standard UILongPressGestureRecognizer - // for tvOS devices and iOS prior to 13.0. -#if !TARGET_OS_TV - if (@available(iOS 13.0, *)) { - UIContextMenuInteraction* rightClickInteraction = [[UIContextMenuInteraction alloc] initWithDelegate:self]; - [self addInteraction:rightClickInteraction]; - } - else -#endif - { - UILongPressGestureRecognizer* longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(hostLongClicked:)]; - [self addGestureRecognizer:longPressRecognizer]; - } - - [self addTarget:self action:@selector(hostClicked) forControlEvents:UIControlEventPrimaryActionTriggered]; - - [self updateContentsForHost:host]; - - return self; -} - -- (void)didMoveToSuperview { - // Start our update loop when we are added to our cell - if (self.superview != nil && _host != nil) { - [self updateLoop]; - } -} - -- (void) updateBounds { - float x = FLT_MAX; - float y = FLT_MAX; - float width = 0; - float height; - - float iconX = _hostIcon.frame.origin.x + _hostIcon.frame.size.width / 2; - _hostLabel.center = CGPointMake(iconX, _hostIcon.frame.origin.y + _hostIcon.frame.size.height + LABEL_DY); - - x = MIN(x, _hostIcon.frame.origin.x); - x = MIN(x, _hostLabel.frame.origin.x); - - y = MIN(y, _hostIcon.frame.origin.y); - y = MIN(y, _hostLabel.frame.origin.y); - - width = MAX(width, _hostIcon.frame.size.width); - width = MAX(width, _hostLabel.frame.size.width); - - height = _hostIcon.frame.size.height + - _hostLabel.frame.size.height + - LABEL_DY / 2; - - self.bounds = CGRectMake(x - ITEM_PADDING, y - ITEM_PADDING, width + 2 * ITEM_PADDING, height + 2 * ITEM_PADDING); -} - -- (void) updateContentsForHost:(TemporaryHost*)host { - _hostLabel.text = _host.name; - [_hostLabel sizeToFit]; - - if (host.state == StateOnline) { - [_hostSpinner stopAnimating]; - - if (host.pairState == PairStateUnpaired) { - [_hostOverlay setImage:[UIImage imageNamed:@"LockedOverlayIcon"]]; - } - else { - [_hostOverlay setImage:nil]; - } - } - else if (host.state == StateOffline) { - [_hostSpinner stopAnimating]; - [_hostOverlay setImage:[UIImage imageNamed:@"ErrorOverlayIcon"]]; - } - else { - [_hostSpinner startAnimating]; - } - - [self updateBounds]; -} - -- (void) updateLoop { - // Stop immediately if the view has been detached - if (self.superview == nil) { - return; - } - - [self updateContentsForHost:_host]; - - // Queue the next refresh cycle - [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; -} - -- (void) hostLongClicked:(UILongPressGestureRecognizer*)gesture { - if (gesture.state == UIGestureRecognizerStateBegan) { - [_callback hostCardLongPressed:_host view:self]; - } -} - -#if !TARGET_OS_TV -- (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction - configurationForMenuAtLocation:(CGPoint)location { - // We don't want to trigger the primary action at this point, so cancel - // tracking touch on this view now. This will also have the (intended) - // effect of removing the touch highlight on this view. - [self cancelTrackingWithEvent:nil]; - - [_callback hostCardLongPressed:_host view:self]; - return nil; -} -#endif - -- (void) hostClicked { - [_callback noneUserInitiatedHostAction:_host view:self]; -} - -- (void) addClicked { - [_callback addHostTapped]; -} - -@end diff --git a/VoidLink/ViewControllers/MainFrameViewController.h b/VoidLink/ViewControllers/MainFrameViewController.h index ebc9b42e..8d978cd8 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.h +++ b/VoidLink/ViewControllers/MainFrameViewController.h @@ -13,7 +13,6 @@ #import "DiscoveryManager.h" #import "PairManager.h" #import "StreamConfiguration.h" -#import "UIComputerView.h" #import "HostCardView.h" #import "UIAppView.h" #import "AppAssetManager.h" @@ -21,7 +20,7 @@ #import "HostCollectionViewController.h" -@interface MainFrameViewController : UICollectionViewController +@interface MainFrameViewController : UICollectionViewController @property (nonatomic, strong) IBOutlet UIBarButtonItem *settingsButton; #if !TARGET_OS_TV diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 84c53759..51cf0ebb 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -16,7 +16,6 @@ #import "Connection.h" #import "StreamManager.h" #import "Utils.h" -#import "UIComputerView.h" #import "UIAppView.h" #import "DataManager.h" #import "TemporarySettings.h" @@ -25,7 +24,6 @@ #import "ServerInfoResponse.h" #import "StreamFrameViewController.h" #import "LoadingFrameViewController.h" -#import "ComputerScrollView.h" #import "TemporaryApp.h" #import "IdManager.h" #import "ConnectionHelper.h" From 02c6a12a821dae55ac466a9b0c5abd585d4ef566 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 15:48:55 +0800 Subject: [PATCH 07/18] - aboutView and tip url fixes for iOS 13 - final fix: osw profile saving issue - replace document link for add widget action --- VoidLink/AboutView.swift | 10 ++-- VoidLink/Localization/Localizable.xcstrings | 58 +++++++++---------- .../LayoutOnScreenControlsViewController.m | 25 ++++---- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/VoidLink/AboutView.swift b/VoidLink/AboutView.swift index d1d2d443..eabd0755 100644 --- a/VoidLink/AboutView.swift +++ b/VoidLink/AboutView.swift @@ -56,12 +56,12 @@ public struct AboutView: View { } } // 链接按钮 - if #available(iOS 14.0, *) { + if #available(iOS 14.0, *),false { Link(SwiftLocalizationHelper.localizedString(forKey: "Join us"), destination: URL(string: SwiftLocalizationHelper.localizedString(forKey: "supportLink"))!) .padding(.top, 10) Spacer() // OK 按钮 - Button("OK") { + Button(SwiftLocalizationHelper.localizedString(forKey: "OK")) { presentationMode.wrappedValue.dismiss() } .padding() @@ -84,8 +84,8 @@ public struct AboutView: View { .cornerRadius(12) .frame(height: 33) .frame(minWidth: 100) - - Button("OK") { + /* + Button(SwiftLocalizationHelper.localizedString(forKey: "OK")) { presentationMode.wrappedValue.dismiss() } .padding() @@ -93,7 +93,7 @@ public struct AboutView: View { .foregroundColor(.white) .cornerRadius(12) .frame(height: 33) - .frame(minWidth: 100) + .frame(minWidth: 100)*/ } .padding(.top, 10) } diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index d763de87..273ba860 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -1457,25 +1457,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" } }, "zh-Hant" : { "stringUnit" : { - "state" : "needs_review", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#" + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" } }, "zh-HK" : { "stringUnit" : { - "state" : "needs_review", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#" + "state" : "translated", + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" } } } @@ -1705,25 +1705,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bu3hki4pezy93g6r?singleDoc# " + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bu3hki4pezy93g6r?singleDoc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc" } } } @@ -2022,25 +2022,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc" } } } @@ -2454,25 +2454,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zdf5l29s3vohs6iq?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zdf5l29s3vohs6iq?singleDoc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc" } } } @@ -3065,19 +3065,19 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc# " + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc# " + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc# " + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc" } } } @@ -4240,25 +4240,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/rgc9if4xrf352555?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/rgc9if4xrf352555?singleDoc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc#" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc" } } } @@ -4344,25 +4344,25 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/dhmg3h27ls8mwt4a?singleDoc# 《About VoidLink》" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/dhmg3h27ls8mwt4a?singleDoc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc# 《关于 VoidLink》" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc# 《关于 VoidLink》" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc# 《关于 VoidLink》" + "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc" } } } diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index fd720ff9..2746a3f2 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -238,12 +238,13 @@ object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(OSCLayoutChanged) name:@"OSCLayoutChanged" object:nil]; // used to notifiy this view controller that the user made a change to the OSC layout so that the VC can either fade in or out its 'Undo button' which will signify to the user whether there are any OSC layout changes to undo + + [[NSNotificationCenter defaultCenter] addObserver: self + selector: @selector(handleReturnToForeground) + name: UIApplicationDidBecomeActiveNotification + object: nil]; + - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillResignActive:) - name:UIApplicationWillResignActiveNotification - object:nil]; - OnScreenWidgetView.editMode = true; [self handleMissingToolBarIcon:toolbarRootView]; [self profileRefresh]; @@ -251,11 +252,10 @@ #pragma mark - Class Helper Functions -- (void)applicationWillResignActive:(NSNotification *)notification { - [self saveTapped:nil]; +- (void)handleReturnToForeground { + [OSCProfilesManager setOnScreenWidgetViewsSet:self.OnScreenWidgetViews]; // pass the keyboard button dict to profiles manager } - /* fades the 'Undo Button' in or out depending on whether the user has any OSC layout changes to undo */ - (void) OSCLayoutChanged { if ([self.layoutOSC.layoutChanges count] > 0) { @@ -379,8 +379,8 @@ UIAlertAction *readInstruction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Read Widget Instruction"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){ - [self saveTapped:nil]; - NSURL *url = [NSURL URLWithString:@"https://b23.tv/J8qEXOr"]; + //[self saveTapped:nil]; + NSURL *url = [NSURL URLWithString:[LocalizationHelper localizedStringForKey:@"onScreenWidgetStackDoc"]]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; } @@ -435,7 +435,8 @@ UIAlertAction *readInstruction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Read Widget Instruction"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){ - NSURL *url = [NSURL URLWithString:@"https://b23.tv/J8qEXOr"]; + //[self saveTapped:nil]; + NSURL *url = [NSURL URLWithString:[LocalizationHelper localizedStringForKey:@"onScreenWidgetStackDoc"]]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; } @@ -503,7 +504,7 @@ textField.spellCheckingType = UITextSpellCheckingTypeNo; textField.text = self->selectedWidgetView.shape; if([self->selectedWidgetView.shape isEqualToString: @"largeSquare"]) textField.enabled = false; - }]; + }]; UIAlertAction *createNewAction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Create New"] From ab7ccc7732abc4d73af1d44f570367aafc8370e9 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 19:56:46 +0800 Subject: [PATCH 08/18] fix: profile saving issue caused by launching osw tool via tap gesture --- VoidLink/Input/CommandManager.swift | 4 +-- VoidLink/Input/OnScreenControls.m | 4 +-- VoidLink/Input/StreamView.h | 2 +- VoidLink/Input/StreamView.m | 4 +-- .../LayoutOnScreenControlsViewController.h | 2 +- .../LayoutOnScreenControlsViewController.m | 20 +++++------ .../StreamFrameViewController.m | 36 ++++++++++++------- 7 files changed, 41 insertions(+), 31 deletions(-) diff --git a/VoidLink/Input/CommandManager.swift b/VoidLink/Input/CommandManager.swift index 5a2c239d..d07cc750 100644 --- a/VoidLink/Input/CommandManager.swift +++ b/VoidLink/Input/CommandManager.swift @@ -417,7 +417,7 @@ import UIKit print("No match found for input: \(input)") return nil } - print("Regex matched for input: \(input)") + // print("Regex matched for input: \(input)") let matchedString = (input as NSString).substring(with: match.range(at: 0)) let keyStrings = matchedString.split(separator: "+").map { String($0) } @@ -475,7 +475,7 @@ import UIKit print("No match found for input: \(input)") return nil } - print("Regex matched for input: \(input)") + // print("Regex matched for input: \(input)") let matchedString = (input as NSString).substring(with: match.range(at: 0)) let cmdStrings = matchedString.split(separator: "-").map { String($0) } diff --git a/VoidLink/Input/OnScreenControls.m b/VoidLink/Input/OnScreenControls.m index 13b585ba..924420c6 100644 --- a/VoidLink/Input/OnScreenControls.m +++ b/VoidLink/Input/OnScreenControls.m @@ -343,7 +343,7 @@ static float L3_Y; if(position.x < 1.0 && position.y < 1.0) { position.x = position.x * _view.bounds.size.width; position.y = position.y * _view.bounds.size.height; - NSLog(@"denormalizing position: %f, %f", position.x, position.y); + // NSLog(@"denormalizing position: %f, %f", position.x, position.y); } return position; } @@ -1930,7 +1930,7 @@ static float L3_Y; if(alpha < 0.23) targetAlpha = 0.23; if(alpha == 0.0 || alpha == 1.0) targetAlpha = 5.0f/6.0f; // invalid alpha value - NSLog(@"alphas: %f",targetAlpha); + // NSLog(@"alphas: %f",targetAlpha); if (layer == self._aButton || layer == self._bButton || diff --git a/VoidLink/Input/StreamView.h b/VoidLink/Input/StreamView.h index 1fff07fc..8aea19c1 100644 --- a/VoidLink/Input/StreamView.h +++ b/VoidLink/Input/StreamView.h @@ -50,7 +50,7 @@ andConfig:(StreamConfiguration*)streamConfig; - (void) reloadOnScreenControlsWith:(ControllerSupport*)controllerSupport andConfig:(StreamConfiguration*)streamConfig; -- (void) clearOnScreenKeyboardButtons; +- (void) clearOnScreenWidgets; - (void) reloadOnScreenWidgetViews; - (CGSize) getVideoAreaSize; diff --git a/VoidLink/Input/StreamView.m b/VoidLink/Input/StreamView.m index 29e8636d..8f838f8f 100644 --- a/VoidLink/Input/StreamView.m +++ b/VoidLink/Input/StreamView.m @@ -399,7 +399,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; } -- (void) clearOnScreenKeyboardButtons{ +- (void) clearOnScreenWidgets{ for (UIView *subview in self->streamFrameTopLayerView.subviews) { // 检查子视图是否是特定类型的实例 if ([subview isKindOfClass:[OnScreenWidgetView class]]) { @@ -422,7 +422,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; // NSLog(@"reload on screen keyboard buttons here"); // remove all keyboard widget views first - [self clearOnScreenKeyboardButtons]; + [self clearOnScreenWidgets]; // bool customOscEnabled = [self isOscEnabled] && settings.onscreenControls.intValue == OnScreenControlsLevelCustom; diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h index 8215aaab..c2272de5 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.h @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)presentProfilesTableView; @property LayoutOnScreenControls *layoutOSC; // object that contains a view which contains the on screen controller buttons that allows the user to drag and positions each button on the screen using touch -@property (nonatomic) NSMutableSet* OnScreenWidgetViews; +@property (nonatomic) NSMutableSet* onScreenWidgetViews; @property int OSCSegmentSelected; @property (nonatomic, assign) bool quickSwitchEnabled; diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index 2746a3f2..2879fa9e 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -62,7 +62,7 @@ - (void) viewWillDisappear:(BOOL)animated{ OnScreenWidgetView.editMode = false; - for (OnScreenWidgetView* widgetView in self.OnScreenWidgetViews){ + for (OnScreenWidgetView* widgetView in self.onScreenWidgetViews){ [widgetView.stickBallLayer removeFromSuperlayer]; [widgetView.crossMarkLayer removeFromSuperlayer]; } @@ -89,7 +89,7 @@ } } - [self.OnScreenWidgetViews removeAllObjects]; + [self.onScreenWidgetViews removeAllObjects]; NSLog(@"reload os Key here"); @@ -120,7 +120,7 @@ [widgetView resizeWidgetView]; // resize must be called after relocation [widgetView adjustTransparencyWithAlpha:buttonState.backgroundAlpha]; [widgetView adjustBorderWithWidth:buttonState.borderWidth]; - [self.OnScreenWidgetViews addObject:widgetView]; + [self.onScreenWidgetViews addObject:widgetView]; } } } @@ -128,8 +128,8 @@ - (void) viewDidLoad { [super viewDidLoad]; profilesManager = [OSCProfilesManager sharedManager:self.view.bounds]; - self.OnScreenWidgetViews = [[NSMutableSet alloc] init]; // will be revised to read persisted data , somewhere else - [OSCProfilesManager setOnScreenWidgetViewsSet:self.OnScreenWidgetViews]; // pass the keyboard button dict to profiles manager + self.onScreenWidgetViews = [[NSMutableSet alloc] init]; // will be revised to read persisted data , somewhere else + [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager //isToolbarHidden = NO; // keeps track if the toolbar is hidden up above the screen so that we know whether to hide or show it when the user taps the toolbar's hide/show button _quickSwitchEnabled = false; @@ -253,7 +253,7 @@ #pragma mark - Class Helper Functions - (void)handleReturnToForeground { - [OSCProfilesManager setOnScreenWidgetViewsSet:self.OnScreenWidgetViews]; // pass the keyboard button dict to profiles manager + [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager } /* fades the 'Undo Button' in or out depending on whether the user has any OSC layout changes to undo */ @@ -588,10 +588,10 @@ [newWidget resizeWidgetView]; // resize must be called after relocation [newWidget adjustTransparencyWithAlpha:widget.backgroundAlpha]; [newWidget adjustBorderWithWidth:widget.borderWidth]; - [self.OnScreenWidgetViews addObject:newWidget]; + [self.onScreenWidgetViews addObject:newWidget]; self->selectedWidgetView = newWidget; if(!createNew){ - [self.OnScreenWidgetViews removeObject:widget]; + [self.onScreenWidgetViews removeObject:widget]; [widget removeFromSuperview]; } } @@ -604,7 +604,7 @@ widgetView.guidelineDelegate = (id)self; widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place widgetView.minStickOffset = [widgetInitParams[@"minStickOffsetString"] floatValue]; - [self.OnScreenWidgetViews addObject:widgetView]; + [self.onScreenWidgetViews addObject:widgetView]; // Add the widgetView to the view controller's view [self.view insertSubview:widgetView belowSubview:self.widgetPanelStack]; [widgetView setLocationWithPosition:CGPointMake(90, 130)]; @@ -1253,7 +1253,7 @@ if(!isToolbarHidden && self->selectedWidgetView != nil && [self layerIsOverlappingWithTrashcanButton:selectedWidgetView.layer]){ [self->selectedWidgetView removeFromSuperview]; - [self.OnScreenWidgetViews removeObject:self->selectedWidgetView]; + [self.onScreenWidgetViews removeObject:self->selectedWidgetView]; [self clearStickIndicator]; [selectedWidgetView.buttonDownVisualEffectLayer removeFromSuperlayer]; } diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 4c3ecca4..b3e665cd 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -218,15 +218,8 @@ } - (void)configOscLayoutTool{ + if([self isOscLayoutToolEnabled]){ - _oscLayoutTapRecoginizer = [[CustomTapGestureRecognizer alloc] initWithTarget:self action:@selector(openWidgetLayoutTool)]; - _oscLayoutTapRecoginizer.numberOfTouchesRequired = _settings.oscLayoutToolFingers.intValue; //tap a predefined number of fingers to open osc layout tool - _oscLayoutTapRecoginizer.tapDownTimeThreshold = 0.2; - _oscLayoutTapRecoginizer.delaysTouchesBegan = NO; - _oscLayoutTapRecoginizer.delaysTouchesEnded = NO; - if(_settings.touchMode.intValue == AbsoluteTouch) _oscLayoutTapRecoginizer.immediateTriggering = true; // make immediate triggering on for absolute touch mode - - [self.view addGestureRecognizer:_oscLayoutTapRecoginizer]; // /* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */ // _layoutOnScreenControlsVC = [[LayoutOnScreenControlsViewController alloc] init]; BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone); @@ -242,20 +235,22 @@ _layoutOnScreenControlsVC.view.backgroundColor = UIColor.clearColor; _layoutOnScreenControlsVC.modalPresentationStyle = UIModalPresentationOverCurrentContext; } + //NSLog(@"in osc frameview gestures: %d", (uint32_t)[self.view.gestureRecognizers count]); + //NSLog(@"in osc streamview gestures: %d", (uint32_t)[_streamView.gestureRecognizers count]); } - (void)presentToolboxViewController{ + [self configOscLayoutTool]; ToolboxViewController* oldToolboxVC = toolBoxViewController; toolBoxViewController = [[ToolboxViewController alloc] init]; toolBoxViewController.specialEntryDelegate = self; toolBoxViewController.specialEntries = oldToolboxVC.specialEntries; - [self configOscLayoutTool]; [self presentViewController:toolBoxViewController animated:YES completion:^{ //[self->toolBoxViewController setupConstraints]; }]; } -- (void)configSwipeGestures{ +- (void)configGestures{ _slideToSettingsRecognizer = [[CustomEdgeSlideGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)]; _slideToSettingsRecognizer.edges = _settings.slideToSettingsScreenEdge.intValue; _slideToSettingsRecognizer.normalizedThresholdDistance = _settings.slideToSettingsDistance.floatValue; @@ -271,6 +266,17 @@ _slideToCmdToolRecognizer.delaysTouchesBegan = NO; _slideToCmdToolRecognizer.delaysTouchesEnded = NO; [self.view addGestureRecognizer:_slideToCmdToolRecognizer]; + + if([self isOscLayoutToolEnabled]){ + _oscLayoutTapRecoginizer = [[CustomTapGestureRecognizer alloc] initWithTarget:self action:@selector(handleWidgetLayoutGesture)]; + _oscLayoutTapRecoginizer.numberOfTouchesRequired = _settings.oscLayoutToolFingers.intValue; //tap a predefined number of fingers to open osc layout tool + _oscLayoutTapRecoginizer.tapDownTimeThreshold = 0.2; + _oscLayoutTapRecoginizer.delaysTouchesBegan = NO; + _oscLayoutTapRecoginizer.delaysTouchesEnded = NO; + if(_settings.touchMode.intValue == AbsoluteTouch) _oscLayoutTapRecoginizer.immediateTriggering = true; // make immediate triggering on for absolute touch mode + [self.view addGestureRecognizer:_oscLayoutTapRecoginizer]; // + } + } - (void)configZoomGestureAndAddStreamView{ @@ -310,7 +316,7 @@ overlayLevel = _settings.statsOverlayLevel.intValue; [self configOscLayoutTool]; [self updateToolboxSpecialEntries]; - [self configSwipeGestures]; + [self configGestures]; [self configZoomGestureAndAddStreamView]; [self->_streamView disableOnScreenControls]; //don't know why but this must be called outside the streamview class, just put it here. execute in streamview class cause hang [self.mainFrameViewcontroller reloadStreamConfig]; // reload streamconfig @@ -628,10 +634,14 @@ [_streamView keyboardWillHide]; } +- (void)handleWidgetLayoutGesture{ + [self configOscLayoutTool]; + [self openWidgetLayoutTool]; +} - (void)openWidgetLayoutTool{ [self->_streamView disableOnScreenControls]; - [self->_streamView clearOnScreenKeyboardButtons]; // clear all onScreenKeyboardButtons before entering edit mode + [self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode _layoutOnScreenControlsVC.quickSwitchEnabled = false; _layoutOnScreenControlsVC.toolbarStackView.hidden = false; _layoutOnScreenControlsVC.toolbarRootView.hidden = false; @@ -640,7 +650,7 @@ - (void)switchWidgetProfile{ [self->_streamView disableOnScreenControls]; - [self->_streamView clearOnScreenKeyboardButtons]; // clear all onScreenKeyboardButtons before entering edit mode + [self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode _layoutOnScreenControlsVC.quickSwitchEnabled = true; _layoutOnScreenControlsVC.toolbarStackView.hidden = true; _layoutOnScreenControlsVC.toolbarRootView.hidden = true; From 0df5135f0bc0190a610c207021c253aefb80f28a Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Fri, 25 Jul 2025 20:18:43 +0800 Subject: [PATCH 09/18] fix: widgets disappears after display orientation change & saving profile in osw tool --- .../LayoutOnScreenControlsViewController.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index 2879fa9e..db269384 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -243,7 +243,10 @@ selector: @selector(handleReturnToForeground) name: UIApplicationDidBecomeActiveNotification object: nil]; - + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deviceOrientationDidChange) // handle orientation change since i made portrait mode available + name:UIDeviceOrientationDidChangeNotification + object:nil]; OnScreenWidgetView.editMode = true; [self handleMissingToolBarIcon:toolbarRootView]; @@ -256,6 +259,13 @@ [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager } +- (void)deviceOrientationDidChange{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + sleep(1); + [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager + }); +} + /* fades the 'Undo Button' in or out depending on whether the user has any OSC layout changes to undo */ - (void) OSCLayoutChanged { if ([self.layoutOSC.layoutChanges count] > 0) { From e301f2af16c8ca3ef71722f0ce6b67cf2c147c0e Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 26 Jul 2025 01:18:48 +0800 Subject: [PATCH 10/18] full portrait support for on-screen widgets & legacy on-screen controls --- VoidLink/Input/StreamView.h | 1 + VoidLink/Input/StreamView.m | 9 ++-- .../LayoutOnScreenControlsViewController.m | 45 +++++++++++-------- .../OSCProfilesTableViewController.h | 2 +- .../OSCProfilesTableViewController.m | 2 +- .../StreamFrameViewController.m | 3 ++ 6 files changed, 38 insertions(+), 24 deletions(-) diff --git a/VoidLink/Input/StreamView.h b/VoidLink/Input/StreamView.h index 8aea19c1..a65e866f 100644 --- a/VoidLink/Input/StreamView.h +++ b/VoidLink/Input/StreamView.h @@ -36,6 +36,7 @@ @property (assign, nonatomic) UIView* streamFrameTopLayerView; @property (assign, nonatomic) CGFloat streamAspectRatio; @property (assign, nonatomic) CGRect originalFrame; +@property (assign, nonatomic) bool widgetToolOpened; - (void) setupStreamView:(ControllerSupport*)controllerSupport diff --git a/VoidLink/Input/StreamView.m b/VoidLink/Input/StreamView.m index 8f838f8f..92ae7144 100644 --- a/VoidLink/Input/StreamView.m +++ b/VoidLink/Input/StreamView.m @@ -350,7 +350,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; - (void) showOnScreenControls { #if !TARGET_OS_TV - [onScreenControls show]; + if(!_widgetToolOpened) [onScreenControls show]; #endif } @@ -411,8 +411,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; - (CGPoint)denormalizeWidgetPosition:(CGPoint)position { if(position.x < 1.0 && position.y < 1.0){ - position.x = position.x * self.bounds.size.width; - position.y = position.y * self.bounds.size.height; + position.x = position.x * streamFrameTopLayerView.bounds.size.width; + position.y = position.y * streamFrameTopLayerView.bounds.size.height; + } + else{ + NSLog(@"invalid coords"); } return position; } diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index db269384..ef316ce5 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -78,7 +78,14 @@ return position; } -- (void) reloadOnScreenWidgetViews { +- (void)reloadLegacyOnScreenControls{ + [self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the o//ne the user selected on the previous screen + [self addInnerAnalogSticksToOuterAnalogLayers]; + [self.layoutOSC.layoutChanges removeAllObjects]; // since a new OSC profile is being loaded, this will remove all previous layout changes made from the array + [self OSCLayoutChanged]; // fades the 'Undo Button' out +} + +- (void)reloadOnScreenWidgetViews{ NSLog(@"reloadOnScreenWidgets %f", CACurrentMediaTime()); OnScreenWidgetView.editMode = true; [self clearStickIndicator]; @@ -155,8 +162,9 @@ self.layoutOSC = [[LayoutOnScreenControls alloc] initWithView:self.view controllerSup:nil streamConfig:nil oscLevel:OSCSegmentSelected]; self.layoutOSC._level = OnScreenControlsLevelCustom; self.layoutOSC.layoutToolVC = self; + //[self.layoutOSC show]; // draw on screen controls [self.layoutOSC show]; // draw on screen controls - + [self addInnerAnalogSticksToOuterAnalogLayers]; // allows inner and analog sticks to be dragged together around the screen together as one unit which is the expected behavior self.undoButton.alpha = 0.3; // no changes to undo yet, so fade out the undo button a bit @@ -259,11 +267,19 @@ [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager } +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator{ + [self saveTapped:nil]; +} + - (void)deviceOrientationDidChange{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - sleep(1); - [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager - }); + [self performSelector:@selector(handleOrientationChangeForOnScreenWidgets) withObject:self afterDelay:1]; +} + +- (void)handleOrientationChangeForOnScreenWidgets{ + _oscProfilesTableViewController.layoutViewBounds = self.view.bounds; + [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager + [self reloadOnScreenWidgetViews]; + [self reloadLegacyOnScreenControls]; } /* fades the 'Undo Button' in or out depending on whether the user has any OSC layout changes to undo */ @@ -1116,12 +1132,9 @@ //this part is just for registration, will not be immediately executed. self->_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position - [self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the one the user selected on the previous screen - [self addInnerAnalogSticksToOuterAnalogLayers]; - [self.layoutOSC.layoutChanges removeAllObjects]; // since a new OSC profile is being loaded, this will remove all previous layout changes made from the array - [self OSCLayoutChanged]; // fades the 'Undo Button' out + NSLog(@"profile profile"); + [self reloadLegacyOnScreenControls]; self->_oscProfilesTableViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayers; //pass updated OSCLayout to OSCProfileTableView again - //[self reloadOnScreenKeyboardButtons]; }; [self.oscProfilesTableViewController profileViewRefresh]; // execute this will make sure OSCLayout is updated from persisted profile, not any cache. @@ -1144,16 +1157,10 @@ } _oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"]; - _oscProfilesTableViewController.streamViewBounds = self.view.bounds; + _oscProfilesTableViewController.layoutViewBounds = self.view.bounds; _oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC ofile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the proffile and then hide/show and move each OSC button to their appropriate position - [self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the one the user selected on the previous screen - - [self addInnerAnalogSticksToOuterAnalogLayers]; - - [self.layoutOSC.layoutChanges removeAllObjects]; // since a new OSC profile is being loaded, this will remove all previous layout changes made from the array - - [self OSCLayoutChanged]; // fades the 'Undo Button' out + [self reloadLegacyOnScreenControls]; }; self.widgetPanelStack.hidden = YES; diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/OSCProfilesTableViewController.h b/VoidLink/ViewControllers/CustomOSCViewControl/OSCProfilesTableViewController.h index c7a3735e..ebd90c87 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/OSCProfilesTableViewController.h +++ b/VoidLink/ViewControllers/CustomOSCViewControl/OSCProfilesTableViewController.h @@ -30,7 +30,7 @@ typedef NS_ENUM(NSUInteger, FileOperation) { @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (nonatomic, copy) void (^needToUpdateOscLayoutTVC)(void); @property (nonatomic, assign) NSMutableArray *currentOSCButtonLayers; -@property (nonatomic, assign) CGRect streamViewBounds; +@property (nonatomic, assign) CGRect layoutViewBounds; @property (weak, nonatomic) IBOutlet UINavigationBar *profileTableViewNavigationBar; @end diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/OSCProfilesTableViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/OSCProfilesTableViewController.m index cde34b02..06546075 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/OSCProfilesTableViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/OSCProfilesTableViewController.m @@ -74,7 +74,7 @@ const double NAV_BAR_HEIGHT = 50; // self.profileTableViewNavigationBar.layer.cornerRadius = 15; // 设置圆角半径 - profilesManager = [OSCProfilesManager sharedManager:_streamViewBounds]; + profilesManager = [OSCProfilesManager sharedManager:_layoutViewBounds]; self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, NAV_BAR_HEIGHT)]; diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index b3e665cd..33139121 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -640,6 +640,7 @@ } - (void)openWidgetLayoutTool{ + _streamView.widgetToolOpened = true; [self->_streamView disableOnScreenControls]; [self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode _layoutOnScreenControlsVC.quickSwitchEnabled = false; @@ -649,6 +650,7 @@ } - (void)switchWidgetProfile{ + _streamView.widgetToolOpened = true; [self->_streamView disableOnScreenControls]; [self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode _layoutOnScreenControlsVC.quickSwitchEnabled = true; @@ -669,6 +671,7 @@ - (void)oscLayoutClosed{ // Handle the callback + _streamView.widgetToolOpened = false; [self->_streamView disableOnScreenControls]; // add this to get realtime back menu working. [self->_streamView reloadOnScreenControlsWith:(ControllerSupport*)_controllerSupport andConfig:(StreamConfiguration*)_streamConfig]; From 49417e7bc921bfe181f36e3415a2244c1a84e80d Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 26 Jul 2025 01:32:12 +0800 Subject: [PATCH 11/18] - aboutView fix --- VoidLink/AboutView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VoidLink/AboutView.swift b/VoidLink/AboutView.swift index eabd0755..2a6cd445 100644 --- a/VoidLink/AboutView.swift +++ b/VoidLink/AboutView.swift @@ -56,7 +56,7 @@ public struct AboutView: View { } } // 链接按钮 - if #available(iOS 14.0, *),false { + if #available(iOS 14.0, *) { Link(SwiftLocalizationHelper.localizedString(forKey: "Join us"), destination: URL(string: SwiftLocalizationHelper.localizedString(forKey: "supportLink"))!) .padding(.top, 10) Spacer() From ee4c1defa072c1287850e1afd5228304814c573b Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 26 Jul 2025 10:11:18 +0800 Subject: [PATCH 12/18] fix: having no touch response under default osw profile. --- VoidLink/Input/OnScreenControls.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VoidLink/Input/OnScreenControls.m b/VoidLink/Input/OnScreenControls.m index 924420c6..8ef5c01b 100644 --- a/VoidLink/Input/OnScreenControls.m +++ b/VoidLink/Input/OnScreenControls.m @@ -1560,7 +1560,7 @@ static float L3_Y; // on screen at the time // DZ capturing shall be refactored in CustomOSC Mode & non-default profile condition - if(self._level == OnScreenControlsLevelCustom && [profilesManager getIndexOfSelectedProfile] != 0){ + if(self._level == OnScreenControlsLevelCustom){ CGPoint touchLocation = [touch locationInView:_view]; for (NSString *key in _activeCustomOscButtonPositionDict) { NSValue *value = _activeCustomOscButtonPositionDict[key]; From d2e0cc779cba8ab45d2c3f7005d8cfac0f2536ae Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 26 Jul 2025 11:42:46 +0800 Subject: [PATCH 13/18] fix: abnormal native/absolute touch after orientation change & expanding setting menu --- VoidLink/ViewControllers/SettingsViewController.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 88a5fee8..042ca92f 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -285,6 +285,8 @@ BOOL isCustomResolution(CGSize res) { } - (void)updateResolutionTable{ + if(self.mainFrameViewController.settingsExpandedInStreamView) return; + UIWindow *window = self.view.window; NSLog(@" window %@", window); From 6a3e1d8a88a00a37bc55b00e66d925a85106cee8 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 26 Jul 2025 13:55:37 +0800 Subject: [PATCH 14/18] - revert to blacklist edge detection for native touch --- VoidLink/Input/NativeTouchHandler.m | 26 ++++++++++-------------- VoidLink/Input/PureNativeTouchHandler.m | 27 +++++++++++-------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/VoidLink/Input/NativeTouchHandler.m b/VoidLink/Input/NativeTouchHandler.m index b5871fb5..95f90a0e 100644 --- a/VoidLink/Input/NativeTouchHandler.m +++ b/VoidLink/Input/NativeTouchHandler.m @@ -35,7 +35,6 @@ NSMutableSet *pointerIdPool; //pre-defined pool of pointerIds. NSMutableSet *unassignedPointerIds; NSMutableSet *blacklistedTouches; - bool doNotBlockTouchEvents; NSMutableDictionary *pointerObjDict; @@ -61,7 +60,6 @@ } self->activePointerIds = [NSMutableSet set]; self->blacklistedTouches = [NSMutableSet set]; - doNotBlockTouchEvents = true; self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff; @@ -160,15 +158,14 @@ NSNumber* touchAddrObj = @((uintptr_t)touch); unassignedPointerIds = [pointerIdPool mutableCopy]; //reset unassignedPointerIds [unassignedPointerIds minusSet:activePointerIds]; - uint8_t pointerId = [[unassignedPointerIds anyObject] unsignedIntValue]; - [pointerIdDict setObject:@(pointerId) forKey:touchAddrObj]; - [activePointerIds addObject:@(pointerId)]; + NSNumber* pointerIdObj = @([[unassignedPointerIds anyObject] unsignedIntValue]); + [pointerIdDict setObject:pointerIdObj forKey:touchAddrObj]; + [activePointerIds addObject:pointerIdObj]; //check if touch point is spawned on the left or right upper half screen edges, event to remote PC. this is for better handling in-stream slide gesture CGPoint initialPoint = [touch locationInView:self->streamView]; if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) { - doNotBlockTouchEvents = false; - // [blacklistedTouches addObject:touchAddrObj]; + [blacklistedTouches addObject:touchAddrObj]; } } @@ -192,8 +189,8 @@ - (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{ //if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash - // if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return; - + if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return; + CGPoint targetCoords; //NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved); // NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]); @@ -239,7 +236,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; if(self->activateCoordSelector) [self updatePointerObjInDict:touch]; - if(self->doNotBlockTouchEvents) [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE]; + [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE]; [[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service) usleep(self->touchMoveEventIntervalUs); } @@ -265,9 +262,8 @@ [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)]; + [self->blacklistedTouches removeObject:@((uintptr_t)touch)]; } - self->doNotBlockTouchEvents = true; //if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) }); else{ @@ -297,10 +293,10 @@ } - (void)removePointerObjFromDict:(UITouch*)touch{ - uintptr_t memAddrValue = (uintptr_t)touch; - NativeTouchPointer* pointer = [pointerObjDict objectForKey:@(memAddrValue)]; + NSNumber* touchAddrObj = @((uintptr_t)touch); + NativeTouchPointer* pointer = [pointerObjDict objectForKey:touchAddrObj]; if(pointer != nil){ - [pointerObjDict removeObjectForKey:@(memAddrValue)]; + [pointerObjDict removeObjectForKey:touchAddrObj]; } } diff --git a/VoidLink/Input/PureNativeTouchHandler.m b/VoidLink/Input/PureNativeTouchHandler.m index 27c58962..988d8870 100644 --- a/VoidLink/Input/PureNativeTouchHandler.m +++ b/VoidLink/Input/PureNativeTouchHandler.m @@ -34,7 +34,6 @@ NSMutableSet *pointerIdPool; //pre-defined pool of pointerIds. NSMutableSet *unassignedPointerIds; NSMutableSet *blacklistedTouches; - bool doNotBlockTouchEvents; NSMutableDictionary *pointerObjDict; @@ -60,7 +59,6 @@ } self->activePointerIds = [NSMutableSet set]; self->blacklistedTouches = [NSMutableSet set]; - doNotBlockTouchEvents = true; self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff; @@ -159,15 +157,14 @@ NSNumber* touchAddrObj = @((uintptr_t)touch); unassignedPointerIds = [pointerIdPool mutableCopy]; //reset unassignedPointerIds [unassignedPointerIds minusSet:activePointerIds]; - uint8_t pointerId = [[unassignedPointerIds anyObject] unsignedIntValue]; - [pointerIdDict setObject:@(pointerId) forKey:touchAddrObj]; - [activePointerIds addObject:@(pointerId)]; + NSNumber* pointerIdObj = @([[unassignedPointerIds anyObject] unsignedIntValue]); + [pointerIdDict setObject:pointerIdObj forKey:touchAddrObj]; + [activePointerIds addObject:pointerIdObj]; //check if touch point is spawned on the left or right upper half screen edges, event to remote PC. this is for better handling in-stream slide gesture CGPoint initialPoint = [touch locationInView:self->streamView]; if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) { - doNotBlockTouchEvents = false; - // [blacklistedTouches addObject:touchAddrObj]; + [blacklistedTouches addObject:touchAddrObj]; } } @@ -191,8 +188,8 @@ - (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{ //if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash - // if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return; - + if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return; + CGPoint targetCoords; //NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved); // NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]); @@ -235,7 +232,7 @@ for (UITouch* touch in touches){ // continue to the next loop if current touch is already captured by OSC. works only in regular native touch if(self->activateCoordSelector) [self updatePointerObjInDict:touch]; - if(self->doNotBlockTouchEvents) [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE]; + [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE]; [[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service) usleep(self->touchMoveEventIntervalUs); } @@ -259,9 +256,8 @@ [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)]; + [self->blacklistedTouches removeObject:@((uintptr_t)touch)]; } - self->doNotBlockTouchEvents = true; //if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) }); else{ @@ -290,10 +286,10 @@ } - (void)removePointerObjFromDict:(UITouch*)touch{ - uintptr_t memAddrValue = (uintptr_t)touch; - NativeTouchPointer* pointer = [pointerObjDict objectForKey:@(memAddrValue)]; + NSNumber* touchAddrObj = @((uintptr_t)touch); + NativeTouchPointer* pointer = [pointerObjDict objectForKey:touchAddrObj]; if(pointer != nil){ - [pointerObjDict removeObjectForKey:@(memAddrValue)]; + [pointerObjDict removeObjectForKey:touchAddrObj]; } } @@ -308,3 +304,4 @@ } @end + From dd9ad9728a918002b21423ec94a822917543df74 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 27 Jul 2025 00:08:10 +0800 Subject: [PATCH 15/18] fix: widget layout tool disfunction after display orientation change --- VoidLink/Input/CustomOSC/OSCProfilesManager.h | 6 ++-- VoidLink/Input/CustomOSC/OSCProfilesManager.m | 24 ++++++++++----- .../LayoutOnScreenControlsViewController.m | 17 ++++++++--- .../ViewControllers/SettingsViewController.m | 29 +++++++++---------- .../StreamFrameViewController.m | 6 +++- 5 files changed, 52 insertions(+), 30 deletions(-) diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.h b/VoidLink/Input/CustomOSC/OSCProfilesManager.h index 58cc6bf3..7feb0711 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.h +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.h @@ -22,8 +22,10 @@ NS_ASSUME_NONNULL_BEGIN @property NSMutableArray *currentProfiles; -+ (OSCProfilesManager *) sharedManager:(CGRect)viewBounds; -+ (void) setOnScreenWidgetViewsSet:(NSMutableSet* )set; ++ (OSCProfilesManager *)sharedManager:(CGRect)viewBounds; ++ (void)setOnScreenWidgetViewsSet:(NSMutableSet* )set; ++ (void)setLayoutViewBounds:(CGRect)bounds; + #pragma mark - Getters /** diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.m b/VoidLink/Input/CustomOSC/OSCProfilesManager.m index bc1e0141..53fe86c7 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.m +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.m @@ -18,15 +18,15 @@ @implementation OSCProfilesManager static NSMutableSet *OnScreenWidgetViews; -static CGRect streamViewBounds; +static CGRect layoutViewBounds; #pragma mark - Initializer + (OSCProfilesManager *) sharedManager:(CGRect)viewBounds { static OSCProfilesManager *_sharedManager = nil; static dispatch_once_t onceToken; - streamViewBounds = viewBounds; - NSLog(@"bounds width: %f, height: %f", streamViewBounds.size.width, streamViewBounds.size.height); + layoutViewBounds = viewBounds; + NSLog(@"bounds width: %f, height: %f", layoutViewBounds.size.width, layoutViewBounds.size.height); dispatch_once(&onceToken, ^{ _sharedManager = [[self alloc] init]; }); @@ -38,10 +38,14 @@ static CGRect streamViewBounds; #pragma mark - Class Helper Methods -+ (void) setOnScreenWidgetViewsSet:(NSMutableSet* )set{ ++ (void)setOnScreenWidgetViewsSet:(NSMutableSet* )set{ OnScreenWidgetViews = set; } ++ (void)setLayoutViewBounds:(CGRect)bounds{ + layoutViewBounds = bounds; +} + /** * Returns the profile whose 'name' property has a value that is equal to the 'name' passed into the method */ @@ -297,17 +301,21 @@ static CGRect streamViewBounds; } - (CGPoint)normalizeWidgetPosition:(CGPoint)position { + CGPoint newPosition = position; if(position.x > 1.0 && position.y >1.0){ - position.x = position.x / streamViewBounds.size.width; - position.y = position.y / streamViewBounds.size.height; + position.x = position.x / layoutViewBounds.size.width; + position.y = position.y / layoutViewBounds.size.height; } + // asdfsda; + NSLog(@"sef.view bounds: %f, %f", layoutViewBounds.size.width, layoutViewBounds.size.height); + NSLog(@"position: %f, %f, denormalized position: %f, %f", position.x, position.y, newPosition.x, newPosition.y); return position; } - (CGPoint)denormalizeWidgetPosition:(CGPoint)position { if(position.x < 1.0 && position.y < 1.0){ - position.x = position.x * streamViewBounds.size.width; - position.y = position.y * streamViewBounds.size.height; + position.x = position.x * layoutViewBounds.size.width; + position.y = position.y * layoutViewBounds.size.height; } return position; } diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index ef316ce5..4500bb38 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -31,6 +31,7 @@ CGRect controllerLoadedBounds; bool widgetViewSelected; bool controllerLayerSelected; + bool viewWillBeResized; __weak IBOutlet NSLayoutConstraint *toolbarTopConstraintiPhone; __weak IBOutlet NSLayoutConstraint *toolbarTopConstraintiPad; UIColor* trashCanStoryBoardColor; @@ -71,11 +72,13 @@ } - (CGPoint)denormalizeWidgetPosition:(CGPoint)position { + // NSLog(@"position: %f, %f", position.x, position.y); + CGPoint newPosition = position; if(position.x < 1.0 && position.y < 1.0){ - position.x = position.x * self.view.bounds.size.width; - position.y = position.y * self.view.bounds.size.height; + newPosition.x = position.x * self.view.bounds.size.width; + newPosition.y = position.y * self.view.bounds.size.height; } - return position; + return newPosition; } - (void)reloadLegacyOnScreenControls{ @@ -140,6 +143,7 @@ //isToolbarHidden = NO; // keeps track if the toolbar is hidden up above the screen so that we know whether to hide or show it when the user taps the toolbar's hide/show button _quickSwitchEnabled = false; + viewWillBeResized = false; /* add curve to bottom of chevron tab view */ UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.chevronView.bounds byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) cornerRadii:CGSizeMake(10.0, 10.0)]; @@ -268,14 +272,18 @@ } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator{ + viewWillBeResized = true; + //if(CGSizeEqualToSize(size, self.view.frame.size)) return; [self saveTapped:nil]; } - (void)deviceOrientationDidChange{ - [self performSelector:@selector(handleOrientationChangeForOnScreenWidgets) withObject:self afterDelay:1]; + [self performSelector:@selector(handleOrientationChangeForOnScreenWidgets) withObject:self afterDelay:0.05]; } - (void)handleOrientationChangeForOnScreenWidgets{ + if(!viewWillBeResized) return; + viewWillBeResized = false; _oscProfilesTableViewController.layoutViewBounds = self.view.bounds; [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager [self reloadOnScreenWidgetViews]; @@ -641,6 +649,7 @@ /* show pop up notification that lets users choose to save the current OSC layout configuration as a profile they can load when they want. User can also choose to cancel out of this pop up */ - (IBAction) saveTapped:(id)sender { + [OSCProfilesManager setLayoutViewBounds:self.view.bounds]; if([self->profilesManager updateSelectedProfile:self.layoutOSC.OSCButtonLayers]){ UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Current profile updated successfully"] preferredStyle:UIAlertControllerStyleAlert]; diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 042ca92f..c45fc929 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -1518,21 +1518,6 @@ BOOL isCustomResolution(CGSize res) { [self.enableOswForNativeTouchSwitch addTarget:self action:@selector(enableOswForNativeTouchSwitchFlipped:) forControlEvents:UIControlEventValueChanged]; [self enableOswForNativeTouchSwitchFlipped:self.enableOswForNativeTouchSwitch]; - - // init CustomOSC stuff - /* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */ - // self.layoutOnScreenControlsVC = [[LayoutOnScreenControlsViewController alloc] init]; - BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone); - if (isIPhone) { - UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPhone" bundle:nil]; - self.layoutOnScreenControlsVC = [storyboard instantiateViewControllerWithIdentifier:@"LayoutOnScreenControlsViewController"]; - } - else { - UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPad" bundle:nil]; - self.layoutOnScreenControlsVC = [storyboard instantiateViewControllerWithIdentifier:@"LayoutOnScreenControlsViewController"]; - self.layoutOnScreenControlsVC.modalPresentationStyle = UIModalPresentationFullScreen; - } - [self.externalDisplayModeSelector setSelectedSegmentIndex:currentSettings.externalDisplayMode.integerValue]; [self.localMousePointerModeSelector setSelectedSegmentIndex:currentSettings.localMousePointerMode.integerValue]; @@ -1638,6 +1623,20 @@ BOOL isCustomResolution(CGSize res) { } - (void)invokeOscLayout{ + // init CustomOSC stuff + /* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */ + // self.layoutOnScreenControlsVC = [[LayoutOnScreenControlsViewController alloc] init]; + BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone); + if (isIPhone) { + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPhone" bundle:nil]; + self.layoutOnScreenControlsVC = [storyboard instantiateViewControllerWithIdentifier:@"LayoutOnScreenControlsViewController"]; + } + else { + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPad" bundle:nil]; + self.layoutOnScreenControlsVC = [storyboard instantiateViewControllerWithIdentifier:@"LayoutOnScreenControlsViewController"]; + self.layoutOnScreenControlsVC.modalPresentationStyle = UIModalPresentationFullScreen; + } + self.layoutOnScreenControlsVC.view.backgroundColor = [UIColor colorWithWhite:0.55 alpha:1.0]; [self presentViewController:self.layoutOnScreenControlsVC animated:YES completion:nil]; } diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 33139121..dd29d57d 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -60,6 +60,7 @@ UIScrollView *_scrollView; BOOL _userIsInteracting; bool viewJustLoaded; + bool viewIsBeingResized; CGSize _keyboardSize; UIWindow *_extWindow; UIView *_streamVideoRenderView; @@ -314,7 +315,8 @@ _settings = [[[DataManager alloc] init] getSettings]; //StreamFrameViewController retrieve the settings here. overlayLevel = _settings.statsOverlayLevel.intValue; - [self configOscLayoutTool]; + if(viewIsBeingResized) viewIsBeingResized = false; + else [self configOscLayoutTool]; [self updateToolboxSpecialEntries]; [self configGestures]; [self configZoomGestureAndAddStreamView]; @@ -491,6 +493,7 @@ - (void)viewDidLoad { viewJustLoaded = true; + viewIsBeingResized = false; [super viewDidLoad]; [self.navigationController setNavigationBarHidden:YES animated:YES]; @@ -869,6 +872,7 @@ } - (void) handleViewResize{ + viewIsBeingResized = true; _streamView.bounds = _deviceWindow.bounds; _streamView.frame = _deviceWindow.frame; if(![self isAirPlaying]){ From 04ecd45f7c551eb98ce7f59dbe06691e5dd286b0 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 27 Jul 2025 00:32:44 +0800 Subject: [PATCH 16/18] osw layouTool post orientation change actions --- .../LayoutOnScreenControlsViewController.m | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index 4500bb38..8b847391 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -273,8 +273,8 @@ - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator{ viewWillBeResized = true; - //if(CGSizeEqualToSize(size, self.view.frame.size)) return; - [self saveTapped:nil]; + [self clearStickIndicator]; + if(!_quickSwitchEnabled) [self saveTapped:nil]; } - (void)deviceOrientationDidChange{ @@ -283,7 +283,12 @@ - (void)handleOrientationChangeForOnScreenWidgets{ if(!viewWillBeResized) return; + [self setupWidgetPanel]; + viewWillBeResized = false; + selectedWidgetView = nil; + selectedControllerLayer = nil; + _oscProfilesTableViewController.layoutViewBounds = self.view.bounds; [OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager [self reloadOnScreenWidgetViews]; From 5fcfa84766bcc11c714529c167b33ad7b09014b7 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 27 Jul 2025 01:12:07 +0800 Subject: [PATCH 17/18] try to fix aboutView ok button --- VoidLink/AboutView.swift | 14 ++++++++++++-- VoidLink/AboutViewController.swift | 6 +++--- VoidLink/Input/CustomOSC/OSCProfilesManager.m | 2 +- VoidLink/Input/OnScreenWidgetView.swift | 4 ++-- VoidLink/ViewControllers/MainFrameViewController.m | 2 +- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/VoidLink/AboutView.swift b/VoidLink/AboutView.swift index 2a6cd445..116b1ff8 100644 --- a/VoidLink/AboutView.swift +++ b/VoidLink/AboutView.swift @@ -10,8 +10,10 @@ import SwiftUI @available(iOS 13.0, *) + public struct AboutView: View { @Environment(\.presentationMode) var presentationMode + public var aboutVC:UIViewController public var body: some View { VStack(spacing: 10) { @@ -62,13 +64,12 @@ public struct AboutView: View { Spacer() // OK 按钮 Button(SwiftLocalizationHelper.localizedString(forKey: "OK")) { - presentationMode.wrappedValue.dismiss() + aboutVC.dismiss(animated:true) } .padding() .background(Color.blue) .foregroundColor(.white) .frame(height: 46) - //.frame(width: 100) .cornerRadius(12) } else { HStack(spacing: 20) { @@ -94,6 +95,14 @@ public struct AboutView: View { .cornerRadius(12) .frame(height: 33) .frame(minWidth: 100)*/ + Button(SwiftLocalizationHelper.localizedString(forKey: "OK")) { + aboutVC.dismiss(animated:true) + } + .padding() + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(12) + .frame(height: 33) } .padding(.top, 10) } @@ -101,3 +110,4 @@ public struct AboutView: View { .padding() } } + diff --git a/VoidLink/AboutViewController.swift b/VoidLink/AboutViewController.swift index be751ec2..ea1fdcb6 100644 --- a/VoidLink/AboutViewController.swift +++ b/VoidLink/AboutViewController.swift @@ -13,8 +13,9 @@ import SwiftUI @objc class AboutViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - - let hostingVC = UIHostingController(rootView: AboutView()) + + let hostingVC = UIHostingController(rootView: AboutView( aboutVC: self)) + addChild(hostingVC) hostingVC.view.translatesAutoresizingMaskIntoConstraints = false view.addSubview(hostingVC.view) @@ -29,7 +30,6 @@ import SwiftUI // 🎯 设置弹窗大小 preferredContentSize = CGSize(width: 530, height: 450) - //preferredContentSize = CGSize(width:150, height:120 ) // ⚠️ 一定要设置以下样式才会生效 preferredContentSize modalPresentationStyle = .formSheet diff --git a/VoidLink/Input/CustomOSC/OSCProfilesManager.m b/VoidLink/Input/CustomOSC/OSCProfilesManager.m index 53fe86c7..ab8fd1fa 100644 --- a/VoidLink/Input/CustomOSC/OSCProfilesManager.m +++ b/VoidLink/Input/CustomOSC/OSCProfilesManager.m @@ -26,7 +26,7 @@ static CGRect layoutViewBounds; static OSCProfilesManager *_sharedManager = nil; static dispatch_once_t onceToken; layoutViewBounds = viewBounds; - NSLog(@"bounds width: %f, height: %f", layoutViewBounds.size.width, layoutViewBounds.size.height); + // NSLog(@"bounds width: %f, height: %f", layoutViewBounds.size.width, layoutViewBounds.size.height); dispatch_once(&onceToken, ^{ _sharedManager = [[self alloc] init]; }); diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index 59f8f46f..48bf2aac 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -472,7 +472,7 @@ import UIKit if vibrationOn { vibrationGenerator.prepare() vibrationGenerator.impactOccurred() - print("vibrationInstance: \(vibrationGenerator)") + // print("vibrationInstance: \(vibrationGenerator)") } return indicatorBorder @@ -864,7 +864,7 @@ import UIKit if vibrationOn { vibrationGenerator.prepare() vibrationGenerator.impactOccurred() - print("vibrationInstance: \(vibrationGenerator)") + // print("vibrationInstance: \(vibrationGenerator)") } CATransaction.commit() } diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 51cf0ebb..ecb98c1e 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1941,7 +1941,7 @@ static NSMutableSet* hostList; // This function forces immediate decoding of the UIImage, rather // than the default lazy decoding that results in janky scrolling. -(void)deviceOrientationDidChange{ - if(self.collectionView.superview == nil) return; + if(self.revealViewController.isStreaming || self.collectionView.superview == nil) return; [NSLayoutConstraint activateConstraints:@[ [self.collectionView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], [self.collectionView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor], From 96f11a4a69f993c212f8c64fda6e018c8da7ecab Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 27 Jul 2025 02:13:11 +0800 Subject: [PATCH 18/18] host view improvements --- VoidLink/Localization/Localizable.xcstrings | 39 +++++++++++++++---- VoidLink/Stream/Connection.h | 2 +- .../ViewControllers/MainFrameViewController.m | 33 ++++++++-------- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 273ba860..0183d2e4 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -2898,6 +2898,29 @@ } } }, + "Offline\n%@" : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "离线\n%@" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "離線\n%@" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "離線\n%@" + } + } + } + }, "Ok" : { "extractionState" : "manual", "localizations" : { @@ -2990,48 +3013,48 @@ } } }, - "Online - Not Paired" : { + "Online - Not Paired\n%@" : { "extractionState" : "manual", "localizations" : { "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "在线 - 未配对" + "value" : "在线 - 未配对\n%@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "線上 - 未配對" + "value" : "線上 - 未配對\n%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "線上 - 未配對" + "value" : "線上 - 未配對\n%@" } } } }, - "Online - Paired" : { + "Online - Paired\n%@" : { "extractionState" : "manual", "localizations" : { "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "在线 - 已配对" + "value" : "在线 - 已配对\n%@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "線上 - 已配對" + "value" : "線上 - 已配對\n%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "線上 - 已配對" + "value" : "線上 - 已配對\n%@" } } } diff --git a/VoidLink/Stream/Connection.h b/VoidLink/Stream/Connection.h index dc155419..ae50056e 100644 --- a/VoidLink/Stream/Connection.h +++ b/VoidLink/Stream/Connection.h @@ -9,7 +9,7 @@ #import "VideoDecoderRenderer.h" #import "StreamConfiguration.h" -#define CONN_TEST_SERVER "ios.conntest.moonlight-stream.org" +#define CONN_TEST_SERVER "www.baidu.com" typedef struct { CFTimeInterval startTime; diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index ecb98c1e..d49bd450 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -595,22 +595,24 @@ static NSMutableSet* hostList; Log(LOG_D, @"Long clicked host: %@", host.name); NSString* message; + NSString* hostAddress = [host.activeAddress componentsSeparatedByString:@":"].firstObject; + switch (host.state) { case StateOffline: - message = [LocalizationHelper localizedStringForKey:@"Offline"]; + message = [LocalizationHelper localizedStringForKey:@"Offline\n%@", hostAddress ? hostAddress : @"Unknown address"]; break; case StateOnline: if (host.pairState == PairStatePaired) { - message = [LocalizationHelper localizedStringForKey:@"Online - Paired"]; + message = [LocalizationHelper localizedStringForKey:@"Online - Paired\n%@", hostAddress ? hostAddress : @"Unknown address"]; } else { - message = [LocalizationHelper localizedStringForKey:@"Online - Not Paired"]; + message = [LocalizationHelper localizedStringForKey:@"Online - Not Paired\n%@", hostAddress ? hostAddress : @"Unknown address"]; } break; case StateUnknown: - message = [LocalizationHelper localizedStringForKey:@"Connecting"]; + message = [LocalizationHelper localizedStringForKey:@"Connecting\n"]; break; default: @@ -620,7 +622,7 @@ static NSMutableSet* hostList; UIAlertController* longClickAlert = [UIAlertController alertControllerWithTitle:host.name message:message preferredStyle:UIAlertControllerStyleActionSheet]; if (host.state != StateOnline) { - /*[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Wake PC"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ + [longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Wake PC"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ UIAlertController* wolAlert = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@"Wake-On-LAN"] message:@"" preferredStyle:UIAlertControllerStyleAlert]; [wolAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:nil]]; if (host.mac == nil || [host.mac isEqualToString:@"00:00:00:00:00:00"]) { @@ -632,7 +634,7 @@ static NSMutableSet* hostList; wolAlert.message = [LocalizationHelper localizedStringForKey:@"Successfully sent wake-up request. It may take a few moments for the PC to wake. If it never wakes up, ensure it's properly configured for Wake-on-LAN."]; } [[self activeViewController] presentViewController:wolAlert animated:YES completion:nil]; - }]];*/ + }]]; } else if (host.pairState == PairStatePaired) { /* @@ -642,18 +644,14 @@ static NSMutableSet* hostList; }]]; */ #if !TARGET_OS_TV - if (false) { - [longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"NVIDIA GameStream End-of-Service"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ - [Utils launchUrl:@"https://github.com/moonlight-stream/moonlight-docs/wiki/NVIDIA-GameStream-End-Of-Service-Announcement-FAQ"]; - }]]; - } + #endif } - [longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Test Network"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { + /*[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Test Network"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) { [self showLoadingFrame:^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Perform the network test on a GCD worker thread. It may take a while. - unsigned int portTestResult = LiTestClientConnectivity(CONN_TEST_SERVER, 443, ML_PORT_FLAG_ALL); + unsigned int portTestResult = LiTestClientConnectivity([host.activeAddress UTF8String], 443, ML_PORT_FLAG_ALL); dispatch_sync(dispatch_get_main_queue(), ^{ [self hideLoadingFrame:^{ NSString* message; @@ -679,8 +677,9 @@ static NSMutableSet* hostList; }); }); }]; - }]]; + }]];*/ #if !TARGET_OS_TV + /* if (host.state != StateOnline) { [longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"NVIDIA GameStream End-of-Service"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ [Utils launchUrl:@"https://github.com/moonlight-stream/moonlight-docs/wiki/NVIDIA-GameStream-End-Of-Service-Announcement-FAQ"]; @@ -688,7 +687,7 @@ static NSMutableSet* hostList; [longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Connection Help"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ [Utils launchUrl:@"https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting"]; }]]; - } + }*/ #endif [longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Remove Host"] style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) { // host removed here [self->_discMan removeHostFromDiscovery:host]; @@ -746,7 +745,7 @@ static NSMutableSet* hostList; }]; }); } else { - unsigned int portTestResults = LiTestClientConnectivity(CONN_TEST_SERVER, 443, + unsigned int portTestResults = LiTestClientConnectivity([host.activeAddress UTF8String], 443, ML_PORT_FLAG_TCP_47984 | ML_PORT_FLAG_TCP_47989); if (portTestResults != ML_TEST_RESULT_INCONCLUSIVE && portTestResults != 0) { error = [error stringByAppendingString:[LocalizationHelper localizedStringForKey:@"!ML_TEST_RESULT_INCONCLUSIVE"]]; @@ -1932,7 +1931,7 @@ static NSMutableSet* hostList; [self updateHostShortcuts]; // Update the title in case we now have a PC - [self updateTitle]; + // [self updateTitle]; // Reset state first so we can rediscover hosts that were deleted before [_discMan resetDiscoveryState];