diff --git a/Limelight/Database/DataManager.h b/Limelight/Database/DataManager.h index eb0d5f5b..be6aba03 100644 --- a/Limelight/Database/DataManager.h +++ b/Limelight/Database/DataManager.h @@ -34,7 +34,8 @@ useFramePacing:(BOOL)useFramePacing enableHdr:(BOOL)enableHdr btMouseSupport:(BOOL)btMouseSupport - absoluteTouchMode:(BOOL)absoluteTouchMode + // absoluteTouchMode:(BOOL)absoluteTouchMode + touchMode:(NSInteger)touchMode statsOverlay:(BOOL)statsOverlay; - (NSArray*) getHosts; diff --git a/Limelight/Database/DataManager.m b/Limelight/Database/DataManager.m index 6c6191c7..22436f09 100644 --- a/Limelight/Database/DataManager.m +++ b/Limelight/Database/DataManager.m @@ -73,7 +73,8 @@ useFramePacing:(BOOL)useFramePacing enableHdr:(BOOL)enableHdr btMouseSupport:(BOOL)btMouseSupport - absoluteTouchMode:(BOOL)absoluteTouchMode + // absoluteTouchMode:(BOOL)absoluteTouchMode + touchMode:(NSInteger)touchMode statsOverlay:(BOOL)statsOverlay { [_managedObjectContext performBlockAndWait:^{ @@ -99,7 +100,8 @@ settingsToSave.useFramePacing = useFramePacing; settingsToSave.enableHdr = enableHdr; settingsToSave.btMouseSupport = btMouseSupport; - settingsToSave.absoluteTouchMode = absoluteTouchMode; + // settingsToSave.absoluteTouchMode = absoluteTouchMode; + settingsToSave.touchMode = [NSNumber numberWithInteger:(uint16_t)touchMode]; settingsToSave.statsOverlay = statsOverlay; [self saveData]; diff --git a/Limelight/Database/TemporarySettings.h b/Limelight/Database/TemporarySettings.h index d55a00f7..ac3990a6 100644 --- a/Limelight/Database/TemporarySettings.h +++ b/Limelight/Database/TemporarySettings.h @@ -7,6 +7,10 @@ // #import "Settings+CoreDataClass.h" +#define RELATIVE_TOUCH_MODE 0 +#define NATIVE_TOUCH_MODE 1 +#define ABSOLUTE_TOUCH_MODE 2 + @interface TemporarySettings : NSObject @@ -37,7 +41,8 @@ @property (nonatomic) BOOL optimizeGames; @property (nonatomic) BOOL enableHdr; @property (nonatomic) BOOL btMouseSupport; -@property (nonatomic) BOOL absoluteTouchMode; +// @property (nonatomic) BOOL absoluteTouchMode; +@property (nonatomic, retain) NSNumber * touchMode; @property (nonatomic) BOOL statsOverlay; @property (nonatomic) BOOL liftStreamViewForKeyboard; @property (nonatomic) BOOL showKeyboardToolbar; diff --git a/Limelight/Database/TemporarySettings.m b/Limelight/Database/TemporarySettings.m index 08657aa6..5264fb0a 100644 --- a/Limelight/Database/TemporarySettings.m +++ b/Limelight/Database/TemporarySettings.m @@ -83,7 +83,8 @@ self.swapABXYButtons = settings.swapABXYButtons; self.onscreenControls = settings.onscreenControls; self.btMouseSupport = settings.btMouseSupport; - self.absoluteTouchMode = settings.absoluteTouchMode; + // self.absoluteTouchMode = settings.absoluteTouchMode; + self.touchMode = settings.touchMode; self.statsOverlay = settings.statsOverlay; self.keyboardToggleFingers = settings.keyboardToggleFingers; self.swipeExitScreenEdge = settings.swipeExitScreenEdge; diff --git a/Limelight/Input/AbsoluteTouchHandler.m b/Limelight/Input/AbsoluteTouchHandler.m index 0066283b..b9b8c59c 100644 --- a/Limelight/Input/AbsoluteTouchHandler.m +++ b/Limelight/Input/AbsoluteTouchHandler.m @@ -38,85 +38,83 @@ return self; } -//- (void)onLongPressStart:(NSTimer*)timer { -// // Raise the left click and start a right click -// LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); -// LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT); -//} -// -//- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { -// // Ignore touch down events with more than one finger -// return; -// if ([[event allTouches] count] > 1) { -// return; -// } -// -// UITouch* touch = [touches anyObject]; -// CGPoint touchLocation = [touch locationInView:view]; -// -// // Don't reposition for finger down events within the deadzone. This makes double-clicking easier. -// if (touch.timestamp - lastTouchUp.timestamp > DOUBLE_TAP_DEAD_ZONE_DELAY || -// sqrt(pow((touchLocation.x / view.bounds.size.width) - (lastTouchUpLocation.x / view.bounds.size.width), 2) + -// pow((touchLocation.y / view.bounds.size.height) - (lastTouchUpLocation.y / view.bounds.size.height), 2)) > DOUBLE_TAP_DEAD_ZONE_DELTA) { -// [view updateCursorLocation:touchLocation isMouse:NO]; -// } -// -// // Press the left button down -// LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT); -// -// // Start the long press timer -// longPressTimer = [NSTimer scheduledTimerWithTimeInterval:LONG_PRESS_ACTIVATION_DELAY -// target:self -// selector:@selector(onLongPressStart:) -// userInfo:nil -// repeats:NO]; -// -// lastTouchDown = touch; -// lastTouchDownLocation = touchLocation; -//} -// -//- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { -// // Ignore touch move events with more than one finger -// return; -// if ([[event allTouches] count] > 1) { -// return; -// } -// -// UITouch* touch = [touches anyObject]; -// CGPoint touchLocation = [touch locationInView:view]; -// -// if (sqrt(pow((touchLocation.x / view.bounds.size.width) - (lastTouchDownLocation.x / view.bounds.size.width), 2) + -// pow((touchLocation.y / view.bounds.size.height) - (lastTouchDownLocation.y / view.bounds.size.height), 2)) > LONG_PRESS_ACTIVATION_DELTA) { -// // Moved too far since touch down. Cancel the long press timer. -// [longPressTimer invalidate]; -// longPressTimer = nil; -// } -// -// [view updateCursorLocation:[[touches anyObject] locationInView:view] isMouse:NO]; -//} -// -//- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { -// // Only fire this logic if all touches have ended -// if ([[event allTouches] count] == [touches count]) { -// // Cancel the long press timer -// [longPressTimer invalidate]; -// longPressTimer = nil; -// -// // Left button up on finger up -// LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); -// -// // Raise right button too in case we triggered a long press gesture -// LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT); -// -// // Remember this last touch for touch-down deadzoning -// lastTouchUp = [touches anyObject]; -// lastTouchUpLocation = [lastTouchUp locationInView:view]; -// } -//} -// -//- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { -// // Treat this as a normal touchesEnded event -// [self touchesEnded:touches withEvent:event]; -//} +- (void)onLongPressStart:(NSTimer*)timer { + // Raise the left click and start a right click + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT); +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + // Ignore touch down events with more than one finger + if ([[event allTouches] count] > 1) { + return; + } + + UITouch* touch = [touches anyObject]; + CGPoint touchLocation = [touch locationInView:view]; + + // Don't reposition for finger down events within the deadzone. This makes double-clicking easier. + if (touch.timestamp - lastTouchUp.timestamp > DOUBLE_TAP_DEAD_ZONE_DELAY || + sqrt(pow((touchLocation.x / view.bounds.size.width) - (lastTouchUpLocation.x / view.bounds.size.width), 2) + + pow((touchLocation.y / view.bounds.size.height) - (lastTouchUpLocation.y / view.bounds.size.height), 2)) > DOUBLE_TAP_DEAD_ZONE_DELTA) { + [view updateCursorLocation:touchLocation isMouse:NO]; + } + + // Press the left button down + LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT); + + // Start the long press timer + longPressTimer = [NSTimer scheduledTimerWithTimeInterval:LONG_PRESS_ACTIVATION_DELAY + target:self + selector:@selector(onLongPressStart:) + userInfo:nil + repeats:NO]; + + lastTouchDown = touch; + lastTouchDownLocation = touchLocation; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + // Ignore touch move events with more than one finger + if ([[event allTouches] count] > 1) { + return; + } + + UITouch* touch = [touches anyObject]; + CGPoint touchLocation = [touch locationInView:view]; + + if (sqrt(pow((touchLocation.x / view.bounds.size.width) - (lastTouchDownLocation.x / view.bounds.size.width), 2) + + pow((touchLocation.y / view.bounds.size.height) - (lastTouchDownLocation.y / view.bounds.size.height), 2)) > LONG_PRESS_ACTIVATION_DELTA) { + // Moved too far since touch down. Cancel the long press timer. + [longPressTimer invalidate]; + longPressTimer = nil; + } + + [view updateCursorLocation:[[touches anyObject] locationInView:view] isMouse:NO]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + // Only fire this logic if all touches have ended + if ([[event allTouches] count] == [touches count]) { + // Cancel the long press timer + [longPressTimer invalidate]; + longPressTimer = nil; + + // Left button up on finger up + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT); + + // Raise right button too in case we triggered a long press gesture + LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT); + + // Remember this last touch for touch-down deadzoning + lastTouchUp = [touches anyObject]; + lastTouchUpLocation = [lastTouchUp locationInView:view]; + } +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + // Treat this as a normal touchesEnded event + [self touchesEnded:touches withEvent:event]; +} @end diff --git a/Limelight/Input/ControllerSupport.m b/Limelight/Input/ControllerSupport.m index 19d13157..0b2a82ca 100644 --- a/Limelight/Input/ControllerSupport.m +++ b/Limelight/Input/ControllerSupport.m @@ -1061,7 +1061,7 @@ static const double MOUSE_SPEED_DIVISOR = 1.25; // Even if no gamepads are present, we will always count one if OSC is enabled, // or it's set to auto and no keyboard or mouse is present. Absolute touch mode // disables the OSC. - if (level != OnScreenControlsLevelOff && (![ControllerSupport hasKeyboardOrMouse] || level != OnScreenControlsLevelAuto) && !settings.absoluteTouchMode) { + if (level != OnScreenControlsLevelOff && (![ControllerSupport hasKeyboardOrMouse] || level != OnScreenControlsLevelAuto) && (settings.touchMode.intValue == RELATIVE_TOUCH_MODE)) { mask |= 0x1; } diff --git a/Limelight/Input/NativeTouchHandler.h b/Limelight/Input/NativeTouchHandler.h index 1ebb7b79..dea03c26 100644 --- a/Limelight/Input/NativeTouchHandler.h +++ b/Limelight/Input/NativeTouchHandler.h @@ -2,33 +2,22 @@ // NativeTouchHandler.h // Moonlight // -// Created by ZWM on 2024/5/14. +// Created by ZWM on 2024/6/16. // Copyright © 2024 Moonlight Game Streaming Project. All rights reserved. // + #import "StreamView.h" +#import "DataManager.h" +#include NS_ASSUME_NONNULL_BEGIN -@interface TouchPointer : NSObject +@interface NativeTouchHandler : UIResponder -+ (void)initContextWith:(StreamView *)view; -+ (void)populatePointerId:(UITouch*)touch; -+ (void)removePointerId:(UITouch*)touch; -+ (uint32_t) retrievePointerIdFromDict:(UITouch*)touch; -+ (void)setPointerVelocityDivider:(CGFloat)dividerLocation; -+ (void)setPointerVelocityFactor:(CGFloat)velocityFactor; -+ (void)populatePointerObjIntoDict:(UITouch*)touch; -+ (void)removePointerObjFromDict:(UITouch*)touch; -+ (void)updatePointerObjInDict:(UITouch *)touch; -+ (CGPoint)selectCoordsFor:(UITouch *)touch; +- (id)initWith:(StreamView*)view and:(TemporarySettings*)settings; -- (instancetype)initWith:(UITouch *)touch; @end - - - NS_ASSUME_NONNULL_END - diff --git a/Limelight/Input/NativeTouchHandler.m b/Limelight/Input/NativeTouchHandler.m index f2c2461a..a2954ef1 100644 --- a/Limelight/Input/NativeTouchHandler.m +++ b/Limelight/Input/NativeTouchHandler.m @@ -2,134 +2,146 @@ // NativeTouchHandler.m // Moonlight // -// Created by ZWM on 2024/5/14. +// Created by Admin on 2024/6/16. // Copyright © 2024 Moonlight Game Streaming Project. All rights reserved. // #import #import "NativeTouchHandler.h" +#import "NativeTouchPointer.h" +#import "StreamView.h" +#import "DataManager.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. -static NSMutableDictionary *pointerDict; -static CGFloat pointerVelocityFactor; -static CGFloat pointerVelocityDivider; -static CGFloat pointerVelocityDividerLocation; - -StreamView *streamView; - -@implementation TouchPointer{ - CGPoint initialPoint; - CGPoint latestPoint; - CGPoint previousPoint; - CGPoint latestRelativePoint; - CGPoint previousRelativePoint; - CGFloat velocityX; - CGFloat velocityY; -} - -+ (void)setPointerVelocityDivider:(CGFloat)dividerLocation{ - pointerVelocityDivider = dividerLocation; -} - -+ (void)setPointerVelocityFactor:(CGFloat)velocityFactor{ - pointerVelocityFactor = velocityFactor; +@implementation NativeTouchHandler { + StreamView* streamView; + bool activateCoordSelector; } -- (instancetype)initWith:(UITouch *)touch{ - self = [self init]; - self->initialPoint = [touch locationInView:streamView]; - self->latestPoint = self->initialPoint; - self->latestRelativePoint = self->initialPoint; - return self; - } - -- (void)updatePointerCoords:(UITouch *)touch{ - previousPoint = latestPoint; - latestPoint = [touch locationInView:streamView]; - velocityX = latestPoint.x - previousPoint.x; - velocityY = latestPoint.y - previousPoint.y; - previousRelativePoint = latestRelativePoint; - latestRelativePoint.x = previousRelativePoint.x + pointerVelocityFactor * velocityX; - latestRelativePoint.y = previousRelativePoint.y + pointerVelocityFactor * velocityY; -} - -+ (void)initContextWith:(StreamView *)view{ - streamView = view; - pointerIdDict = [NSMutableDictionary dictionary]; - pointerIdSet = [NSMutableSet set]; - pointerDict = [NSMutableDictionary dictionary]; - pointerVelocityDividerLocation = CGRectGetWidth([[UIScreen mainScreen] bounds]) * pointerVelocityDivider; - NSLog(@"pointerVelocityDivider: %.2f", pointerVelocityDivider); - NSLog(@"pointerVelocityDividerLocation: %.2f", pointerVelocityDividerLocation); -} - -+ (void)populatePointerObjIntoDict:(UITouch*)touch{ - [pointerDict setObject:[[TouchPointer alloc] initWith:touch] forKey:@((uint64_t)touch)]; -} - -+ (void)removePointerObjFromDict:(UITouch*)touch{ - uint64_t eventAddrValue = (uint64_t)touch; - TouchPointer* pointer = [pointerDict objectForKey:@(eventAddrValue)]; - if(pointer != nil){ - [pointerDict removeObjectForKey:@(eventAddrValue)]; - } - -} - -+ (void)updatePointerObjInDict:(UITouch *)touch{ - [[pointerDict objectForKey:@((uint64_t)touch)] updatePointerCoords:touch]; +- (id)initWith:(StreamView*)view and:(TemporarySettings*)settings{ + self = [self init]; + self->streamView = view; + activateCoordSelector = (settings.pointerVelocityModeDivider.floatValue != 1.0); + [NativeTouchPointer setPointerVelocityDivider:settings.pointerVelocityModeDivider.floatValue]; + [NativeTouchPointer setPointerVelocityFactor:settings.touchPointerVelocityFactor.floatValue]; + [NativeTouchPointer initContextWith:streamView]; + + return self; } -+ (CGPoint)selectCoordsFor:(UITouch *)touch{ - TouchPointer *pointer = [pointerDict objectForKey:@((uint64_t)touch)]; - if((pointer -> initialPoint).x > pointerVelocityDividerLocation){ //if first contact coords locates on the right side of divider. - return pointer->latestRelativePoint; - } - return [touch locationInView:streamView]; -} - - - -// 随机生成pointerId并填入NSDict和NSSet -// generate & populate pointerId into NSDict & NSSet, called in UITouchPhaseBegan -+ (void)populatePointerId:(UITouch*)touch{ - uint64_t eventAddrValue = (uint64_t)touch; - uint32_t randomPointerId = arc4random_uniform(UINT32_MAX); // generate pointerId from eletronic noise. - for(;;){ - 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)]; +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + +#if !TARGET_OS_TV + if (@available(iOS 13.4, *)) { + for (UITouch* touch in touches) [self handleUITouch:touch index:0];// Native touch (absoluteTouch) first! return; } +#endif } + + + +- (void)sendTouchEvent:(UITouch*)event touchType:(uint8_t)touchType{ + CGPoint targetCoords; + if(activateCoordSelector && event.phase == UITouchPhaseMoved) targetCoords = [NativeTouchPointer selectCoordsFor:event]; // coordinates of touch pointer replaced to relative ones here. + else targetCoords = [event locationInView:streamView]; + CGPoint location = [streamView adjustCoordinatesForVideoArea:targetCoords]; + CGSize videoSize = [streamView getVideoAreaSize]; + LiSendTouchEvent(touchType,[NativeTouchPointer retrievePointerIdFromDict:event],location.x / videoSize.width, location.y / videoSize.height,(event.force / event.maximumPossibleForce) / sin(event.altitudeAngle),0.0f, 0.0f,[streamView getRotationFromAzimuthAngle:[event azimuthAngleInView:streamView]]); } - -// remove pointerId in UITouchPhaseEnded condition -+ (void)removePointerId:(UITouch*)touch{ - uint64_t eventAddrValue = (uint64_t)touch; - NSNumber* pointerIdObj = [pointerIdDict objectForKey:@(eventAddrValue)]; - if(pointerIdObj != nil){ - [pointerIdSet removeObject:pointerIdObj]; - [pointerIdDict removeObjectForKey:@(eventAddrValue)]; +- (void)handleUITouch:(UITouch*)event index:(int)index{ + uint8_t type; + //BOOL pointerVelocityScaleEnabled = (settings.pointerVelocityModeDivider.floatValue != 1.0); // when the divider is 1.0, means 0% of screen shall pass velocity-scaled pointer to sunshine. + // NSLog(@"handleUITouch %ld,%d",(long)event.phase,(uint32_t)event); +//#define LI_TOUCH_EVENT_HOVER 0x00 +//#define LI_TOUCH_EVENT_DOWN 0x01 +//#define LI_TOUCH_EVENT_UP 0x02 +//#define LI_TOUCH_EVENT_MOVE 0x03 +//#define LI_TOUCH_EVENT_CANCEL 0x04 +//#define LI_TOUCH_EVENT_BUTTON_ONLY 0x05 +//#define LI_TOUCH_EVENT_HOVER_LEAVE 0x06 +//#define LI_TOUCH_EVENT_CANCEL_ALL 0x07 +//#define LI_ROT_UNKNOWN 0xFFFF + +// UITouchPhaseBegan, // whenever a finger touches the surface. +// UITouchPhaseMoved, // whenever a finger moves on the surface. +// UITouchPhaseStationary, // whenever a finger is touching the surface but hasn't moved since the previous event. +// UITouchPhaseEnded, // whenever a finger leaves the surface. +// UITouchPhaseCancelled, // whenever a touch doesn't end but we need to stop tracking (e.g. putting device to face) +// UITouchPhaseRegionEntered API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos), // whenever a touch is entering the region of a user interface +// UITouchPhaseRegionMoved API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos), // when a touch is inside the region of a user interface, but hasn’t yet made contact or left the region +// UITouchPhaseRegionExited API_AVAILABLE(ios(13.4), tvos(13.4)) + + switch (event.phase) { + case UITouchPhaseBegan://开始触摸 + type = LI_TOUCH_EVENT_DOWN; + [NativeTouchPointer populatePointerId:event]; //获取并记录pointerId + if(activateCoordSelector) [NativeTouchPointer populatePointerObjIntoDict:event]; + break; + case UITouchPhaseMoved://移动 + case UITouchPhaseStationary: + type = LI_TOUCH_EVENT_MOVE; + if(activateCoordSelector) [NativeTouchPointer updatePointerObjInDict:event]; + break; + case UITouchPhaseEnded://触摸结束 + type = LI_TOUCH_EVENT_UP; + [self sendTouchEvent:event touchType:type]; //先发送,再删除 + [NativeTouchPointer removePointerId:event]; //删除pointerId + if(activateCoordSelector) [NativeTouchPointer removePointerObjFromDict:event]; + return; + case UITouchPhaseCancelled://触摸取消 + type = LI_TOUCH_EVENT_CANCEL; + [self sendTouchEvent:event touchType:type]; //先发送,再删除 + [NativeTouchPointer removePointerId:event]; //删除pointerId + if(activateCoordSelector) [NativeTouchPointer removePointerObjFromDict:event]; + return; + case UITouchPhaseRegionEntered://停留 + case UITouchPhaseRegionMoved://停留 + type = LI_TOUCH_EVENT_HOVER; + break; + default: + return; } + [self sendTouchEvent:event touchType:type]; } -// 从字典中获取UITouch事件对应的pointerId -// call this only when NSDcit & NSSet is up-to-date. -+ (uint32_t) retrievePointerIdFromDict:(UITouch*)touch{ - return [[pointerIdDict objectForKey:@((uint64_t)touch)] unsignedIntValue]; + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { +#if !TARGET_OS_TV + if (@available(iOS 13.4, *)) { + for (UITouch* touch in touches) [self handleUITouch:touch index:0];// Native touch (absoluteTouch) first! + return; + // NSLog(@"touchesMoved - allTouches %lu, pointerSet count %lu",[[event allTouches] count], [pointerIdSet count]); + } +#endif +} + + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { +#if !TARGET_OS_TV + if (@available(iOS 13.4, *)) { + for (UITouch* touch in touches) [self handleUITouch:touch index:0];// Native touch (absoluteTouch) first! + return; + // NSLog(@"touchesEnded - allTouches %lu, pointerSet count %lu",[[event allTouches] count], [pointerIdSet count]); + } +#endif +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { +#if !TARGET_OS_TV + if (@available(iOS 13.4, *)) { + for (UITouch* touch in touches) [self handleUITouch:touch index:0];// Native touch (absoluteTouch) first! + return; + } + // NSLog(@"touchesCancelled - allTouches %lu, pointerSet count %lu",[[event allTouches] count], [pointerIdSet count]); +#endif } diff --git a/Limelight/Input/NativeTouchPointer.h b/Limelight/Input/NativeTouchPointer.h new file mode 100644 index 00000000..37959e7e --- /dev/null +++ b/Limelight/Input/NativeTouchPointer.h @@ -0,0 +1,34 @@ +// +// NativeTouchPointer.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 NativeTouchPointer : NSObject + ++ (void)initContextWith:(StreamView *)view; ++ (void)populatePointerId:(UITouch*)touch; ++ (void)removePointerId:(UITouch*)touch; ++ (uint32_t) retrievePointerIdFromDict:(UITouch*)touch; ++ (void)setPointerVelocityDivider:(CGFloat)dividerLocation; ++ (void)setPointerVelocityFactor:(CGFloat)velocityFactor; ++ (void)populatePointerObjIntoDict:(UITouch*)touch; ++ (void)removePointerObjFromDict:(UITouch*)touch; ++ (void)updatePointerObjInDict:(UITouch *)touch; ++ (CGPoint)selectCoordsFor:(UITouch *)touch; + + +- (instancetype)initWith:(UITouch *)touch; +@end + + + + +NS_ASSUME_NONNULL_END + diff --git a/Limelight/Input/NativeTouchPointer.m b/Limelight/Input/NativeTouchPointer.m new file mode 100644 index 00000000..d7a00c1e --- /dev/null +++ b/Limelight/Input/NativeTouchPointer.m @@ -0,0 +1,141 @@ +// +// NativeTouchPointer.m +// Moonlight +// +// Created by ZWM on 2024/5/14. +// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved. +// + +#import +#import "NativeTouchPointer.h" +#include + +// native touch pointer class deals with: +// generating pointerId randomly, pointerId Dic & set management +// pointer instance with coordinate manipulation. + + +// 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. +static NSMutableDictionary *pointerDict; + +static CGFloat pointerVelocityFactor; +static CGFloat pointerVelocityDivider; +static CGFloat pointerVelocityDividerLocation; + +StreamView *streamView; + +@implementation NativeTouchPointer{ + CGPoint initialPoint; + CGPoint latestPoint; + CGPoint previousPoint; + CGPoint latestRelativePoint; + CGPoint previousRelativePoint; + CGFloat velocityX; + CGFloat velocityY; +} + ++ (void)setPointerVelocityDivider:(CGFloat)dividerLocation{ + pointerVelocityDivider = dividerLocation; +} + ++ (void)setPointerVelocityFactor:(CGFloat)velocityFactor{ + pointerVelocityFactor = velocityFactor; +} + + +- (instancetype)initWith:(UITouch *)touch{ + self = [self init]; + self->initialPoint = [touch locationInView:streamView]; + self->latestPoint = self->initialPoint; + self->latestRelativePoint = self->initialPoint; + return self; + } + +- (void)updatePointerCoords:(UITouch *)touch{ + previousPoint = latestPoint; + latestPoint = [touch locationInView:streamView]; + velocityX = latestPoint.x - previousPoint.x; + velocityY = latestPoint.y - previousPoint.y; + previousRelativePoint = latestRelativePoint; + latestRelativePoint.x = previousRelativePoint.x + pointerVelocityFactor * velocityX; + latestRelativePoint.y = previousRelativePoint.y + pointerVelocityFactor * velocityY; +} + ++ (void)initContextWith:(StreamView *)view{ + streamView = view; + pointerIdDict = [NSMutableDictionary dictionary]; + pointerIdSet = [NSMutableSet set]; + pointerDict = [NSMutableDictionary dictionary]; + pointerVelocityDividerLocation = CGRectGetWidth([[UIScreen mainScreen] bounds]) * pointerVelocityDivider; + NSLog(@"pointerVelocityDivider: %.2f", pointerVelocityDivider); + NSLog(@"pointerVelocityDividerLocation: %.2f", pointerVelocityDividerLocation); +} + ++ (void)populatePointerObjIntoDict:(UITouch*)touch{ + [pointerDict setObject:[[NativeTouchPointer alloc] initWith:touch] forKey:@((uint64_t)touch)]; +} + ++ (void)removePointerObjFromDict:(UITouch*)touch{ + uint64_t eventAddrValue = (uint64_t)touch; + NativeTouchPointer* pointer = [pointerDict objectForKey:@(eventAddrValue)]; + if(pointer != nil){ + [pointerDict removeObjectForKey:@(eventAddrValue)]; + } + +} + ++ (void)updatePointerObjInDict:(UITouch *)touch{ + [[pointerDict objectForKey:@((uint64_t)touch)] updatePointerCoords:touch]; +} + + ++ (CGPoint)selectCoordsFor:(UITouch *)touch{ + NativeTouchPointer *pointer = [pointerDict objectForKey:@((uint64_t)touch)]; + if((pointer -> initialPoint).x > pointerVelocityDividerLocation){ //if first contact coords locates on the right side of divider. + return pointer->latestRelativePoint; + } + return [touch locationInView:streamView]; +} + + + +// 随机生成pointerId并填入NSDict和NSSet +// generate & populate pointerId into NSDict & NSSet, called in UITouchPhaseBegan ++ (void)populatePointerId:(UITouch*)touch{ + uint64_t eventAddrValue = (uint64_t)touch; + uint32_t randomPointerId = arc4random_uniform(UINT32_MAX); // generate pointerId from eletronic noise. + for(;;){ + 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*)touch{ + uint64_t eventAddrValue = (uint64_t)touch; + 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*)touch{ + return [[pointerIdDict objectForKey:@((uint64_t)touch)] unsignedIntValue]; +} + + + +@end diff --git a/Limelight/Input/StreamView.h b/Limelight/Input/StreamView.h index 9f500101..40d940e1 100644 --- a/Limelight/Input/StreamView.h +++ b/Limelight/Input/StreamView.h @@ -28,6 +28,10 @@ interactionDelegate:(id)interactionDelegate config:(StreamConfiguration*)streamConfig; - (void) showOnScreenControls; +- (CGSize) getVideoAreaSize; +- (CGPoint) adjustCoordinatesForVideoArea:(CGPoint)point; +- (uint16_t)getRotationFromAzimuthAngle:(float)azimuthAngle; + - (OnScreenControlsLevel) getCurrentOscState; #if !TARGET_OS_TV diff --git a/Limelight/Input/StreamView.m b/Limelight/Input/StreamView.m index 9afc56ad..b244d84e 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 "NativeTouchPointer.h" #import "NativeTouchHandler.h" #import "RelativeTouchHandler.h" #import "AbsoluteTouchHandler.h" @@ -92,21 +93,24 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; // tvOS requires RelativeTouchHandler to manage Apple Remote input self->touchHandler = [[RelativeTouchHandler alloc] initWithView:self]; #else - // iOS uses RelativeTouchHandler or AbsoluteTouchHandler depending on user preference - if (settings.absoluteTouchMode) { - // self->touchHandler = [[AbsoluteTouchHandler alloc] initWithView:self]; - [TouchPointer setPointerVelocityDivider:settings.pointerVelocityModeDivider.floatValue]; - [TouchPointer setPointerVelocityFactor:settings.touchPointerVelocityFactor.floatValue]; - [TouchPointer initContextWith:self]; - } - else { - self->touchHandler = [[RelativeTouchHandler alloc] initWithView:self]; + // iOS uses touch Mode depending on user preference + + + switch (settings.touchMode.intValue) { + case NATIVE_TOUCH_MODE: + self->touchHandler = [[NativeTouchHandler alloc] initWith:self and:settings]; break; + case RELATIVE_TOUCH_MODE: + self->touchHandler = [[RelativeTouchHandler alloc] initWithView:self]; break; + case ABSOLUTE_TOUCH_MODE: + self->touchHandler = [[AbsoluteTouchHandler alloc] initWithView:self]; break; + default: + break; } onScreenControls = [[OnScreenControls alloc] initWithView:self controllerSup:controllerSupport streamConfig:streamConfig]; OnScreenControlsLevel level = (OnScreenControlsLevel)[settings.onscreenControls integerValue]; - if (settings.absoluteTouchMode) { - Log(LOG_I, @"On-screen controls disabled in absolute touch mode"); + if (settings.touchMode.intValue != RELATIVE_TOUCH_MODE) { + Log(LOG_I, @"On-screen controls disabled in non-relative touch mode"); [onScreenControls setLevel:OnScreenControlsLevelOff]; } else if (level == OnScreenControlsLevelAuto) { @@ -335,11 +339,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; - (void)sendTouchEvent:(UITouch*)event touchType:(uint8_t)touchType{ CGPoint targetCoords; - if(settings.pointerVelocityModeDivider.floatValue != 1.0 && event.phase == UITouchPhaseMoved) targetCoords = [TouchPointer selectCoordsFor:event]; // coordinates of touch pointer replaced to relative ones here. + if(settings.pointerVelocityModeDivider.floatValue != 1.0 && event.phase == UITouchPhaseMoved) targetCoords = [NativeTouchPointer selectCoordsFor:event]; // coordinates of touch pointer replaced to relative ones here. else targetCoords = [event locationInView:self]; CGPoint location = [self adjustCoordinatesForVideoArea:targetCoords]; CGSize videoSize = [self getVideoAreaSize]; - LiSendTouchEvent(touchType,[TouchPointer 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,[NativeTouchPointer 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]]); } @@ -369,25 +373,25 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; switch (event.phase) { case UITouchPhaseBegan://开始触摸 type = LI_TOUCH_EVENT_DOWN; - [TouchPointer populatePointerId:event]; //获取并记录pointerId - if(settings.pointerVelocityModeDivider.floatValue != 1.0) [TouchPointer populatePointerObjIntoDict:event]; + [NativeTouchPointer populatePointerId:event]; //获取并记录pointerId + if(settings.pointerVelocityModeDivider.floatValue != 1.0) [NativeTouchPointer populatePointerObjIntoDict:event]; break; case UITouchPhaseMoved://移动 case UITouchPhaseStationary: type = LI_TOUCH_EVENT_MOVE; - if(settings.pointerVelocityModeDivider.floatValue != 1.0) [TouchPointer updatePointerObjInDict:event]; + if(settings.pointerVelocityModeDivider.floatValue != 1.0) [NativeTouchPointer updatePointerObjInDict:event]; break; case UITouchPhaseEnded://触摸结束 type = LI_TOUCH_EVENT_UP; [self sendTouchEvent:event touchType:type]; //先发送,再删除 - [TouchPointer removePointerId:event]; //删除pointerId - if(settings.pointerVelocityModeDivider.floatValue != 1.0) [TouchPointer removePointerObjFromDict:event]; + [NativeTouchPointer removePointerId:event]; //删除pointerId + if(settings.pointerVelocityModeDivider.floatValue != 1.0) [NativeTouchPointer removePointerObjFromDict:event]; return; case UITouchPhaseCancelled://触摸取消 type = LI_TOUCH_EVENT_CANCEL; [self sendTouchEvent:event touchType:type]; //先发送,再删除 - [TouchPointer removePointerId:event]; //删除pointerId - if(settings.pointerVelocityModeDivider.floatValue != 1.0) [TouchPointer removePointerObjFromDict:event]; + [NativeTouchPointer removePointerId:event]; //删除pointerId + if(settings.pointerVelocityModeDivider.floatValue != 1.0) [NativeTouchPointer removePointerObjFromDict:event]; return; case UITouchPhaseRegionEntered://停留 case UITouchPhaseRegionMoved://停留 @@ -480,29 +484,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; #endif - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - #if !TARGET_OS_TV if (@available(iOS 13.4, *)) { - // NSArray *arr1 = [[event allTouches] allObjects]; - //// NSLog(@"touchesBegan - %lu",[[event allTouches] count]); - //// NSLog(@"touchesBegan - %lu",arr1.count); - // - // for(int i=0;i - @@ -42,6 +41,7 @@ + diff --git a/Limelight/ViewControllers/SettingsViewController.m b/Limelight/ViewControllers/SettingsViewController.m index 2b58f407..76eeae6b 100644 --- a/Limelight/ViewControllers/SettingsViewController.m +++ b/Limelight/ViewControllers/SettingsViewController.m @@ -264,7 +264,8 @@ BOOL isCustomResolution(CGSize res) { [self.hdrSelector setSelectedSegmentIndex:currentSettings.enableHdr ? 1 : 0]; } - [self.touchModeSelector setSelectedSegmentIndex:currentSettings.absoluteTouchMode ? 1 : 0]; + // [self.touchModeSelector setSelectedSegmentIndex:currentSettings.absoluteTouchMode ? 1 : 0]; + [self.touchModeSelector setSelectedSegmentIndex:currentSettings.touchMode.intValue]; //Load old touchMode setting [self.touchModeSelector addTarget:self action:@selector(touchModeChanged) forControlEvents:UIControlEventValueChanged]; [self.statsOverlaySelector setSelectedSegmentIndex:currentSettings.statsOverlay ? 1 : 0]; [self.btMouseSelector setSelectedSegmentIndex:currentSettings.btMouseSupport ? 1 : 0]; @@ -280,7 +281,7 @@ BOOL isCustomResolution(CGSize res) { [self.framerateSelector setSelectedSegmentIndex:framerate]; [self.framerateSelector addTarget:self action:@selector(updateBitrate) forControlEvents:UIControlEventValueChanged]; [self.onscreenControlSelector setSelectedSegmentIndex:onscreenControls]; - [self.onscreenControlSelector setEnabled:!currentSettings.absoluteTouchMode]; + [self.onscreenControlSelector setEnabled:[self.touchModeSelector selectedSegmentIndex] == RELATIVE_TOUCH_MODE];//initialize controller UI widget [self.bitrateSlider setMinimumValue:0]; [self.bitrateSlider setMaximumValue:(sizeof(bitrateTable) / sizeof(*bitrateTable)) - 1]; [self.bitrateSlider setValue:[self getSliderValueForBitrate:_bitrate] animated:YES]; @@ -302,10 +303,13 @@ BOOL isCustomResolution(CGSize res) { [self.pointerVelocityModeDividerSlider setValue:currentSettings.pointerVelocityModeDivider.floatValue * 100 animated:YES]; // Load old setting. [self.pointerVelocityModeDividerUILabel setText:[NSString stringWithFormat:@"Touch Pointer Velocity: Scaled on %d%% of Right Screen", 100 - (uint8_t)self.pointerVelocityModeDividerSlider.value]]; // Initiate label display [self.pointerVelocityModeDividerSlider addTarget:self action:@selector(pointerVelocityModeDividerSliderMoved) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. - + [self.pointerVelocityModeDividerSlider setEnabled:[self.touchModeSelector selectedSegmentIndex] == NATIVE_TOUCH_MODE]; // pointer velocity scaling works only in native touch mode. + [self.touchPointerVelocityFactorSlider setValue:currentSettings.touchPointerVelocityFactor.floatValue * 100 animated:YES]; // Load old setting. [self.touchPointerVelocityFactorUILabel setText:[NSString stringWithFormat:@"Touch Pointer Velocity Factor: %d%%", (uint16_t)self.touchPointerVelocityFactorSlider.value]]; // Initiate label display [self.touchPointerVelocityFactorSlider addTarget:self action:@selector(touchPointerVelocityFactorSliderMoved) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved. + [self.touchPointerVelocityFactorSlider setEnabled:[self.touchModeSelector selectedSegmentIndex] == NATIVE_TOUCH_MODE]; // pointer velocity scaling works only in native touch mode. + } - (void) pointerVelocityModeDividerSliderMoved { @@ -338,9 +342,9 @@ BOOL isCustomResolution(CGSize res) { - (void) touchModeChanged { // Disable on-screen controls in absolute touch mode - [self.onscreenControlSelector setEnabled:[self.touchModeSelector selectedSegmentIndex] == 0]; - [self.pointerVelocityModeDividerSlider setEnabled:[self.touchModeSelector selectedSegmentIndex] == 1]; // pointer velocity scaling works only in native touch mode. - [self.touchPointerVelocityFactorSlider setEnabled:[self.touchModeSelector selectedSegmentIndex] == 1]; // pointer velocity scaling works only in native touch mode. + [self.onscreenControlSelector setEnabled:[self.touchModeSelector selectedSegmentIndex] == RELATIVE_TOUCH_MODE]; + [self.pointerVelocityModeDividerSlider setEnabled:[self.touchModeSelector selectedSegmentIndex] == NATIVE_TOUCH_MODE]; // pointer velocity scaling works only in native touch mode. + [self.touchPointerVelocityFactorSlider setEnabled:[self.touchModeSelector selectedSegmentIndex] == NATIVE_TOUCH_MODE]; // pointer velocity scaling works only in native touch mode. } - (void) updateBitrate { @@ -625,7 +629,8 @@ BOOL isCustomResolution(CGSize res) { uint32_t preferredCodec = [self getChosenCodecPreference]; BOOL btMouseSupport = [self.btMouseSelector selectedSegmentIndex] == 1; BOOL useFramePacing = [self.framePacingSelector selectedSegmentIndex] == 1; - BOOL absoluteTouchMode = [self.touchModeSelector selectedSegmentIndex] == 1; + // BOOL absoluteTouchMode = [self.touchModeSelector selectedSegmentIndex] == 1; + NSInteger touchMode = [self.touchModeSelector selectedSegmentIndex]; BOOL statsOverlay = [self.statsOverlaySelector selectedSegmentIndex] == 1; BOOL enableHdr = [self.hdrSelector selectedSegmentIndex] == 1; [dataMan saveSettingsWithBitrate:_bitrate @@ -649,7 +654,8 @@ BOOL isCustomResolution(CGSize res) { useFramePacing:useFramePacing enableHdr:enableHdr btMouseSupport:btMouseSupport - absoluteTouchMode:absoluteTouchMode + // absoluteTouchMode:absoluteTouchMode + touchMode:touchMode statsOverlay:statsOverlay]; } diff --git a/Moonlight.xcodeproj/project.pbxproj b/Moonlight.xcodeproj/project.pbxproj index ed23738a..8f92d276 100644 --- a/Moonlight.xcodeproj/project.pbxproj +++ b/Moonlight.xcodeproj/project.pbxproj @@ -8,10 +8,12 @@ /* Begin PBXBuildFile section */ 693B3A9B218638CD00982F7B /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 693B3A9A218638CD00982F7B /* Settings.bundle */; }; - 840751AF2BF472A30096B657 /* NativeTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751AE2BF472A30096B657 /* NativeTouchHandler.m */; }; - 840751B02BF472A30096B657 /* NativeTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751AE2BF472A30096B657 /* NativeTouchHandler.m */; }; 840751B32BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */; }; 840751B42BF49C050096B657 /* CustomTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */; }; + 843CC7F02C1E757D00BBC475 /* NativeTouchPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = 843CC7EF2C1E757D00BBC475 /* NativeTouchPointer.m */; }; + 843CC7F12C1E757D00BBC475 /* NativeTouchPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = 843CC7EF2C1E757D00BBC475 /* NativeTouchPointer.m */; }; + 843CC7F62C1E7A1700BBC475 /* NativeTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 843CC7F52C1E7A1700BBC475 /* NativeTouchHandler.m */; }; + 843CC7F72C1E7A1700BBC475 /* NativeTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 843CC7F52C1E7A1700BBC475 /* NativeTouchHandler.m */; }; 84591A512BE1625E006AAB10 /* CustomEdgeSwipeGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 84591A502BE1625E006AAB10 /* CustomEdgeSwipeGestureRecognizer.m */; }; 98181BE92791277400E43572 /* libSDL2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98181BE82791275D00E43572 /* libSDL2.a */; }; 98181BEB2791278F00E43572 /* libSDL2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98181BEA2791278300E43572 /* libSDL2.a */; }; @@ -177,10 +179,12 @@ /* Begin PBXFileReference section */ 566E9D2B2770B23A00EF7BFE /* Moonlight v1.7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.7.xcdatamodel"; sourceTree = ""; }; 693B3A9A218638CD00982F7B /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; - 840751AD2BF472810096B657 /* NativeTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeTouchHandler.h; sourceTree = ""; }; - 840751AE2BF472A30096B657 /* NativeTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NativeTouchHandler.m; sourceTree = ""; }; 840751B12BF49B5A0096B657 /* CustomTapGestureRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomTapGestureRecognizer.h; sourceTree = ""; }; 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomTapGestureRecognizer.m; sourceTree = ""; }; + 843CC7EE2C1E757D00BBC475 /* NativeTouchPointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeTouchPointer.h; sourceTree = ""; }; + 843CC7EF2C1E757D00BBC475 /* NativeTouchPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NativeTouchPointer.m; sourceTree = ""; }; + 843CC7F42C1E778000BBC475 /* NativeTouchHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeTouchHandler.h; sourceTree = ""; }; + 843CC7F52C1E7A1700BBC475 /* NativeTouchHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NativeTouchHandler.m; sourceTree = ""; }; 84591A4F2BE16240006AAB10 /* CustomEdgeSwipeGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomEdgeSwipeGestureRecognizer.h; sourceTree = ""; }; 84591A502BE1625E006AAB10 /* CustomEdgeSwipeGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomEdgeSwipeGestureRecognizer.m; sourceTree = ""; }; 9803CCAB254F9EAF00EE185E /* ConnectionCallbacks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ConnectionCallbacks.h; sourceTree = ""; }; @@ -591,12 +595,12 @@ FB89460919F646E200339C8A /* Input */ = { isa = PBXGroup; children = ( - 840751AD2BF472810096B657 /* NativeTouchHandler.h */, - 840751AE2BF472A30096B657 /* NativeTouchHandler.m */, FB89460A19F646E200339C8A /* ControllerSupport.h */, FB89460B19F646E200339C8A /* ControllerSupport.m */, FB89460C19F646E200339C8A /* StreamView.h */, FB89460D19F646E200339C8A /* StreamView.m */, + 843CC7EE2C1E757D00BBC475 /* NativeTouchPointer.h */, + 843CC7EF2C1E757D00BBC475 /* NativeTouchPointer.m */, FB4678EB1A50C40900377732 /* OnScreenControls.h */, FB4678EC1A50C40900377732 /* OnScreenControls.m */, FB1A674B2131E65900507771 /* KeyboardSupport.h */, @@ -608,6 +612,8 @@ 840751B12BF49B5A0096B657 /* CustomTapGestureRecognizer.h */, 840751B22BF49C050096B657 /* CustomTapGestureRecognizer.m */, 9819CC12254F107A008A7C8E /* RelativeTouchHandler.h */, + 843CC7F42C1E778000BBC475 /* NativeTouchHandler.h */, + 843CC7F52C1E7A1700BBC475 /* NativeTouchHandler.m */, 9819CC13254F107A008A7C8E /* RelativeTouchHandler.m */, 9819CC20254F180F008A7C8E /* AbsoluteTouchHandler.h */, 9819CC21254F180F008A7C8E /* AbsoluteTouchHandler.m */, @@ -1053,6 +1059,7 @@ FB1A67C3213245EA00507771 /* AppAssetManager.m in Sources */, FB1A67C5213245EA00507771 /* AppAssetRetriever.m in Sources */, FB1A67C7213245EA00507771 /* PairManager.m in Sources */, + 843CC7F12C1E757D00BBC475 /* NativeTouchPointer.m in Sources */, FB1A67C9213245EA00507771 /* WakeOnLanManager.m in Sources */, FB1A67CB213245EA00507771 /* ConnectionHelper.m in Sources */, FB1A67BD213245E000507771 /* DiscoveryManager.m in Sources */, @@ -1062,6 +1069,7 @@ FB1A67B1213245D500507771 /* HttpManager.m in Sources */, FB1A67B3213245D500507771 /* HttpRequest.m in Sources */, FB1A67B5213245D500507771 /* HttpResponse.m in Sources */, + 843CC7F72C1E7A1700BBC475 /* NativeTouchHandler.m in Sources */, FB1A67B7213245D500507771 /* ServerInfoResponse.m in Sources */, FB1A67B9213245D500507771 /* AppAssetResponse.m in Sources */, FB1A67BB213245D500507771 /* AppListResponse.m in Sources */, @@ -1071,7 +1079,6 @@ 988FCD42293B091B003050E2 /* KeyboardInputField.m in Sources */, FB1A67A3213245BD00507771 /* Connection.m in Sources */, FB1A67A5213245BD00507771 /* StreamConfiguration.m in Sources */, - 840751B02BF472A30096B657 /* NativeTouchHandler.m in Sources */, FB1A67A7213245BD00507771 /* StreamManager.m in Sources */, FB1A67A9213245BD00507771 /* VideoDecoderRenderer.m in Sources */, FB1A67A12132458C00507771 /* main.m in Sources */, @@ -1087,7 +1094,7 @@ 9819CC14254F107A008A7C8E /* RelativeTouchHandler.m in Sources */, FB1A674D2131E65900507771 /* KeyboardSupport.m in Sources */, 84591A512BE1625E006AAB10 /* CustomEdgeSwipeGestureRecognizer.m in Sources */, - 840751AF2BF472A30096B657 /* NativeTouchHandler.m in Sources */, + 843CC7F62C1E7A1700BBC475 /* NativeTouchHandler.m in Sources */, FB89463219F646E200339C8A /* VideoDecoderRenderer.m in Sources */, FB290D0419B2C406004C83CF /* AppDelegate.m in Sources */, FB9AFD401A7E127D00872C98 /* AppListResponse.m in Sources */, @@ -1105,6 +1112,7 @@ FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */, FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */, FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */, + 843CC7F02C1E757D00BBC475 /* NativeTouchPointer.m in Sources */, FB89462C19F646E200339C8A /* HttpManager.m in Sources */, FB89462D19F646E200339C8A /* MDNSManager.m in Sources */, FB89462B19F646E200339C8A /* StreamView.m in Sources */, diff --git a/iPad.storyboard b/iPad.storyboard index cec3bf4a..31855e08 100644 --- a/iPad.storyboard +++ b/iPad.storyboard @@ -153,7 +153,8 @@ - + + diff --git a/iPhone.storyboard b/iPhone.storyboard index 5a75a680..21db342f 100644 --- a/iPhone.storyboard +++ b/iPhone.storyboard @@ -155,7 +155,8 @@ - + +