From 6c72f0331b0bd061ddc84a1bb990de93bd0c80f5 Mon Sep 17 00:00:00 2001 From: TrueZhuanjia Date: Sat, 13 Sep 2025 17:33:40 +0800 Subject: [PATCH] feat: implements button visual feedback switch for legacy osc --- VoidLink/Input/OnScreenControls.m | 102 +++++++++--------- VoidLink/Stream/StreamConfiguration.h | 1 + VoidLink/Stream/StreamConfiguration.m | 2 +- .../ViewControllers/MainFrameViewController.m | 1 + 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/VoidLink/Input/OnScreenControls.m b/VoidLink/Input/OnScreenControls.m index 62db5033..98f6c128 100644 --- a/VoidLink/Input/OnScreenControls.m +++ b/VoidLink/Input/OnScreenControls.m @@ -73,6 +73,7 @@ static NSSet *validPositionButtonNames; VoidController *_controller; NSMutableArray* _deadTouches; BOOL _swapABXY; + BOOL _visualFeedbackEnabled; BOOL _largerStickLR1; CGFloat _oscTapExlusionAreaSizeFactor; OSCProfilesManager *profilesManager; @@ -219,6 +220,7 @@ static float L3_Y; _deadTouches = [[NSMutableArray alloc] init]; if (streamConfig) { _swapABXY = streamConfig.swapABXYButtons; + _visualFeedbackEnabled = streamConfig.buttonVisualFeedback; } _originalControllerLayerOpacityDict = [[NSMutableDictionary alloc] init]; @@ -1103,7 +1105,7 @@ static float L3_Y; // Allow the user to slide their finger to another d-pad button if ([_upButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:UP_FLAG]; - [self oscButtonTouchDownFeedback:_upButton]; + [self handleButtonFeedback:_upButton]; updated = true; } else { _upButton.opacity = [_originalControllerLayerOpacityDict[_upButton.name] floatValue]; @@ -1112,7 +1114,7 @@ static float L3_Y; if ([_downButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:DOWN_FLAG]; - [self oscButtonTouchDownFeedback:_downButton]; + [self handleButtonFeedback:_downButton]; updated = true; } else { _downButton.opacity = [_originalControllerLayerOpacityDict[_downButton.name] floatValue]; @@ -1121,7 +1123,7 @@ static float L3_Y; if ([_leftButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:LEFT_FLAG]; - [self oscButtonTouchDownFeedback:_leftButton]; + [self handleButtonFeedback:_leftButton]; updated = true; } else { _leftButton.opacity = [_originalControllerLayerOpacityDict[_leftButton.name] floatValue]; @@ -1130,7 +1132,7 @@ static float L3_Y; if ([_rightButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:RIGHT_FLAG]; - [self oscButtonTouchDownFeedback:_rightButton]; + [self handleButtonFeedback:_rightButton]; updated = true; } else { _rightButton.opacity = [_originalControllerLayerOpacityDict[_rightButton.name] floatValue]; @@ -1177,35 +1179,35 @@ static float L3_Y; } -- (void)oscButtonTouchDownFeedback:(CALayer* )button{ - [CATransaction begin]; - [CATransaction setDisableActions:YES]; - - /* - if(![[_originalControllerLayerOpacityDict allKeys] containsObject:button.name]){ - [_originalControllerLayerOpacityDict setObject:@(button.opacity) forKey:button.name]; +- (void)handleButtonFeedback:(CALayer* )button{ + if(_visualFeedbackEnabled){ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + + button.opacity = 0.9; // set a high opacity to ensure visibility of visual effect + button.borderColor = [UIColor clearColor].CGColor; // Color of the outline + button.borderWidth = 20; // Width of the outline + button.cornerRadius = button.bounds.size.width/2; + + // 使用 shadowPath 定义阴影形状和扩展范围 + CGFloat spread = 15; // 扩散的大小 + if([button.name isEqualToString:@"leftStick"] || [button.name isEqualToString:@"rightStick"]) spread = 12; + CGRect largerRect = CGRectInset(button.bounds, -spread, -spread); + UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:largerRect cornerRadius:button.cornerRadius]; + button.shadowPath = shadowPath.CGPath; + + //button.shadowColor = [[UIColor colorWithRed:0/255.0 green:110/255.0 blue:255/255.0 alpha:1.0] colorWithAlphaComponent:0.7].CGColor; + button.shadowColor = [[UIColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1] colorWithAlphaComponent:1].CGColor; + //button.shadowColor = [UIColor colorWithRed:0/255.0 green:51/255.0 blue:102/255.0 alpha:0.6].CGColor; + button.shadowOffset = CGSizeZero; + button.shadowOpacity = 1.0; + button.shadowRadius = 0.0; // Adjust the radius to simulate border thickness + [CATransaction commit]; } - */ - - button.opacity = 0.9; // set a high opacity to ensure visibility of visual effect - button.borderColor = [UIColor clearColor].CGColor; // Color of the outline - button.borderWidth = 20; // Width of the outline - button.cornerRadius = button.bounds.size.width/2; - - // 使用 shadowPath 定义阴影形状和扩展范围 - CGFloat spread = 15; // 扩散的大小 - if([button.name isEqualToString:@"leftStick"] || [button.name isEqualToString:@"rightStick"]) spread = 12; - CGRect largerRect = CGRectInset(button.bounds, -spread, -spread); - UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:largerRect cornerRadius:button.cornerRadius]; - button.shadowPath = shadowPath.CGPath; - - //button.shadowColor = [[UIColor colorWithRed:0/255.0 green:110/255.0 blue:255/255.0 alpha:1.0] colorWithAlphaComponent:0.7].CGColor; - button.shadowColor = [[UIColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1] colorWithAlphaComponent:1].CGColor; - //button.shadowColor = [UIColor colorWithRed:0/255.0 green:51/255.0 blue:102/255.0 alpha:0.6].CGColor; - button.shadowOffset = CGSizeZero; - button.shadowOpacity = 1.0; - button.shadowRadius = 0.0; // Adjust the radius to simulate border thickness - + [self oscButtonHapticFeedback:button]; +} + +- (void)oscButtonHapticFeedback:(CALayer* )button{ NSNumber *style = [OnScreenControls.layerVibrationStyleDic objectForKey:button.name]; uint8_t vibrationStyle = [style unsignedCharValue]; @@ -1223,12 +1225,8 @@ static float L3_Y; [vibrationGenerator impactOccurred]; NSLog(@"vibration instance: %@",vibrationGenerator); } - - - [CATransaction commit]; } - // osc Button capturing here - (BOOL)handleTouchDownEvent:touches { BOOL updated = false; @@ -1242,28 +1240,28 @@ static float L3_Y; if ([_aButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:A_FLAG]; _aTouch = touch; - [self oscButtonTouchDownFeedback:_aButton]; + [self handleButtonFeedback:_aButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC A"); } else if ([_bButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:B_FLAG]; _bTouch = touch; - [self oscButtonTouchDownFeedback:_bButton]; + [self handleButtonFeedback:_bButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC B"); } else if ([_xButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:X_FLAG]; _xTouch = touch; - [self oscButtonTouchDownFeedback:_xButton]; + [self handleButtonFeedback:_xButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC X"); } else if ([_yButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:Y_FLAG]; _yTouch = touch; - [self oscButtonTouchDownFeedback:_yButton]; + [self handleButtonFeedback:_yButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC Y"); @@ -1271,7 +1269,7 @@ static float L3_Y; [_controllerSupport setButtonFlag:_controller flags:UP_FLAG]; _dpadTouch = touch; _upTouch = touch; - [self oscButtonTouchDownFeedback:_upButton]; + [self handleButtonFeedback:_upButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC UP"); @@ -1279,14 +1277,14 @@ static float L3_Y; [_controllerSupport setButtonFlag:_controller flags:DOWN_FLAG]; _dpadTouch = touch; _downTouch = touch; - [self oscButtonTouchDownFeedback:_downButton]; + [self handleButtonFeedback:_downButton]; updated = true; touchEventCapturedByOsc = true; } else if ([_leftButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:LEFT_FLAG]; _dpadTouch = touch; _leftTouch = touch; - [self oscButtonTouchDownFeedback:_leftButton]; + [self handleButtonFeedback:_leftButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC LEFT"); @@ -1294,49 +1292,49 @@ static float L3_Y; [_controllerSupport setButtonFlag:_controller flags:RIGHT_FLAG]; _dpadTouch = touch; _rightTouch = touch; - [self oscButtonTouchDownFeedback:_rightButton]; + [self handleButtonFeedback:_rightButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC RIGHT"); } else if ([_startButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:PLAY_FLAG]; _startTouch = touch; - [self oscButtonTouchDownFeedback:_startButton]; + [self handleButtonFeedback:_startButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC PLAY"); } else if ([_selectButton.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:BACK_FLAG]; _selectTouch = touch; - [self oscButtonTouchDownFeedback:_selectButton]; + [self handleButtonFeedback:_selectButton]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC BACK"); } else if ([_l1Button.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:LB_FLAG]; _l1Touch = touch; - [self oscButtonTouchDownFeedback:_l1Button]; + [self handleButtonFeedback:_l1Button]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC LB"); } else if ([_r1Button.presentationLayer hitTest:touchLocation]) { [_controllerSupport setButtonFlag:_controller flags:RB_FLAG]; _r1Touch = touch; - [self oscButtonTouchDownFeedback:_r1Button]; + [self handleButtonFeedback:_r1Button]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC RB"); } else if ([_l2Button.presentationLayer hitTest:touchLocation]) { [_controllerSupport updateLeftTrigger:_controller left:0xFF]; _l2Touch = touch; - [self oscButtonTouchDownFeedback:_l2Button]; + [self handleButtonFeedback:_l2Button]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC LeftTrigger"); } else if ([_r2Button.presentationLayer hitTest:touchLocation]) { [_controllerSupport updateRightTrigger:_controller right:0xFF]; _r2Touch = touch; - [self oscButtonTouchDownFeedback:_r2Button]; + [self handleButtonFeedback:_r2Button]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC RightTrigger"); @@ -1376,7 +1374,7 @@ static float L3_Y; double l3TouchTime = [l3TouchStart timeIntervalSinceNow] * -1000.0; if (l3TouchTime < STICK_CLICK_RATE) { [_controllerSupport setButtonFlag:_controller flags:LS_CLK_FLAG]; - [self oscButtonTouchDownFeedback:_leftStick]; + [self handleButtonFeedback:_leftStick]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC LS_CLK3"); @@ -1394,7 +1392,7 @@ static float L3_Y; double r3TouchTime = [r3TouchStart timeIntervalSinceNow] * -1000.0; if (r3TouchTime < STICK_CLICK_RATE) { [_controllerSupport setButtonFlag:_controller flags:RS_CLK_FLAG]; - [self oscButtonTouchDownFeedback:_rightStick]; + [self handleButtonFeedback:_rightStick]; updated = true; touchEventCapturedByOsc = true; // NSLog(@"Captured OSC LS_CLK4"); diff --git a/VoidLink/Stream/StreamConfiguration.h b/VoidLink/Stream/StreamConfiguration.h index 13fe8348..5ff686a0 100644 --- a/VoidLink/Stream/StreamConfiguration.h +++ b/VoidLink/Stream/StreamConfiguration.h @@ -33,6 +33,7 @@ @property BOOL playAudioOnPC; @property BOOL redirectMic; @property BOOL swapABXYButtons; +@property BOOL buttonVisualFeedback; @property BOOL asyncNativeTouchPriority; @property int gyroMode; @property int emulatedControllerType; diff --git a/VoidLink/Stream/StreamConfiguration.m b/VoidLink/Stream/StreamConfiguration.m index 67329718..aab4f9d7 100644 --- a/VoidLink/Stream/StreamConfiguration.m +++ b/VoidLink/Stream/StreamConfiguration.m @@ -9,5 +9,5 @@ #import "StreamConfiguration.h" @implementation StreamConfiguration -@synthesize host, httpsPort, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, appName, optimizeGameSettings, playAudioOnPC, swapABXYButtons, gyroMode, emulatedControllerType, audioConfiguration, supportedVideoFormats, multiController, serverCert, rtspSessionUrl, serverCodecModeSupport, enableYUV444, enablePIP; +@synthesize host, httpsPort, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, appName, optimizeGameSettings, playAudioOnPC, swapABXYButtons, buttonVisualFeedback, gyroMode, emulatedControllerType, audioConfiguration, supportedVideoFormats, multiController, serverCert, rtspSessionUrl, serverCodecModeSupport, enableYUV444, enablePIP; @end diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 57c4ad0b..0793a57d 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -822,6 +822,7 @@ static NSMutableSet* hostList; _streamConfig.playAudioOnPC = streamSettings.playAudioOnPC; _streamConfig.redirectMic = streamSettings.redirectMic; _streamConfig.swapABXYButtons = streamSettings.swapABXYButtons; + _streamConfig.buttonVisualFeedback = streamSettings.buttonVisualFeedback; _streamConfig.asyncNativeTouchPriority = streamSettings.asyncNativeTouchPriority; // new streamConfig segment _streamConfig.gyroMode = [streamSettings.gyroMode intValue]; _streamConfig.emulatedControllerType = streamSettings.emulatedControllerType.intValue;