From 1046cfe9a01edc04d194ba19b8a2711ed6fb5d8a Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 20 Jul 2025 19:20:10 +0800 Subject: [PATCH 1/6] - iOS 13 compatibility: try to fix iOS transparent osw widgetPanel background --- .../LayoutOnScreenControlsViewController.m | 51 ++++++++++++++++--- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index 35e425d4..2bed81b4 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -626,7 +626,9 @@ CGRect newFrame = view.frame; newFrame.size = fittingSize; view.frame = newFrame; - if([self isIPhone]) [self updateClippedMaskForView:view]; + [self updateClippedMaskForView:view]; + if (@available(iOS 14, *)) nil; + else [self applyBackgroundForiOS13:view]; } - (void)enableCommonWidgetTools{ @@ -719,7 +721,6 @@ [self.decelerationRateLabel setText:[LocalizationHelper localizedStringForKey:@"Deceleration Rate: %.3f ", selectedWidgetView.trackballDecelerationRate]]; self.mouseButtonDownSelector.selectedSegmentIndex = selectedWidgetView.mouseButtonAction; - if([self isIPhone]){ self.vibrationStyleStack.hidden = [widgetView.cmdString containsString:@"MOUSEPAD"] || @@ -1020,13 +1021,47 @@ } } +- (void)applyBackgroundForiOS13:(UIView* )view { + view.backgroundColor = [UIColor clearColor]; + UIView *backgroundView; + for(UIView* subview in view.subviews){ + if([subview.accessibilityIdentifier isEqualToString:@"iOS13Background"]){ + backgroundView = subview; + break; + } + } + if(!backgroundView){ + backgroundView = [[UIView alloc] init]; + backgroundView.accessibilityIdentifier = @"iOS13Background"; + } + + backgroundView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; + backgroundView.layer.cornerRadius = 16; + backgroundView.clipsToBounds = YES; + backgroundView.translatesAutoresizingMaskIntoConstraints = NO; + backgroundView.userInteractionEnabled = NO; + + [view insertSubview:backgroundView atIndex:0]; + + // UIEdgeInsets m = self.layoutMargins; + [NSLayoutConstraint activateConstraints:@[ + [backgroundView.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:0], + [backgroundView.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:0], + [backgroundView.topAnchor constraintEqualToAnchor:view.topAnchor constant:0], + [backgroundView.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:0] + ]]; +} + + - (void)updateClippedMaskForView:(UIView* )view{ - CAShapeLayer *maskLayer = [CAShapeLayer layer]; - view.layer.mask = nil; - CGRect visibleRect = CGRectInset(view.bounds, 40, 0); // 左右各裁掉 20pt - UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:visibleRect cornerRadius:12]; - maskLayer.path = path.CGPath; - view.layer.mask = maskLayer; + if([self isIPhone]){ + CAShapeLayer *maskLayer = [CAShapeLayer layer]; + view.layer.mask = nil; + CGRect visibleRect = CGRectInset(view.bounds, 40, 0); // 左右各裁掉 20pt + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:visibleRect cornerRadius:12]; + maskLayer.path = path.CGPath; + view.layer.mask = maskLayer; + } } - (BOOL)isIPhone{ From 25073803ba7aad239a55e5c169f70e092c58e134 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 20 Jul 2025 21:54:06 +0800 Subject: [PATCH 2/6] iOS13 compatibility: apply background for each widget in widget panel --- .../LayoutOnScreenControlsViewController.m | 75 ++++++++++--------- .../ViewControllers/MainFrameViewController.m | 3 +- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m index 2bed81b4..1e881a1c 100644 --- a/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m +++ b/VoidLink/ViewControllers/CustomOSCViewControl/LayoutOnScreenControlsViewController.m @@ -621,14 +621,14 @@ } } -- (void)autoFitView:(UIView* )view{ - CGSize fittingSize = [view systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; - CGRect newFrame = view.frame; +- (void)autoFitStack:(UIStackView* )stack{ + CGSize fittingSize = [stack systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; + CGRect newFrame = stack.frame; newFrame.size = fittingSize; - view.frame = newFrame; - [self updateClippedMaskForView:view]; + stack.frame = newFrame; + [self updateClippedMaskForView:stack]; if (@available(iOS 14, *)) nil; - else [self applyBackgroundForiOS13:view]; + else [self applyShadowForiOS13:stack]; } - (void)enableCommonWidgetTools{ @@ -683,7 +683,7 @@ self.mouseDownButtonStack.hidden = !([selectedWidgetView.cmdString containsString:@"MOUSEPAD"] && selectedWidgetView.widgetType == WidgetTypeEnumTouchPad); self.decelerationRateStack.hidden = !([selectedWidgetView.cmdString containsString:@"TRACKBALL"] && selectedWidgetView.widgetType == WidgetTypeEnumTouchPad); - [self autoFitView:self.widgetPanelStack]; + [self autoFitStack:self.widgetPanelStack]; if(showSensitivityFactorStack){ [self.sensitivityXSlider setValue:self->selectedWidgetView.sensitivityFactorX]; @@ -725,7 +725,7 @@ self.vibrationStyleStack.hidden = [widgetView.cmdString containsString:@"MOUSEPAD"] || [widgetView.cmdString containsString:@"TRACKBALL"]; - [self autoFitView:self.widgetPanelStack]; + [self autoFitStack:self.widgetPanelStack]; self.vibrationStyleSelector.selectedSegmentIndex = self->selectedWidgetView.vibrationStyle; } } @@ -770,7 +770,7 @@ NSNumber *style = [OnScreenControls.layerVibrationStyleDic objectForKey:selectedControllerLayer.name]; self.vibrationStyleSelector.selectedSegmentIndex = [style unsignedCharValue]; } - [self autoFitView:_widgetPanelStack]; + [self autoFitStack:_widgetPanelStack]; } - (void)widgetSizeSliderMoved:(UISlider* )sender{ @@ -996,7 +996,7 @@ self.widgetPanelStack.frame = frame; - [self autoFitView:self.widgetPanelStack]; + [self autoFitStack:self.widgetPanelStack]; if([self isIPhone]) { for(UIView* view in _widgetPanelStack.arrangedSubviews){ @@ -1021,35 +1021,36 @@ } } -- (void)applyBackgroundForiOS13:(UIView* )view { - view.backgroundColor = [UIColor clearColor]; - UIView *backgroundView; - for(UIView* subview in view.subviews){ - if([subview.accessibilityIdentifier isEqualToString:@"iOS13Background"]){ - backgroundView = subview; - break; +- (void)applyShadowForiOS13:(UIStackView* )stack { + stack.backgroundColor = [UIColor clearColor]; + + for(UIView* view in stack.arrangedSubviews){ + if([view isKindOfClass:[UIStackView class]]){ + UIStackView* subStack = (UIStackView* )view; + for(UIView* view in subStack.arrangedSubviews){ + if([view isKindOfClass:[UILabel class]]){ + view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; + view.layer.cornerRadius = 6; + view.clipsToBounds = YES; + UILabel* label = (UILabel* )view; + label.textAlignment = NSTextAlignmentCenter; + } + else{ + view.tintColor= [UIColor systemTealColor]; + view.layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.9].CGColor; + //view.layer.shadowColor = [UIColor blackColor].CGColor; + view.layer.shadowOffset = CGSizeMake(1, 1); + view.layer.shadowOpacity = 1; + view.layer.shadowRadius = 5; + } + } + } + else{ + view.layer.cornerRadius = 10; + view.clipsToBounds = YES; + view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; } } - if(!backgroundView){ - backgroundView = [[UIView alloc] init]; - backgroundView.accessibilityIdentifier = @"iOS13Background"; - } - - backgroundView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; - backgroundView.layer.cornerRadius = 16; - backgroundView.clipsToBounds = YES; - backgroundView.translatesAutoresizingMaskIntoConstraints = NO; - backgroundView.userInteractionEnabled = NO; - - [view insertSubview:backgroundView atIndex:0]; - - // UIEdgeInsets m = self.layoutMargins; - [NSLayoutConstraint activateConstraints:@[ - [backgroundView.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:0], - [backgroundView.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:0], - [backgroundView.topAnchor constraintEqualToAnchor:view.topAnchor constant:0], - [backgroundView.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:0] - ]]; } diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 6383fc2f..711af1a0 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1788,9 +1788,10 @@ static NSMutableSet* hostList; [super viewWillAppear:NO]; /* this makes background color works*/ + /* for (UIView *subview in self.view.subviews) { [subview removeFromSuperview]; // 暂时移除所有子视图 - } + }*/ // We can get here on home press while streaming From b118f80b520398c31d6b04386b592854f2e191b1 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sun, 20 Jul 2025 23:11:02 +0800 Subject: [PATCH 3/6] touchHandler debug --- VoidLink/Input/NativeTouchHandler.m | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/VoidLink/Input/NativeTouchHandler.m b/VoidLink/Input/NativeTouchHandler.m index 0c82e10b..b5871fb5 100644 --- a/VoidLink/Input/NativeTouchHandler.m +++ b/VoidLink/Input/NativeTouchHandler.m @@ -35,6 +35,7 @@ NSMutableSet *pointerIdPool; //pre-defined pool of pointerIds. NSMutableSet *unassignedPointerIds; NSMutableSet *blacklistedTouches; + bool doNotBlockTouchEvents; NSMutableDictionary *pointerObjDict; @@ -60,6 +61,7 @@ } self->activePointerIds = [NSMutableSet set]; self->blacklistedTouches = [NSMutableSet set]; + doNotBlockTouchEvents = true; self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff; @@ -165,7 +167,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]; } } @@ -189,8 +192,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]); @@ -236,7 +239,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]; - [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); } @@ -262,8 +265,9 @@ [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{ From 0728959ebe31ae8c5e1287a70157f4118d6ee349 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Mon, 21 Jul 2025 00:41:59 +0800 Subject: [PATCH 4/6] iOS13 compatibility: audio menu section icon --- .../speaker.wave.2.imageset/Contents.json | 21 +++++++++++++++++++ .../speaker.wave.2.svg | 13 ++++++++++++ .../ViewControllers/SettingsViewController.m | 2 +- 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 VoidLink/Images.xcassets/speaker.wave.2.imageset/Contents.json create mode 100644 VoidLink/Images.xcassets/speaker.wave.2.imageset/speaker.wave.2.svg diff --git a/VoidLink/Images.xcassets/speaker.wave.2.imageset/Contents.json b/VoidLink/Images.xcassets/speaker.wave.2.imageset/Contents.json new file mode 100644 index 00000000..2a082ab7 --- /dev/null +++ b/VoidLink/Images.xcassets/speaker.wave.2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "speaker.wave.2.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/VoidLink/Images.xcassets/speaker.wave.2.imageset/speaker.wave.2.svg b/VoidLink/Images.xcassets/speaker.wave.2.imageset/speaker.wave.2.svg new file mode 100644 index 00000000..3f993019 --- /dev/null +++ b/VoidLink/Images.xcassets/speaker.wave.2.imageset/speaker.wave.2.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 39555fd1..76b0ce7f 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -576,7 +576,7 @@ BOOL isCustomResolution(CGSize res) { audioSection.delegate = self; audioSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Audio"]; if (@available(iOS 13.0, *)) { - [audioSection setSectionWithIcon:[UIImage systemImageNamed:@"speaker.wave.2"] andSize:23]; + [audioSection setSectionWithIcon:[UIImage imageNamed:@"speaker.wave.2"] andSize:20]; } [self addSetting:self.audioOnPcStack ofId:@"audioOnPcStack" withInfoTag:NO withDynamicLabel:NO to:audioSection]; From eb4153674957e4f42d5bf9afd17093095182940f Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Mon, 21 Jul 2025 13:01:19 +0800 Subject: [PATCH 5/6] disable background hostCard update while streaming --- VoidLink/HostCardView.h | 1 + VoidLink/HostCardView.m | 4 +++- .../ViewControllers/MainFrameViewController.m | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/VoidLink/HostCardView.h b/VoidLink/HostCardView.h index b78f2e07..a7c903a2 100644 --- a/VoidLink/HostCardView.h +++ b/VoidLink/HostCardView.h @@ -16,6 +16,7 @@ - (void)wakeupButtonTappedForHost:(TemporaryHost *)host; - (void)pairButtonTappedForHost:(TemporaryHost *)host; - (void)hostCardLongPressed:(TemporaryHost *)host view:(UIView *)view; +- (bool)isStreaming; @end diff --git a/VoidLink/HostCardView.m b/VoidLink/HostCardView.m index 3a1b1d0e..85ed5a21 100644 --- a/VoidLink/HostCardView.m +++ b/VoidLink/HostCardView.m @@ -509,10 +509,12 @@ static const float REFRESH_CYCLE = 2.0f; return; } + NSLog(@"hostCard update loop %f", CACurrentMediaTime()); + [self updateContentsForHost:_host]; // Queue the next refresh cycle - [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; + if(![self.delegate isStreaming]) [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE]; } - (void) updateContentsForHost:(TemporaryHost*)host { diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 711af1a0..46f7f0b9 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -69,6 +69,7 @@ NSCache* _boxArtCache; bool _background; bool _enteredAppView; + bool _settingsViewExpanded; UIView* menuSeparator; UIView* snapshot; SettingsViewController* settingsViewController; @@ -1119,6 +1120,7 @@ static NSMutableSet* hostList; - (void)revealController:(SWRevealViewController *)revealController willMoveToPosition:(FrontViewPosition)position { settingsViewController = (SettingsViewController*)[revealController rearViewController]; revealController.navBarMenuDelegate = settingsViewController; + _settingsViewExpanded = position != FrontViewPositionLeft; if (position == FrontViewPositionLeft) { self.navigationItem.leftBarButtonItems = @[_settingsButton]; } @@ -1788,11 +1790,12 @@ static NSMutableSet* hostList; [super viewWillAppear:NO]; /* this makes background color works*/ - /* - for (UIView *subview in self.view.subviews) { - [subview removeFromSuperview]; // 暂时移除所有子视图 - }*/ - + + if(!_settingsViewExpanded){ + for (UIView *subview in self.view.subviews) { + [subview removeFromSuperview]; // 暂时移除所有子视图 + } + } // We can get here on home press while streaming // since the stream view segues to us just before @@ -2008,6 +2011,10 @@ static NSMutableSet* hostList; return !self.revealViewController.isStreaming && _enteredAppView; } +- (bool)isStreaming{ + return self.revealViewController.isStreaming; +} + - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AppCell" forIndexPath:indexPath]; From 282b3d654508cbf19cdf5c85130e8fedddb39b83 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Mon, 21 Jul 2025 13:14:12 +0800 Subject: [PATCH 6/6] appView tweak --- VoidLink/UIAppView.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/VoidLink/UIAppView.m b/VoidLink/UIAppView.m index 5975b34f..abe6d208 100644 --- a/VoidLink/UIAppView.m +++ b/VoidLink/UIAppView.m @@ -236,7 +236,7 @@ static UIImage* noImage; _appImage.layer.opacity = 0.5f; } - (void) buttonDeselected:(id)sender { - _appImage.layer.opacity = 1.0f; + _appImage.layer.opacity = [self isCurrentApp] ? 0.75 : 1.0f; } - (void) positionSubviews { @@ -252,7 +252,8 @@ static UIImage* noImage; if (_appOverlay != nil) { _appOverlay.frame = [self isCurrentApp] ? CGRectMake(0, 0, frameSize.width / 2.39, frameSize.width / 2.39) : CGRectMake(0, 0, frameSize.width / 2, frameSize.width / 2); _appOverlay.center = CGPointMake(frameSize.width/2, frameSize.height/2 - 2 * verticalPadding); - + if([self isCurrentApp]) _appImage.layer.opacity = 0.75f; + //[_appLabel setFrame:CGRectMake(padding, _appOverlay.frame.size.height + padding, frameSize.width - 2 * padding, frameSize.height - _appOverlay.frame.size.height - 2 * padding)]; } }