diff --git a/Limelight/Database/TemporarySettings.h b/Limelight/Database/TemporarySettings.h index da35b30c..c15b58d8 100644 --- a/Limelight/Database/TemporarySettings.h +++ b/Limelight/Database/TemporarySettings.h @@ -18,6 +18,8 @@ @property (nonatomic, retain) NSNumber * width; @property (nonatomic, retain) NSNumber * audioConfig; @property (nonatomic, retain) NSNumber * onscreenControls; +@property (nonatomic, retain) NSNumber * keyboardToggleFingers; +@property (nonatomic, retain) NSNumber * keyboardToggleTaps; @property (nonatomic, retain) NSString * uniqueId; @property (nonatomic) enum { CODEC_PREF_AUTO, diff --git a/Limelight/Database/TemporarySettings.m b/Limelight/Database/TemporarySettings.m index e627e0c1..66bd48f0 100644 --- a/Limelight/Database/TemporarySettings.m +++ b/Limelight/Database/TemporarySettings.m @@ -85,6 +85,9 @@ self.btMouseSupport = settings.btMouseSupport; self.absoluteTouchMode = settings.absoluteTouchMode; self.statsOverlay = settings.statsOverlay; + + self.keyboardToggleTaps = @1; + self.keyboardToggleFingers = @4; #endif self.uniqueId = settings.uniqueId; diff --git a/Limelight/Input/NativeTouchHandler.h b/Limelight/Input/NativeTouchHandler.h new file mode 100644 index 00000000..d0083c9d --- /dev/null +++ b/Limelight/Input/NativeTouchHandler.h @@ -0,0 +1,27 @@ +// +// NativeTouchHandler.h +// Moonlight +// +// Created by ZWM on 2024/5/14. +// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved. +// + +#import "StreamView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NativeTouchHandler : UIResponder + ++ (NSMutableDictionary* )initializePointerIdDict; ++ (NSMutableSet* )initializePointerIdSet; ++ (void)populatePointerId:(UITouch*)event; ++ (void)removePointerId:(UITouch*)event; ++ (uint32_t) retrievePointerIdFromDict:(UITouch*)event; + + +-(id)initWithView:(StreamView*)view; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Limelight/Input/NativeTouchHandler.m b/Limelight/Input/NativeTouchHandler.m new file mode 100644 index 00000000..3e6818b4 --- /dev/null +++ b/Limelight/Input/NativeTouchHandler.m @@ -0,0 +1,75 @@ +// +// NativeTouchHandler.m +// Moonlight +// +// Created by ZWM on 2024/5/14. +// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved. +// + +#import + +#import "NativeTouchHandler.h" + +#include + + +// Use a Dictionary to store UITouch object's memory address as key, and pointerId as value,字典存放UITouch对象地址和pointerId映射关系 +// pointerId will be generated from electronic noise, by arc4_random, pointerId,由随机噪声生成 +// Use a NSSet store pointerId, for quick repeition inquiry, NSSet存放活跃的pointerId合集,用于快速查找,以防重复. +static NSMutableDictionary *pointerIdDict; //pointerId Dict for native touch. +static NSMutableSet *pointerIdSet; //pointerIdSet for native touch. + +@implementation NativeTouchHandler { + StreamView* view; +} + +- (id)initWithView:(StreamView*)view { + self = [self init]; + self->view = view; + return self; +} + + ++ (NSMutableDictionary* )initializePointerIdDict { + return pointerIdDict = [NSMutableDictionary dictionary]; +} + ++ (NSMutableSet* )initializePointerIdSet { + return pointerIdSet = [NSMutableSet set]; +} + + +// 随机生成pointerId并填入NSDict和NSSet +// generate & populate pointerId into NSDict & NSSet, called in UITouchPhaseBegan ++ (void)populatePointerId:(UITouch*)event{ + uint64_t eventAddrValue = (uint64_t)event; + uint32_t randomPointerId = arc4random_uniform(UINT32_MAX); // generate pointerId from eletronic noise. + while(true){ + if([pointerIdSet containsObject:@(randomPointerId)]) randomPointerId = arc4random_uniform(UINT32_MAX); // in case of new pointerId collides with existing ones, generate again. + else{ // populate pointerId into NSDict & NSSet. + [pointerIdDict setObject:@(randomPointerId) forKey:@(eventAddrValue)]; + [pointerIdSet addObject:@(randomPointerId)]; + return; + } + } +} + +// remove pointerId in UITouchPhaseEnded condition ++ (void)removePointerId:(UITouch*)event{ + uint64_t eventAddrValue = (uint64_t)event; + NSNumber* pointerIdObj = [pointerIdDict objectForKey:@(eventAddrValue)]; + if(pointerIdObj != nil){ + [pointerIdSet removeObject:pointerIdObj]; + [pointerIdDict removeObjectForKey:@(eventAddrValue)]; + } +} + +// 从字典中获取UITouch事件对应的pointerId +// call this only when NSDcit & NSSet is up-to-date. ++ (uint32_t) retrievePointerIdFromDict:(UITouch*)event{ + return [[pointerIdDict objectForKey:@((uint64_t)event)] unsignedIntValue]; +} + + + +@end diff --git a/Limelight/Input/StreamView.m b/Limelight/Input/StreamView.m index 9df03eb3..ea4848b4 100644 --- a/Limelight/Input/StreamView.m +++ b/Limelight/Input/StreamView.m @@ -11,6 +11,7 @@ #import "DataManager.h" #import "ControllerSupport.h" #import "KeyboardSupport.h" +#import "NativeTouchHandler.h" #import "RelativeTouchHandler.h" #import "AbsoluteTouchHandler.h" #import "KeyboardInputField.h" @@ -23,15 +24,10 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; KeyboardInputField* keyInputField; BOOL isInputingText; NSMutableSet* keysDown; - UITapGestureRecognizer* _keyboardToggleTapRecognizer; - + uint32_t keyboardToggleFingers; float streamAspectRatio; - - // Use a Dictionary to store UITouch object's memory address as key, and pointerId as value,字典存放UITouch对象地址和pointerId映射关系 - // pointerId will be generated from electronic noise, by arc4_random, pointerId,由随机噪声生成 - // Use a NSSet store pointerId, for quick repeition inquiry, NSSet存放活跃的pointerId合集,用于快速查找,以防重复. - NSMutableDictionary *pointerIdDict; //pointerId Dict for native touch. - NSMutableSet *pointerIdSet; //pointerIdSet for native touch. + + // iOS 13.4 mouse support NSInteger lastMouseButtonMask; @@ -56,8 +52,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; - (void) setupStreamView:(ControllerSupport*)controllerSupport interactionDelegate:(id)interactionDelegate config:(StreamConfiguration*)streamConfig { - pointerIdDict = [NSMutableDictionary dictionary]; - pointerIdSet = [NSMutableSet set]; + NSMutableDictionary *pointerIdDict = [NativeTouchHandler initializePointerIdDict]; + NSMutableSet *pointerIdSet = [NativeTouchHandler initializePointerIdSet]; self->interactionDelegate = interactionDelegate; self->streamAspectRatio = (float)streamConfig.width / (float)streamConfig.height; @@ -71,12 +67,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; [keyInputField setAutocapitalizationType:UITextAutocapitalizationTypeNone]; [keyInputField setSpellCheckingType:UITextSpellCheckingTypeNo]; [self addSubview:keyInputField]; - _keyboardToggleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleKeyboard)]; - _keyboardToggleTapRecognizer.numberOfTouchesRequired = 2; - _keyboardToggleTapRecognizer.numberOfTapsRequired = 2; - _keyboardToggleTapRecognizer.delaysTouchesBegan = NO; - _keyboardToggleTapRecognizer.delaysTouchesEnded = NO; - [self addGestureRecognizer:_keyboardToggleTapRecognizer]; + keyboardToggleFingers = settings.keyboardToggleFingers.intValue; + //[self addGestureRecognizer:_keyboardToggleTapRecognizer]; #if TARGET_OS_TV // tvOS requires RelativeTouchHandler to manage Apple Remote input @@ -291,42 +283,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; return 90 - MIN(90, altitudeDegs); } -// 随机生成pointerId并填入NSDict和NSSet -// generate & populate pointerId into NSDict & NSSet, called in UITouchPhaseBegan -- (void)populatePointerId:(UITouch*)event{ - uint64_t eventAddrValue = (uint64_t)event; - uint32_t randomPointerId = arc4random_uniform(UINT32_MAX); // generate pointerId from eletronic noise. - while(true){ - if([pointerIdSet containsObject:@(randomPointerId)]) randomPointerId = arc4random_uniform(UINT32_MAX); // in case of new pointerId collides with existing ones, generate again. - else{ // populate pointerId into NSDict & NSSet. - [pointerIdDict setObject:@(randomPointerId) forKey:@(eventAddrValue)]; - [pointerIdSet addObject:@(randomPointerId)]; - return; - } - } -} - -// remove pointerId in UITouchPhaseEnded condition -- (void)removePointerId:(UITouch*)event{ - uint64_t eventAddrValue = (uint64_t)event; - NSNumber* pointerIdObj = [pointerIdDict objectForKey:@(eventAddrValue)]; - if(pointerIdObj != nil){ - [pointerIdSet removeObject:pointerIdObj]; - [pointerIdDict removeObjectForKey:@(eventAddrValue)]; - } -} - -// 从字典中获取UITouch事件对应的pointerId -// call this only when NSDcit & NSSet is up-to-date. -- (uint32_t) retrievePointerIdFromDict:(UITouch*)event{ - return [[pointerIdDict objectForKey:@((uint64_t)event)] unsignedIntValue]; -} - - (void)sendTouchEvent:(UITouch*)event touchType:(uint8_t)touchType{ CGPoint location = [self adjustCoordinatesForVideoArea:[event locationInView:self]]; CGSize videoSize = [self getVideoAreaSize]; - LiSendTouchEvent(touchType,[self retrievePointerIdFromDict:event],location.x / videoSize.width, location.y / videoSize.height,(event.force / event.maximumPossibleForce) / sin(event.altitudeAngle),0.0f, 0.0f,[self getRotationFromAzimuthAngle:[event azimuthAngleInView:self]]); + LiSendTouchEvent(touchType,[NativeTouchHandler retrievePointerIdFromDict:event],location.x / videoSize.width, location.y / videoSize.height,(event.force / event.maximumPossibleForce) / sin(event.altitudeAngle),0.0f, 0.0f,[self getRotationFromAzimuthAngle:[event azimuthAngleInView:self]]); } @@ -356,7 +317,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; switch (event.phase) { case UITouchPhaseBegan://开始触摸 type = LI_TOUCH_EVENT_DOWN; - [self populatePointerId:event]; //获取并记录pointerId + [NativeTouchHandler populatePointerId:event]; //获取并记录pointerId break; case UITouchPhaseMoved://移动 case UITouchPhaseStationary: @@ -369,12 +330,12 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; case UITouchPhaseCancelled://触摸取消 type = LI_TOUCH_EVENT_CANCEL; [self sendTouchEvent:event touchType:type]; //先发送,再删除 - [self removePointerId:event]; //删除pointerId + [NativeTouchHandler removePointerId:event]; //删除pointerId return; case UITouchPhaseEnded://触摸结束 type = LI_TOUCH_EVENT_UP; [self sendTouchEvent:event touchType:type]; //先发送,再删除 - [self removePointerId:event]; //删除pointerId + [NativeTouchHandler removePointerId:event]; //删除pointerId return; default: return; @@ -519,7 +480,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; // is triggered. [touchHandler touchesBegan:touches withEvent:event]; - //if ([[event allTouches] count] == 3) [self toggleKeyboard]; + if ([[event allTouches] count] == keyboardToggleFingers) [self toggleKeyboard]; } } diff --git a/Limelight/ViewControllers/CustomEdgeSwipeGestureRecognizer.m b/Limelight/ViewControllers/CustomEdgeSwipeGestureRecognizer.m index af4214c0..874d2523 100644 --- a/Limelight/ViewControllers/CustomEdgeSwipeGestureRecognizer.m +++ b/Limelight/ViewControllers/CustomEdgeSwipeGestureRecognizer.m @@ -24,14 +24,14 @@ static CGFloat EDGE_TOLERANCE_POINTS = 50.0f; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - [super touchesBegan:touches withEvent:event]; + // [super touchesBegan:touches withEvent:event]; UITouch *touch = [touches anyObject]; capturedUITouch = touch; _startPointX = [capturedUITouch locationInView:self.view].x; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - [super touchesEnded:touches withEvent:event]; + // [super touchesEnded:touches withEvent:event]; if([touches containsObject:capturedUITouch]){ CGFloat _endPointX = [capturedUITouch locationInView:self.view].x;