Native touch oriented improvements
This commit is contained in:
1 parent
557765f2c0
commit
873269e410
25 files changed
+2254
-283
No files matched your search
@@ -17,8 +17,15 @@
|
||||
framerate:(NSInteger)framerate
|
||||
height:(NSInteger)height
|
||||
width:(NSInteger)width
|
||||
audioConfig:(NSInteger)audioConfig
|
||||
audioConfig:(NSInteger)audioConfig
|
||||
onscreenControls:(NSInteger)onscreenControls
|
||||
keyboardToggleFingers:(NSInteger)keyboardToggleFingers
|
||||
swipeExitScreenEdge:(NSInteger)swipeExitScreenEdge
|
||||
swipeToExitDistance:(CGFloat)swipeToExitDistance
|
||||
pointerVelocityModeDivider:(CGFloat)pointerVelocityModeDivider
|
||||
touchPointerVelocityFactor:(CGFloat)touchPointerVelocityFactor
|
||||
liftStreamViewForKeyboard:(BOOL)liftStreamViewForKeyboard
|
||||
showKeyboardToolbar:(BOOL)showKeyboardToolbar
|
||||
optimizeGames:(BOOL)optimizeGames
|
||||
multiController:(BOOL)multiController
|
||||
swapABXYButtons:(BOOL)swapABXYButtons
|
||||
|
||||
@@ -58,6 +58,13 @@
|
||||
width:(NSInteger)width
|
||||
audioConfig:(NSInteger)audioConfig
|
||||
onscreenControls:(NSInteger)onscreenControls
|
||||
keyboardToggleFingers:(NSInteger)keyboardToggleFingers
|
||||
swipeExitScreenEdge:(NSInteger)swipeExitScreenEdge
|
||||
swipeToExitDistance:(CGFloat)swipeToExitDistance
|
||||
pointerVelocityModeDivider:(CGFloat)pointerVelocityModeDivider
|
||||
touchPointerVelocityFactor:(CGFloat)touchPointerVelocityFactor
|
||||
liftStreamViewForKeyboard:(BOOL)liftStreamViewForKeyboard
|
||||
showKeyboardToolbar:(BOOL)showKeyboardToolbar
|
||||
optimizeGames:(BOOL)optimizeGames
|
||||
multiController:(BOOL)multiController
|
||||
swapABXYButtons:(BOOL)swapABXYButtons
|
||||
@@ -77,6 +84,13 @@
|
||||
settingsToSave.width = [NSNumber numberWithInteger:width];
|
||||
settingsToSave.audioConfig = [NSNumber numberWithInteger:audioConfig];
|
||||
settingsToSave.onscreenControls = [NSNumber numberWithInteger:onscreenControls];
|
||||
settingsToSave.keyboardToggleFingers = [NSNumber numberWithInteger:(uint16_t)keyboardToggleFingers];
|
||||
settingsToSave.swipeExitScreenEdge = [NSNumber numberWithInteger:(uint32_t)swipeExitScreenEdge];
|
||||
settingsToSave.swipeToExitDistance = [NSNumber numberWithFloat:swipeToExitDistance];
|
||||
settingsToSave.pointerVelocityModeDivider = [NSNumber numberWithFloat:pointerVelocityModeDivider];
|
||||
settingsToSave.touchPointerVelocityFactor = [NSNumber numberWithFloat:touchPointerVelocityFactor];
|
||||
settingsToSave.liftStreamViewForKeyboard = liftStreamViewForKeyboard;
|
||||
settingsToSave.showKeyboardToolbar = showKeyboardToolbar;
|
||||
settingsToSave.optimizeGames = optimizeGames;
|
||||
settingsToSave.multiController = multiController;
|
||||
settingsToSave.swapABXYButtons = swapABXYButtons;
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
@property (nonatomic, retain) NSNumber * width;
|
||||
@property (nonatomic, retain) NSNumber * audioConfig;
|
||||
@property (nonatomic, retain) NSNumber * onscreenControls;
|
||||
@property (nonatomic, retain) NSNumber * keyboardToggleFingers;
|
||||
@property (nonatomic, retain) NSNumber * swipeExitScreenEdge;
|
||||
@property (nonatomic, retain) NSNumber * swipeToExitDistance;
|
||||
@property (nonatomic, retain) NSNumber * touchPointerVelocityFactor;
|
||||
@property (nonatomic, retain) NSNumber * pointerVelocityModeDivider;
|
||||
@property (nonatomic, retain) NSString * uniqueId;
|
||||
@property (nonatomic) enum {
|
||||
CODEC_PREF_AUTO,
|
||||
@@ -34,6 +39,9 @@
|
||||
@property (nonatomic) BOOL btMouseSupport;
|
||||
@property (nonatomic) BOOL absoluteTouchMode;
|
||||
@property (nonatomic) BOOL statsOverlay;
|
||||
@property (nonatomic) BOOL liftStreamViewForKeyboard;
|
||||
@property (nonatomic) BOOL showKeyboardToolbar;
|
||||
|
||||
|
||||
- (id) initFromSettings:(Settings*)settings;
|
||||
|
||||
|
||||
@@ -85,6 +85,13 @@
|
||||
self.btMouseSupport = settings.btMouseSupport;
|
||||
self.absoluteTouchMode = settings.absoluteTouchMode;
|
||||
self.statsOverlay = settings.statsOverlay;
|
||||
self.keyboardToggleFingers = settings.keyboardToggleFingers;
|
||||
self.swipeExitScreenEdge = settings.swipeExitScreenEdge;
|
||||
self.swipeToExitDistance = settings.swipeToExitDistance;
|
||||
self.liftStreamViewForKeyboard = settings.liftStreamViewForKeyboard;
|
||||
self.showKeyboardToolbar = settings.showKeyboardToolbar;
|
||||
self.touchPointerVelocityFactor = settings.touchPointerVelocityFactor;
|
||||
self.pointerVelocityModeDivider = settings.pointerVelocityModeDivider;
|
||||
#endif
|
||||
self.uniqueId = settings.uniqueId;
|
||||
|
||||
|
||||
@@ -38,83 +38,85 @@
|
||||
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
|
||||
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];
|
||||
}
|
||||
//- (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];
|
||||
//}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// CustomTapGestureRecognizer.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by ZWM on 2024/5/15.
|
||||
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef CustomTapGestureRecognizer_h
|
||||
#define CustomTapGestureRecognizer_h
|
||||
|
||||
|
||||
|
||||
@interface CustomTapGestureRecognizer : UIGestureRecognizer
|
||||
|
||||
@property (nonatomic, assign) uint8_t numberOfTouchesRequired;
|
||||
@property (nonatomic, assign) double tapDownTimeThreshold;
|
||||
@property (atomic, readonly) CGFloat lowestTouchPointHeight;
|
||||
|
||||
|
||||
@end
|
||||
#endif /* CustomTapGestureRecognizer_h */
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// CustomTapGestureRecognizer.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by ZWM on 2024/5/15.
|
||||
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||
#import "CustomTapGestureRecognizer.h"
|
||||
|
||||
@implementation CustomTapGestureRecognizer
|
||||
|
||||
static NSTimeInterval multiFingerDownTime;
|
||||
static bool multiFingerDown;
|
||||
static CGFloat screenHeightInPoints;
|
||||
static CGFloat lowestTouchPointYCoord = 0.0;
|
||||
|
||||
- (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action {
|
||||
self = [super initWithTarget:target action:action];
|
||||
screenHeightInPoints = CGRectGetHeight([[UIScreen mainScreen] bounds]);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
// Check if the number of touches and taps meets the required criteria
|
||||
if ([[event allTouches] count] == _numberOfTouchesRequired) {
|
||||
multiFingerDownTime = CACurrentMediaTime();
|
||||
multiFingerDown = true;
|
||||
|
||||
for(UITouch *touch in [event allTouches]){
|
||||
if(lowestTouchPointYCoord < [touch locationInView:self.view].y) lowestTouchPointYCoord = [touch locationInView:self.view].y;
|
||||
}
|
||||
_lowestTouchPointHeight = screenHeightInPoints - lowestTouchPointYCoord;
|
||||
self.state = UIGestureRecognizerStatePossible;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
// [super touchesEnded:touches withEvent:event];
|
||||
if(multiFingerDown && [[event allTouches] count] == [touches count]){
|
||||
multiFingerDown = false;
|
||||
if((CACurrentMediaTime() - multiFingerDownTime) < _tapDownTimeThreshold / 1000){
|
||||
lowestTouchPointYCoord = 0.0; //reset for next recognition
|
||||
self.state = UIGestureRecognizerStateRecognized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (CGFloat)lowestTouchPointHeight{
|
||||
@synchronized (self){
|
||||
return self.lowestTouchPointHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// 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 TouchPointer : 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
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// NativeTouchHandler.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by ZWM on 2024/5/14.
|
||||
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "NativeTouchHandler.h"
|
||||
#include <Limelight.h>
|
||||
|
||||
|
||||
// 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<NSNumber *> *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;
|
||||
}
|
||||
|
||||
|
||||
- (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];
|
||||
if (pointerVelocityFactor != 1.0f){
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
+ (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)];
|
||||
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
|
||||
+252
-88
@@ -11,20 +11,26 @@
|
||||
#import "DataManager.h"
|
||||
#import "ControllerSupport.h"
|
||||
#import "KeyboardSupport.h"
|
||||
#import "NativeTouchHandler.h"
|
||||
#import "RelativeTouchHandler.h"
|
||||
#import "AbsoluteTouchHandler.h"
|
||||
#import "KeyboardInputField.h"
|
||||
#import "CustomTapGestureRecognizer.h"
|
||||
|
||||
|
||||
static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
@implementation StreamView {
|
||||
TemporarySettings* settings;
|
||||
|
||||
OnScreenControls* onScreenControls;
|
||||
|
||||
KeyboardInputField* keyInputField;
|
||||
BOOL isInputingText;
|
||||
NSMutableSet* keysDown;
|
||||
|
||||
float streamAspectRatio;
|
||||
|
||||
|
||||
|
||||
// iOS 13.4 mouse support
|
||||
NSInteger lastMouseButtonMask;
|
||||
@@ -44,15 +50,18 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
BOOL hasUserInteracted;
|
||||
|
||||
NSDictionary<NSString *, NSNumber *> *dictCodes;
|
||||
CustomTapGestureRecognizer *keyboardToggleRecognizer;
|
||||
CGFloat HeightViewLiftedTo;
|
||||
}
|
||||
|
||||
- (void) setupStreamView:(ControllerSupport*)controllerSupport
|
||||
interactionDelegate:(id<UserInteractionDelegate>)interactionDelegate
|
||||
config:(StreamConfiguration*)streamConfig {
|
||||
|
||||
self->interactionDelegate = interactionDelegate;
|
||||
self->streamAspectRatio = (float)streamConfig.width / (float)streamConfig.height;
|
||||
|
||||
TemporarySettings* settings = [[[DataManager alloc] init] getSettings];
|
||||
settings = [[[DataManager alloc] init] getSettings];
|
||||
|
||||
keysDown = [[NSMutableSet alloc] init];
|
||||
keyInputField = [[KeyboardInputField alloc] initWithFrame:CGRectZero];
|
||||
@@ -62,13 +71,33 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
[keyInputField setSpellCheckingType:UITextSpellCheckingTypeNo];
|
||||
[self addSubview:keyInputField];
|
||||
|
||||
isInputingText = false;
|
||||
keyboardToggleRecognizer = [[CustomTapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleKeyboard)];
|
||||
keyboardToggleRecognizer.numberOfTouchesRequired = settings.keyboardToggleFingers.intValue;
|
||||
keyboardToggleRecognizer.tapDownTimeThreshold = 300.0; // tap down time threshold in milli seconds.
|
||||
keyboardToggleRecognizer.delaysTouchesBegan = NO;
|
||||
keyboardToggleRecognizer.delaysTouchesEnded = NO;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(keyboardWillShow:)
|
||||
name:UIKeyboardWillShowNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(keyboardWillHide:)
|
||||
name:UIKeyboardWillHideNotification
|
||||
object:nil];
|
||||
[self addGestureRecognizer:keyboardToggleRecognizer];
|
||||
|
||||
#if TARGET_OS_TV
|
||||
// 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];
|
||||
// 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];
|
||||
@@ -87,25 +116,24 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
Log(LOG_I, @"Setting manual on-screen controls level: %d", (int)level);
|
||||
[onScreenControls setLevel:level];
|
||||
}
|
||||
|
||||
// It would be nice to just use GCMouse on iOS 14+ and the older API on iOS 13
|
||||
// but unfortunately that isn't possible today. GCMouse doesn't recognize many
|
||||
// mice correctly, but UIKit does. We will register for both and ignore UIKit
|
||||
// events if a GCMouse is connected.
|
||||
if (@available(iOS 13.4, *)) {
|
||||
[self addInteraction:[[UIPointerInteraction alloc] initWithDelegate:self]];
|
||||
|
||||
UIPanGestureRecognizer *discreteMouseWheelRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(mouseWheelMovedDiscrete:)];
|
||||
discreteMouseWheelRecognizer.maximumNumberOfTouches = 0;
|
||||
discreteMouseWheelRecognizer.allowedScrollTypesMask = UIScrollTypeMaskDiscrete;
|
||||
discreteMouseWheelRecognizer.allowedTouchTypes = @[@(UITouchTypeIndirectPointer)];
|
||||
[self addGestureRecognizer:discreteMouseWheelRecognizer];
|
||||
|
||||
UIPanGestureRecognizer *continuousMouseWheelRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(mouseWheelMovedContinuous:)];
|
||||
continuousMouseWheelRecognizer.maximumNumberOfTouches = 0;
|
||||
continuousMouseWheelRecognizer.allowedScrollTypesMask = UIScrollTypeMaskContinuous;
|
||||
continuousMouseWheelRecognizer.allowedTouchTypes = @[@(UITouchTypeIndirectPointer)];
|
||||
[self addGestureRecognizer:continuousMouseWheelRecognizer];
|
||||
// [self addInteraction:[[UIPointerInteraction alloc] initWithDelegate:self]];
|
||||
//
|
||||
// UIPanGestureRecognizer *discreteMouseWheelRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(mouseWheelMovedDiscrete:)];
|
||||
// discreteMouseWheelRecognizer.maximumNumberOfTouches = 0;
|
||||
// discreteMouseWheelRecognizer.allowedScrollTypesMask = UIScrollTypeMaskDiscrete;
|
||||
// discreteMouseWheelRecognizer.allowedTouchTypes = @[@(UITouchTypeIndirectPointer)];
|
||||
// [self addGestureRecognizer:discreteMouseWheelRecognizer];
|
||||
//
|
||||
// UIPanGestureRecognizer *continuousMouseWheelRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(mouseWheelMovedContinuous:)];
|
||||
// continuousMouseWheelRecognizer.maximumNumberOfTouches = 0;
|
||||
// continuousMouseWheelRecognizer.allowedScrollTypesMask = UIScrollTypeMaskContinuous;
|
||||
// continuousMouseWheelRecognizer.allowedTouchTypes = @[@(UITouchTypeIndirectPointer)];
|
||||
// [self addGestureRecognizer:continuousMouseWheelRecognizer];
|
||||
}
|
||||
|
||||
#if defined(__IPHONE_16_1) || defined(__TVOS_16_1)
|
||||
@@ -129,6 +157,70 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
[self becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)keyboardWillShow:(NSNotification *)notification{
|
||||
if(settings.liftStreamViewForKeyboard && !isInputingText){
|
||||
NSDictionary *userInfo = notification.userInfo;
|
||||
// Get the keyboard size from the notification
|
||||
CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||||
// NSLog(@"keyboard height %f, lowest point height %f", keyboardFrame.size.height, keyboardToggleRecognizer.lowestTouchPointHeight);
|
||||
if(keyboardFrame.size.height < CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.25) return; // return in case of abnormal keyboard height
|
||||
HeightViewLiftedTo = keyboardFrame.size.height - keyboardToggleRecognizer.lowestTouchPointHeight + CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.1; // lift the StreamView to the height of lowest touch point of multi-finger tap gesture, while reserving the view of 1/10 screen height for remote typing.
|
||||
if(HeightViewLiftedTo < 0) HeightViewLiftedTo = 0; // set HeightViewLiftedTo to 0 if it is high enough and not going to be covered by keyboard.
|
||||
CGRect liftedStreamFrame = self.frame;
|
||||
liftedStreamFrame.origin.y -= HeightViewLiftedTo;
|
||||
self.frame = liftedStreamFrame;
|
||||
isInputingText = true;
|
||||
}
|
||||
// NSLog(@"keyboard will show");
|
||||
}
|
||||
|
||||
- (void)keyboardWillHide:(NSNotification *)notification{
|
||||
|
||||
if(isInputingText){
|
||||
CGRect liftedStreamFrame = self.frame;
|
||||
// recover view position in keyboard hiding.
|
||||
liftedStreamFrame.origin.y += HeightViewLiftedTo;
|
||||
self.frame = liftedStreamFrame;
|
||||
isInputingText = false;
|
||||
}
|
||||
// NSLog(@"keyboard will hide");
|
||||
}
|
||||
|
||||
|
||||
- (void)toggleKeyboard{
|
||||
if (isInputingText) {
|
||||
Log(LOG_D, @"Closing the keyboard");
|
||||
[keyInputField resignFirstResponder];
|
||||
} else {
|
||||
Log(LOG_D, @"Opening the keyboard");
|
||||
// Prepare the textbox used to capture keyboard events.
|
||||
keyInputField.delegate = self;
|
||||
keyInputField.text = @"0";
|
||||
#if !TARGET_OS_TV
|
||||
// Prepare the toolbar above the keyboard for more options
|
||||
if(settings.showKeyboardToolbar){
|
||||
UIToolbar *customToolbarView = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, 44)];
|
||||
UIBarButtonItem *doneBarButton = [self createButtonWithImageNamed:@"DoneIcon.png" backgroundColor:[UIColor clearColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x00 isToggleable:NO];
|
||||
UIBarButtonItem *windowsBarButton = [self createButtonWithImageNamed:@"WindowsIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x5B isToggleable:YES];
|
||||
UIBarButtonItem *tabBarButton = [self createButtonWithImageNamed:@"TabIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x09 isToggleable:NO];
|
||||
UIBarButtonItem *shiftBarButton = [self createButtonWithImageNamed:@"ShiftIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0xA0 isToggleable:YES];
|
||||
UIBarButtonItem *escapeBarButton = [self createButtonWithImageNamed:@"EscapeIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x1B isToggleable:NO];
|
||||
UIBarButtonItem *controlBarButton = [self createButtonWithImageNamed:@"ControlIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0xA2 isToggleable:YES];
|
||||
UIBarButtonItem *altBarButton = [self createButtonWithImageNamed:@"AltIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0xA4 isToggleable:YES];
|
||||
UIBarButtonItem *deleteBarButton = [self createButtonWithImageNamed:@"DeleteIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x2E isToggleable:NO];
|
||||
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
|
||||
|
||||
[customToolbarView setItems:[NSArray arrayWithObjects:doneBarButton, windowsBarButton, escapeBarButton, tabBarButton, shiftBarButton, controlBarButton, altBarButton, deleteBarButton, flexibleSpace, nil]];
|
||||
keyInputField.inputAccessoryView = customToolbarView;
|
||||
}
|
||||
#endif
|
||||
[keyInputField becomeFirstResponder];
|
||||
[keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
|
||||
// Undo causes issues for our state management, so turn it off
|
||||
[keyInputField.undoManager disableUndoRegistration];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startInteractionTimer {
|
||||
// Restart user interaction tracking
|
||||
hasUserInteracted = NO;
|
||||
@@ -240,6 +332,75 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
return 90 - MIN(90, altitudeDegs);
|
||||
}
|
||||
|
||||
|
||||
- (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.
|
||||
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]]);
|
||||
}
|
||||
|
||||
|
||||
- (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;
|
||||
[TouchPointer populatePointerId:event]; //获取并记录pointerId
|
||||
if(settings.pointerVelocityModeDivider.floatValue != 1.0) [TouchPointer populatePointerObjIntoDict:event];
|
||||
break;
|
||||
case UITouchPhaseMoved://移动
|
||||
case UITouchPhaseStationary:
|
||||
type = LI_TOUCH_EVENT_MOVE;
|
||||
if(settings.pointerVelocityModeDivider.floatValue != 1.0) [TouchPointer 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];
|
||||
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];
|
||||
return;
|
||||
case UITouchPhaseRegionEntered://停留
|
||||
case UITouchPhaseRegionMoved://停留
|
||||
type = LI_TOUCH_EVENT_HOVER;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
[self sendTouchEvent:event touchType:type];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (BOOL)sendStylusEvent:(UITouch*)event {
|
||||
uint8_t type;
|
||||
|
||||
@@ -319,6 +480,40 @@ 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<UITouch*> *arr1 = [[event allTouches] allObjects];
|
||||
//// NSLog(@"touchesBegan - %lu",[[event allTouches] count]);
|
||||
//// NSLog(@"touchesBegan - %lu",arr1.count);
|
||||
//
|
||||
// for(int i=0;i<arr1.count;i++){
|
||||
// if (arr1[i].type == UITouchTypePencil) {
|
||||
// [self sendStylusEvent:arr1[i]];
|
||||
// return;
|
||||
//
|
||||
// }
|
||||
// // TemporarySettings* settings = [[[DataManager alloc] init] getSettings];
|
||||
// if (settings.absoluteTouchMode) {
|
||||
// [self handleUITouch:arr1[i] index:i];
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (settings.absoluteTouchMode) {
|
||||
for (UITouch* touch in touches) [self handleUITouch:touch index:0];// Native touch (absoluteTouch) first!
|
||||
return;
|
||||
}
|
||||
else{
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
if ([self sendStylusEvent:touch]) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NSLog(@"touchesBegan - allTouches %lu, pointerSet count %lu",[[event allTouches] count], [pointerIdSet count]);
|
||||
}
|
||||
#endif
|
||||
if ([self handleMouseButtonEvent:BUTTON_ACTION_PRESS
|
||||
forTouches:touches
|
||||
withEvent:event]) {
|
||||
@@ -331,18 +526,6 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
// Notify of user interaction and start expiration timer
|
||||
[self startInteractionTimer];
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
if (@available(iOS 13.4, *)) {
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
if ([self sendStylusEvent:touch]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (![onScreenControls handleTouchDownEvent:touches]) {
|
||||
// We still inform the touch handler even if we're going trigger the
|
||||
// keyboard activation gesture. This is important to ensure the touch
|
||||
@@ -350,43 +533,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
// activation of one or two finger gestures when a three finger gesture
|
||||
// is triggered.
|
||||
[touchHandler touchesBegan:touches withEvent:event];
|
||||
|
||||
if ([[event allTouches] count] == 3) {
|
||||
if (isInputingText) {
|
||||
Log(LOG_D, @"Closing the keyboard");
|
||||
[keyInputField resignFirstResponder];
|
||||
isInputingText = false;
|
||||
} else {
|
||||
Log(LOG_D, @"Opening the keyboard");
|
||||
// Prepare the textbox used to capture keyboard events.
|
||||
keyInputField.delegate = self;
|
||||
keyInputField.text = @"0";
|
||||
#if !TARGET_OS_TV
|
||||
// Prepare the toolbar above the keyboard for more options
|
||||
UIToolbar *customToolbarView = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, 44)];
|
||||
|
||||
UIBarButtonItem *doneBarButton = [self createButtonWithImageNamed:@"DoneIcon.png" backgroundColor:[UIColor clearColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x00 isToggleable:NO];
|
||||
UIBarButtonItem *windowsBarButton = [self createButtonWithImageNamed:@"WindowsIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x5B isToggleable:YES];
|
||||
UIBarButtonItem *tabBarButton = [self createButtonWithImageNamed:@"TabIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x09 isToggleable:NO];
|
||||
UIBarButtonItem *shiftBarButton = [self createButtonWithImageNamed:@"ShiftIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0xA0 isToggleable:YES];
|
||||
UIBarButtonItem *escapeBarButton = [self createButtonWithImageNamed:@"EscapeIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x1B isToggleable:NO];
|
||||
UIBarButtonItem *controlBarButton = [self createButtonWithImageNamed:@"ControlIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0xA2 isToggleable:YES];
|
||||
UIBarButtonItem *altBarButton = [self createButtonWithImageNamed:@"AltIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0xA4 isToggleable:YES];
|
||||
UIBarButtonItem *deleteBarButton = [self createButtonWithImageNamed:@"DeleteIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x2E isToggleable:NO];
|
||||
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
|
||||
|
||||
[customToolbarView setItems:[NSArray arrayWithObjects:doneBarButton, windowsBarButton, escapeBarButton, tabBarButton, shiftBarButton, controlBarButton, altBarButton, deleteBarButton, flexibleSpace, nil]];
|
||||
keyInputField.inputAccessoryView = customToolbarView;
|
||||
#endif
|
||||
[keyInputField becomeFirstResponder];
|
||||
[keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
|
||||
|
||||
// Undo causes issues for our state management, so turn it off
|
||||
[keyInputField.undoManager disableUndoRegistration];
|
||||
|
||||
isInputingText = true;
|
||||
}
|
||||
}
|
||||
// I refactored keyboard toggle by the CustomTapGestureRecognizer
|
||||
// if ([[event allTouches] count] == keyboardToggleFingers) [self toggleKeyboard];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,14 +659,19 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
#if !TARGET_OS_TV
|
||||
if (@available(iOS 13.4, *)) {
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
if ([self sendStylusEvent:touch]) {
|
||||
return;
|
||||
if (settings.absoluteTouchMode) {
|
||||
for (UITouch* touch in touches) [self handleUITouch:touch index:0];// Native touch (absoluteTouch) first!
|
||||
return;
|
||||
}
|
||||
else{
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
if ([self sendStylusEvent:touch]) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NSLog(@"touchesMoved - allTouches %lu, pointerSet count %lu",[[event allTouches] count], [pointerIdSet count]);
|
||||
UITouch *touch = [touches anyObject];
|
||||
if (touch.type == UITouchTypeIndirectPointer) {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
@@ -586,6 +739,22 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
#if !TARGET_OS_TV
|
||||
if (@available(iOS 13.4, *)) {
|
||||
if (settings.absoluteTouchMode) {
|
||||
for (UITouch* touch in touches) [self handleUITouch:touch index:0];// Native touch (absoluteTouch) first!
|
||||
return;
|
||||
}
|
||||
else{
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
if ([self sendStylusEvent:touch]) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NSLog(@"touchesEnded - allTouches %lu, pointerSet count %lu",[[event allTouches] count], [pointerIdSet count]);
|
||||
}
|
||||
#endif
|
||||
if ([self handleMouseButtonEvent:BUTTON_ACTION_RELEASE
|
||||
forTouches:touches
|
||||
withEvent:event]) {
|
||||
@@ -597,18 +766,6 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
hasUserInteracted = YES;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
if (@available(iOS 13.4, *)) {
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
if ([self sendStylusEvent:touch]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (![onScreenControls handleTouchUpEvent:touches]) {
|
||||
[touchHandler touchesEnded:touches withEvent:event];
|
||||
}
|
||||
@@ -616,18 +773,25 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
[touchHandler touchesCancelled:touches withEvent:event];
|
||||
[self handleMouseButtonEvent:BUTTON_ACTION_RELEASE
|
||||
forTouches:touches
|
||||
withEvent:event];
|
||||
#if !TARGET_OS_TV
|
||||
if (@available(iOS 13.4, *)) {
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
[self sendStylusEvent:touch];
|
||||
if (settings.absoluteTouchMode) {
|
||||
for (UITouch* touch in touches) [self handleUITouch:touch index:0];// Native touch (absoluteTouch) first!
|
||||
return;
|
||||
}
|
||||
else{
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.type == UITouchTypePencil) {
|
||||
if ([self sendStylusEvent:touch]) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// NSLog(@"touchesCancelled - allTouches %lu, pointerSet count %lu",[[event allTouches] count], [pointerIdSet count]);
|
||||
#endif
|
||||
[self handleMouseButtonEvent:BUTTON_ACTION_RELEASE
|
||||
forTouches:touches
|
||||
withEvent:event];
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -9,7 +9,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<string>Moonlight-ZWM</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@@ -69,7 +69,7 @@
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22225" systemVersion="23A344" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22522" systemVersion="22G313" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="hidden" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
@@ -21,7 +21,7 @@
|
||||
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="absoluteTouchMode" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="absoluteTouchMode" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="audioConfig" attributeType="Integer 32" defaultValueString="2" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="btMouseSupport" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
@@ -29,15 +29,22 @@
|
||||
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="keyboardToggleFingers" optional="YES" attributeType="Integer 16" defaultValueString="3" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="liftStreamViewForKeyboard" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="pointerVelocityModeDivider" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="preferredCodec" attributeType="Integer 32" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="showKeyboardToolbar" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="statsOverlay" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="swapABXYButtons" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="swipeExitScreenEdge" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="swipeToExitDistance" optional="YES" attributeType="Float" defaultValueString="0.5" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="touchPointerVelocityFactor" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
|
||||
<attribute name="useFramePacing" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="useFramePacing" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
|
||||
</entity>
|
||||
</model>
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// CustomEdgeSwipeGestureRecognizer.h
|
||||
// Moonlight-ZWM
|
||||
//
|
||||
// Created by ZWM on 2024/4/30.
|
||||
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef CustomEdgeSwipeGestureRecognizer_h
|
||||
#define CustomEdgeSwipeGestureRecognizer_h
|
||||
// CustomEdgeSwipeGestureRecognizer.h
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface CustomEdgeSwipeGestureRecognizer : UIGestureRecognizer
|
||||
|
||||
@property (nonatomic, assign) UIRectEdge edges; // Specify the edge(s) you want to recognize the swipe gesture on
|
||||
@property (nonatomic, assign) CGFloat normalizedThresholdDistance; // Distance from the edge to start recognizing the gesture
|
||||
|
||||
@end
|
||||
#endif /* CustomEdgeSwipeGestureRecognizer_h */
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// CustomEdgeSwipeGestureRecognizer.m
|
||||
// Moonlight-ZWM
|
||||
//
|
||||
// Created by ZWM on 2024/4/30.
|
||||
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
|
||||
//
|
||||
|
||||
// #import <Foundation/Foundation.h>
|
||||
|
||||
#import "CustomEdgeSwipeGestureRecognizer.h"
|
||||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||
|
||||
@implementation CustomEdgeSwipeGestureRecognizer
|
||||
UITouch* capturedUITouch;
|
||||
CGFloat _startPointX;
|
||||
static CGFloat screenWidthInPoints;
|
||||
static CGFloat EDGE_TOLERANCE_POINTS = 50.0f;
|
||||
|
||||
- (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action {
|
||||
self = [super initWithTarget:target action:action];
|
||||
screenWidthInPoints = CGRectGetWidth([[UIScreen mainScreen] bounds]); // Get the screen's bounds (in points)
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
// [super touchesBegan:touches withEvent:event];
|
||||
UITouch *touch = [touches anyObject];
|
||||
capturedUITouch = touch;
|
||||
_startPointX = [capturedUITouch locationInView:self.view].x;
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
// [super touchesEnded:touches withEvent:event];
|
||||
|
||||
if([touches containsObject:capturedUITouch]){
|
||||
CGFloat _endPointX = [capturedUITouch locationInView:self.view].x;
|
||||
CGFloat normalizedGestureDistance = fabs(_endPointX - _startPointX)/screenWidthInPoints;
|
||||
|
||||
if(self.edges & UIRectEdgeLeft){
|
||||
if(_startPointX < EDGE_TOLERANCE_POINTS && normalizedGestureDistance > _normalizedThresholdDistance){
|
||||
self.state = UIGestureRecognizerStateBegan;
|
||||
self.state = UIGestureRecognizerStateEnded;
|
||||
}
|
||||
// NSLog(@"_startPointX %f , normalizedGestureDeltaX %f", _startPointX, normalizedGestureDistance);
|
||||
}
|
||||
if(self.edges & UIRectEdgeRight){
|
||||
if((_startPointX > (screenWidthInPoints - EDGE_TOLERANCE_POINTS)) && normalizedGestureDistance > _normalizedThresholdDistance){
|
||||
self.state = UIGestureRecognizerStateBegan;
|
||||
self.state = UIGestureRecognizerStateEnded;
|
||||
}
|
||||
// NSLog(@"_startPointX %f , normalizedGestureDeltaX %f", _startPointX, normalizedGestureDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,19 @@
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *btMouseSelector;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *statsOverlaySelector;
|
||||
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *keyboardToggleFingerNumLabel;
|
||||
@property (strong, nonatomic) IBOutlet UISlider *keyboardToggleFingerNumSlider;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *liftStreamViewForKeyboardSelector;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *showKeyboardToolbarSelector;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *swipeExitScreenEdgeSelector;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *swipeToExitDistanceUILabel;
|
||||
@property (strong, nonatomic) IBOutlet UISlider *swipeToExitDistanceSlider;
|
||||
@property (strong, nonatomic) IBOutlet UISlider *pointerVelocityModeDividerSlider;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *pointerVelocityModeDividerUILabel;
|
||||
@property (strong, nonatomic) IBOutlet UISlider *touchPointerVelocityFactorSlider;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *touchPointerVelocityFactorUILabel;
|
||||
|
||||
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#import "TemporarySettings.h"
|
||||
#import "DataManager.h"
|
||||
|
||||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||
#import <VideoToolbox/VideoToolbox.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@@ -263,11 +264,60 @@ BOOL isCustomResolution(CGSize res) {
|
||||
[self.bitrateSlider addTarget:self action:@selector(bitrateSliderMoved) forControlEvents:UIControlEventValueChanged];
|
||||
[self updateBitrateText];
|
||||
[self updateResolutionDisplayViewText];
|
||||
|
||||
[self.keyboardToggleFingerNumSlider setValue:(CGFloat)currentSettings.keyboardToggleFingers.intValue animated:YES]; // Load old setting. old setting was converted to uint16_t before saving.
|
||||
if (self.keyboardToggleFingerNumSlider.value > 10.5f) [self.keyboardToggleFingerNumLabel setText:[NSString stringWithFormat:@"Tap-Toggle Local Keyboard Disabled"]]; // Initiate label display. Allow higher required finger number to completely disable keyboard toggle
|
||||
else [self.keyboardToggleFingerNumLabel setText:[NSString stringWithFormat:@"Fingers to Tap-Toggle Local Keyboard: %d", (uint16_t)self.keyboardToggleFingerNumSlider.value]]; // Initiate label display
|
||||
[self.keyboardToggleFingerNumSlider addTarget:self action:@selector(keyboardToggleFingerNumSliderMoved) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||
[self.liftStreamViewForKeyboardSelector setSelectedSegmentIndex:currentSettings.liftStreamViewForKeyboard ? 1 : 0];// Load old setting
|
||||
[self.showKeyboardToolbarSelector setSelectedSegmentIndex:currentSettings.showKeyboardToolbar ? 1 : 0];// Load old setting
|
||||
[self.swipeExitScreenEdgeSelector setSelectedSegmentIndex:[self getSelectorIndexFromScreenEdge:(uint32_t)currentSettings.swipeExitScreenEdge.integerValue]]; // Load old setting
|
||||
[self.swipeToExitDistanceSlider setValue:(CGFloat)currentSettings.swipeToExitDistance.floatValue animated:YES]; // Load old setting.
|
||||
[self.swipeToExitDistanceUILabel setText:[NSString stringWithFormat:@"Swipe & Exit Distance: %.2f * screen-width", self.swipeToExitDistanceSlider.value]]; // Initiate label display
|
||||
[self.swipeToExitDistanceSlider addTarget:self action:@selector(swipeToExitDistanceSliderMoved) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
|
||||
|
||||
[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.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.
|
||||
}
|
||||
|
||||
- (void) pointerVelocityModeDividerSliderMoved {
|
||||
[self.pointerVelocityModeDividerUILabel setText:[NSString stringWithFormat:@"Touch Pointer Velocity: Scaled on %d%% of Right Screen", 100 - (uint8_t)self.pointerVelocityModeDividerSlider.value]];
|
||||
}
|
||||
|
||||
- (void) touchPointerVelocityFactorSliderMoved {
|
||||
[self.touchPointerVelocityFactorUILabel setText:[NSString stringWithFormat:@"Touch Pointer Velocity Factor: %d%%", (uint16_t)self.touchPointerVelocityFactorSlider.value]]; // Initiate label display
|
||||
}
|
||||
|
||||
- (uint32_t) getScreenEdgeFromSelector {
|
||||
switch (self.swipeExitScreenEdgeSelector.selectedSegmentIndex) {
|
||||
case 0: return UIRectEdgeLeft;
|
||||
case 1: return UIRectEdgeRight;
|
||||
case 2: return UIRectEdgeLeft|UIRectEdgeRight;
|
||||
default: return UIRectEdgeLeft;
|
||||
}
|
||||
}
|
||||
|
||||
- (uint32_t) getSelectorIndexFromScreenEdge: (uint32_t)edge {
|
||||
switch (edge) {
|
||||
case UIRectEdgeLeft: return 0;
|
||||
case UIRectEdgeRight: return 1;
|
||||
case UIRectEdgeLeft|UIRectEdgeRight: return 2;
|
||||
default: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
- (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.
|
||||
}
|
||||
|
||||
- (void) updateBitrate {
|
||||
@@ -456,6 +506,15 @@ BOOL isCustomResolution(CGSize res) {
|
||||
[self.resolutionDisplayView addSubview:label2];
|
||||
}
|
||||
|
||||
- (void) keyboardToggleFingerNumSliderMoved{
|
||||
if (self.keyboardToggleFingerNumSlider.value > 10.5f) [self.keyboardToggleFingerNumLabel setText:[NSString stringWithFormat:@"Tap-Toggle Local Keyboard Disabled"]];
|
||||
else [self.keyboardToggleFingerNumLabel setText:[NSString stringWithFormat:@"Fingers to Tap-Toggle Local Keyboard: %d", (uint16_t)self.keyboardToggleFingerNumSlider.value]]; // Initiate label display
|
||||
}
|
||||
|
||||
- (void) swipeToExitDistanceSliderMoved{
|
||||
[self.swipeToExitDistanceUILabel setText:[NSString stringWithFormat:@"Swipe & Exit Distance: %.2f * screen-width", self.swipeToExitDistanceSlider.value]];
|
||||
}
|
||||
|
||||
- (void) bitrateSliderMoved {
|
||||
assert(self.bitrateSlider.value < (sizeof(bitrateTable) / sizeof(*bitrateTable)));
|
||||
_bitrate = bitrateTable[(int)self.bitrateSlider.value];
|
||||
@@ -528,6 +587,14 @@ BOOL isCustomResolution(CGSize res) {
|
||||
NSInteger height = [self getChosenStreamHeight];
|
||||
NSInteger width = [self getChosenStreamWidth];
|
||||
NSInteger onscreenControls = [self.onscreenControlSelector selectedSegmentIndex];
|
||||
NSInteger keyboardToggleFingers = (uint16_t)self.keyboardToggleFingerNumSlider.value;
|
||||
// NSLog(@"saveSettings keyboardToggleFingers %d", (uint16_t)keyboardToggleFingers);
|
||||
CGFloat swipeToExitDistance = self.swipeToExitDistanceSlider.value;
|
||||
uint32_t swipeExitScreenEdge = [self getScreenEdgeFromSelector];
|
||||
CGFloat pointerVelocityModeDivider = (CGFloat)(uint8_t)self.pointerVelocityModeDividerSlider.value/100;
|
||||
CGFloat touchPointerVelocityFactor =(CGFloat)(uint16_t)self.touchPointerVelocityFactorSlider.value/100;
|
||||
BOOL liftStreamViewForKeyboard = [self.liftStreamViewForKeyboardSelector selectedSegmentIndex] == 1;
|
||||
BOOL showKeyboardToolbar = [self.showKeyboardToolbarSelector selectedSegmentIndex] == 1;
|
||||
BOOL optimizeGames = [self.optimizeSettingsSelector selectedSegmentIndex] == 1;
|
||||
BOOL multiController = [self.multiControllerSelector selectedSegmentIndex] == 1;
|
||||
BOOL swapABXYButtons = [self.swapABXYButtonsSelector selectedSegmentIndex] == 1;
|
||||
@@ -544,6 +611,13 @@ BOOL isCustomResolution(CGSize res) {
|
||||
width:width
|
||||
audioConfig:2 // Stereo
|
||||
onscreenControls:onscreenControls
|
||||
keyboardToggleFingers:keyboardToggleFingers
|
||||
swipeExitScreenEdge:swipeExitScreenEdge
|
||||
swipeToExitDistance:swipeToExitDistance
|
||||
pointerVelocityModeDivider:pointerVelocityModeDivider
|
||||
touchPointerVelocityFactor:touchPointerVelocityFactor
|
||||
liftStreamViewForKeyboard:liftStreamViewForKeyboard
|
||||
showKeyboardToolbar:showKeyboardToolbar
|
||||
optimizeGames:optimizeGames
|
||||
multiController:multiController
|
||||
swapABXYButtons:swapABXYButtons
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <Limelight.h>
|
||||
#import "CustomEdgeSwipeGestureRecognizer.h"
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#import <AVFoundation/AVDisplayCriteria.h>
|
||||
@@ -30,6 +31,8 @@
|
||||
- (id)initWithRefreshRate:(float)arg1 videoDynamicRange:(int)arg2;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation StreamFrameViewController {
|
||||
ControllerSupport *_controllerSupport;
|
||||
StreamManager *_streamMan;
|
||||
@@ -49,7 +52,7 @@
|
||||
CGSize _keyboardSize;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
UIScreenEdgePanGestureRecognizer *_exitSwipeRecognizer;
|
||||
CustomEdgeSwipeGestureRecognizer *_exitSwipeRecognizer;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -83,7 +86,7 @@
|
||||
|
||||
[UIApplication sharedApplication].idleTimerDisabled = YES;
|
||||
|
||||
_settings = [[[DataManager alloc] init] getSettings];
|
||||
_settings = [[[DataManager alloc] init] getSettings]; //StreamFrameViewController retrieve the settings here.
|
||||
|
||||
_stageLabel = [[UILabel alloc] init];
|
||||
[_stageLabel setUserInteractionEnabled:NO];
|
||||
@@ -129,12 +132,13 @@
|
||||
[self.view addGestureRecognizer:_playPauseTapGestureRecognizer];
|
||||
|
||||
#else
|
||||
_exitSwipeRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)];
|
||||
_exitSwipeRecognizer.edges = UIRectEdgeLeft;
|
||||
_exitSwipeRecognizer = [[CustomEdgeSwipeGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)];
|
||||
_exitSwipeRecognizer.edges = _settings.swipeExitScreenEdge.intValue;
|
||||
_exitSwipeRecognizer.normalizedThresholdDistance = _settings.swipeToExitDistance.floatValue;
|
||||
_exitSwipeRecognizer.delaysTouchesBegan = NO;
|
||||
_exitSwipeRecognizer.delaysTouchesEnded = NO;
|
||||
|
||||
[self.view addGestureRecognizer:_exitSwipeRecognizer];
|
||||
|
||||
#endif
|
||||
|
||||
_tipLabel = [[UILabel alloc] init];
|
||||
@@ -143,7 +147,7 @@
|
||||
#if TARGET_OS_TV
|
||||
[_tipLabel setText:@"Tip: Tap the Play/Pause button on the Apple TV Remote to disconnect from your PC"];
|
||||
#else
|
||||
[_tipLabel setText:@"Tip: Swipe from the left edge to disconnect from your PC"];
|
||||
[_tipLabel setText:@"Tip: Swipe from the left edge to the middle of your screen to disconnect from your PC"];
|
||||
#endif
|
||||
|
||||
[_tipLabel sizeToFit];
|
||||
@@ -175,6 +179,7 @@
|
||||
#if 0
|
||||
// FIXME: This doesn't work reliably on iPad for some reason. Showing and hiding the keyboard
|
||||
// several times in a row will not correctly restore the state of the UIScrollView.
|
||||
// I add this notification to the StreamView, works well on ipad mini6.
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(keyboardWillShow:)
|
||||
name: UIKeyboardWillShowNotification
|
||||
@@ -187,25 +192,25 @@
|
||||
#endif
|
||||
|
||||
// Only enable scroll and zoom in absolute touch mode
|
||||
if (_settings.absoluteTouchMode) {
|
||||
_scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
|
||||
#if !TARGET_OS_TV
|
||||
[_scrollView.panGestureRecognizer setMinimumNumberOfTouches:2];
|
||||
#endif
|
||||
[_scrollView setShowsHorizontalScrollIndicator:NO];
|
||||
[_scrollView setShowsVerticalScrollIndicator:NO];
|
||||
[_scrollView setDelegate:self];
|
||||
[_scrollView setMaximumZoomScale:10.0f];
|
||||
|
||||
// Add StreamView inside a UIScrollView for absolute mode
|
||||
[_scrollView addSubview:_streamView];
|
||||
[self.view addSubview:_scrollView];
|
||||
}
|
||||
else {
|
||||
// Add StreamView directly in relative mode
|
||||
[self.view addSubview:_streamView];
|
||||
}
|
||||
|
||||
// if (_settings.absoluteTouchMode) {
|
||||
// _scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
|
||||
//#if !TARGET_OS_TV
|
||||
// [_scrollView.panGestureRecognizer setMinimumNumberOfTouches:2];
|
||||
//#endif
|
||||
// [_scrollView setShowsHorizontalScrollIndicator:NO];
|
||||
// [_scrollView setShowsVerticalScrollIndicator:NO];
|
||||
// [_scrollView setDelegate:self];
|
||||
// [_scrollView setMaximumZoomScale:10.0f];
|
||||
//
|
||||
// // Add StreamView inside a UIScrollView for absolute mode
|
||||
// [_scrollView addSubview:_streamView];
|
||||
// [self.view addSubview:_scrollView];
|
||||
// }
|
||||
// else {
|
||||
// // Add StreamView directly in relative mode
|
||||
// [self.view addSubview:_streamView];
|
||||
// }
|
||||
[self.view addSubview:_streamView];
|
||||
[self.view addSubview:_stageLabel];
|
||||
[self.view addSubview:_spinner];
|
||||
[self.view addSubview:_tipLabel];
|
||||
|
||||
Reference in new issue
Block a user