From bb6f2f0c0dd891587fdc12fe4a8d6ce821157050 Mon Sep 17 00:00:00 2001 From: True Zhuanjia Date: Wed, 11 Feb 2026 22:56:50 +0800 Subject: [PATCH] feat: track point for native touch --- VoidLink/Input/NativeTouchHandler.m | 77 ++++++++++++++++++++----- VoidLink/Input/OnScreenWidgetView.swift | 3 +- VoidLink/Input/PureNativeTouchHandler.m | 74 +++++++++++++++++++----- 3 files changed, 125 insertions(+), 29 deletions(-) diff --git a/VoidLink/Input/NativeTouchHandler.m b/VoidLink/Input/NativeTouchHandler.m index 69f70322..f25a1eb4 100644 --- a/VoidLink/Input/NativeTouchHandler.m +++ b/VoidLink/Input/NativeTouchHandler.m @@ -15,7 +15,6 @@ #include @implementation NativeTouchHandler { - TemporarySettings* currentSettings; __weak StreamView* streamView; bool activateCoordSelector; bool singleTouchDisabled; @@ -34,6 +33,9 @@ NSMutableSet *pointerIdPool; //pre-defined pool of pointerIds. NSMutableSet *unassignedPointerIds; NSMutableSet *blacklistedTouches; + + bool trackPointEnabled; + NSMutableArray * trackPointPool; //pre-defined pool of pointerIds. NSMutableDictionary *pointerObjDict; @@ -49,16 +51,19 @@ - (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings{ self = [super init]; self->streamView = view; - self->currentSettings = settings; - self->activateCoordSelector = currentSettings.pointerVelocityModeDivider.floatValue != 1.0; - self->moveEventIntervalNSec = (int64_t)(currentSettings.touchMoveEventInterval.intValue * 1000);; + self->activateCoordSelector = settings.pointerVelocityModeDivider.floatValue != 1.0; + self->moveEventIntervalNSec = (int64_t)(settings.touchMoveEventInterval.intValue * 1000);; self->streamViewBounds = view.bounds; - + self->pointerIdDict = [NSMutableDictionary dictionary]; self->pointerIdPool = [NSMutableSet set]; + self->trackPointEnabled = settings.touchPointTracking; + self->trackPointPool = [NSMutableArray array]; for (uint8_t i = 0; i <= 10; i++) { //ipadOS supports upto 11 finger touches [self->pointerIdPool addObject:@(i)]; + [self->trackPointPool addObject:[GraphicUtils makeTouchTrackpointIn:streamView]]; } + self->activePointerIds = [NSMutableSet set]; self->blacklistedTouches = [NSMutableSet set]; @@ -219,7 +224,7 @@ } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - if (asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchDownQos, 0), ^{ + 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 if([OnScreenControls.touchesCapturedByOnScreenControls containsObject:touch]) continue; @@ -227,16 +232,21 @@ if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch]; [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN withEvent:event]; } + + dispatch_async(dispatch_get_main_queue(), ^{ + if(self->trackPointEnabled){ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + for (UITouch* touch in touches){ + uint8_t pointerId = [self retrievePointerIdFromDict:touch]; + CAShapeLayer* trackPoint = self->trackPointPool[pointerId]; + trackPoint.position = [touch locationInView:self->streamView]; + trackPoint.hidden = false; + } + [CATransaction commit]; + } + }); }); - 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([OnScreenControls.touchesCapturedByOnScreenControls containsObject:touch]) continue; - [self handleTouchDown:touch]; //generate & populate pointerId - if(self->activateCoordSelector) [self populatePointerObjIntoDict:touch]; - [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN withEvent:event]; - } - } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { @@ -248,6 +258,19 @@ [[self getPointerObjFromDict:touch] doesNeedResetCoords]; } }); + + dispatch_async(dispatch_get_main_queue(), ^{ + if(self->trackPointEnabled){ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + for (UITouch* touch in touches){ + uint8_t pointerId = [self retrievePointerIdFromDict:touch]; + CAShapeLayer* trackPoint = self->trackPointPool[pointerId]; + trackPoint.position = [touch locationInView:self->streamView]; + } + [CATransaction commit]; + } + }); } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { @@ -263,6 +286,19 @@ [self->blacklistedTouches removeObject:@((uintptr_t)touch)]; } }); + + dispatch_async(dispatch_get_main_queue(), ^{ + if(self->trackPointEnabled){ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + for (UITouch* touch in touches){ + uint8_t pointerId = [self retrievePointerIdFromDict:touch]; + CAShapeLayer* trackPoint = self->trackPointPool[pointerId]; + trackPoint.hidden = true; + } + [CATransaction commit]; + } + }); } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { @@ -302,4 +338,15 @@ singleTouchDisabled = !enabled; } +- (void)dealloc{ + NSLog(@"dealloc nativeTouchHanlder %f", CACurrentMediaTime()); + pointerIdDict = nil; + activePointerIds = nil; + pointerIdPool= nil; + unassignedPointerIds = nil; + blacklistedTouches = nil; + trackPointPool = nil; + pointerObjDict = nil; +} + @end diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift index e7cfabe5..21b662a6 100644 --- a/VoidLink/Input/OnScreenWidgetView.swift +++ b/VoidLink/Input/OnScreenWidgetView.swift @@ -470,9 +470,10 @@ import SVGKit self.hasL3R3Indicator = !self.isStickWheel && !self.isDirectionPad && self.widgetType == WidgetTypeEnum.touchPad self.hasTrackPoint = (CommandManager.vectorTouchPads.contains(self.touchPadString) + || self.isStickWheel || (self.widgetType == WidgetTypeEnum.button && (buttonMode == .slideAndHold || buttonMode == .slideToToggle))) - } + } // ====================================================================================================== @objc public func setupAutoTapTimer() { diff --git a/VoidLink/Input/PureNativeTouchHandler.m b/VoidLink/Input/PureNativeTouchHandler.m index 19663af0..b8ead698 100644 --- a/VoidLink/Input/PureNativeTouchHandler.m +++ b/VoidLink/Input/PureNativeTouchHandler.m @@ -15,7 +15,6 @@ #include @implementation PureNativeTouchHandler { - TemporarySettings* currentSettings; __weak StreamView* streamView; bool activateCoordSelector; CGFloat pointerVelocityDividerLocationByPoints; @@ -33,6 +32,9 @@ NSMutableSet *pointerIdPool; //pre-defined pool of pointerIds. NSMutableSet *unassignedPointerIds; NSMutableSet *blacklistedTouches; + + bool trackPointEnabled; + NSMutableArray * trackPointPool; NSMutableDictionary *pointerObjDict; @@ -48,16 +50,20 @@ - (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings{ self = [super init]; self->streamView = view; - self->currentSettings = settings; - self->activateCoordSelector = currentSettings.pointerVelocityModeDivider.floatValue != 1.0; - self->moveEventIntervalNSec = (int64_t)(currentSettings.touchMoveEventInterval.intValue * 1000);; + + self->activateCoordSelector = settings.pointerVelocityModeDivider.floatValue != 1.0; + self->moveEventIntervalNSec = (int64_t)(settings.touchMoveEventInterval.intValue * 1000);; self->streamViewBounds = view.bounds; self->pointerIdDict = [NSMutableDictionary dictionary]; self->pointerIdPool = [NSMutableSet set]; + self->trackPointEnabled = settings.touchPointTracking; + self->trackPointPool = [NSMutableArray array]; for (uint8_t i = 0; i <= 10; i++) { //ipadOS supports upto 11 finger touches [self->pointerIdPool addObject:@(i)]; + [self->trackPointPool addObject:[GraphicUtils makeTouchTrackpointIn:streamView]]; } + self->activePointerIds = [NSMutableSet set]; self->blacklistedTouches = [NSMutableSet set]; @@ -209,22 +215,27 @@ } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - if (asyncNativeTouch) dispatch_async(dispatch_get_global_queue(touchDownQos, 0), ^{ + 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]; } + dispatch_async(dispatch_get_main_queue(), ^{ + if(self->trackPointEnabled){ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + for (UITouch* touch in touches){ + uint8_t pointerId = [self retrievePointerIdFromDict:touch]; + CAShapeLayer* trackPoint = self->trackPointPool[pointerId]; + trackPoint.position = [touch locationInView:self->streamView]; + trackPoint.hidden = false; + } + [CATransaction commit]; + } + }); }); - 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]; - } - } } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { @@ -235,6 +246,19 @@ [[self getPointerObjFromDict:touch] doesNeedResetCoords]; } }); + + dispatch_async(dispatch_get_main_queue(), ^{ + if(self->trackPointEnabled){ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + for (UITouch* touch in touches){ + uint8_t pointerId = [self retrievePointerIdFromDict:touch]; + CAShapeLayer* trackPoint = self->trackPointPool[pointerId]; + trackPoint.position = [touch locationInView:self->streamView]; + } + [CATransaction commit]; + } + }); } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { @@ -246,6 +270,19 @@ [self->blacklistedTouches removeObject:@((uintptr_t)touch)]; } }); + + dispatch_async(dispatch_get_main_queue(), ^{ + if(self->trackPointEnabled){ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + for (UITouch* touch in touches){ + uint8_t pointerId = [self retrievePointerIdFromDict:touch]; + CAShapeLayer* trackPoint = self->trackPointPool[pointerId]; + trackPoint.hidden = true; + } + [CATransaction commit]; + } + }); } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { @@ -281,5 +318,16 @@ return pointer.useRelativeCoords ? pointer.latestRelativePoint : pointer.latestPoint; } +- (void)dealloc{ + NSLog(@"dealloc purePativeTouchHanlder %f", CACurrentMediaTime()); + pointerIdDict = nil; + activePointerIds = nil; + pointerIdPool= nil; + unassignedPointerIds = nil; + blacklistedTouches = nil; + trackPointPool = nil; + pointerObjDict = nil; +} + @end