Merge remote-tracking branch 'refs/remotes/upstream/Integration' into feat/frame_pacing_metal

# Conflicts:
#	VoidLink.xcodeproj/project.pbxproj
#	VoidLink/Input/PureNativeTouchHandler.m
#	VoidLink/Input/StreamView.m
#	VoidLink/UIComputerView.m
This commit is contained in:
Acaki
2025-07-28 23:23:13 +08:00
30 changed files with 367 additions and 571 deletions
+2 -14
View File
@@ -78,7 +78,6 @@
DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1F5A06206436B20037755F /* ConnectionHelper.m */; };
F74BEF9C2C1A705600224667 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = F74BEF9B2C1A705600224667 /* OpenSSL */; };
FB1A674D2131E65900507771 /* KeyboardSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1A674C2131E65900507771 /* KeyboardSupport.m */; };
FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */; };
FB1D599A1BBCCD7E00F482CA /* AppCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */; };
FB290CF219B2C406004C83CF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF119B2C406004C83CF /* Foundation.framework */; };
FB290CF419B2C406004C83CF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF319B2C406004C83CF /* CoreGraphics.framework */; };
@@ -123,7 +122,6 @@
FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494F19FF2174002D2A60 /* SettingsViewController.m */; };
FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */; };
FBD349621A0089F6002D2A60 /* DataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD349611A0089F6002D2A60 /* DataManager.m */; };
FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86DF19F7A837001C18A8 /* UIComputerView.m */; };
FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86E519F82297001C18A8 /* UIAppView.m */; };
/* End PBXBuildFile section */
@@ -290,8 +288,6 @@
FB1A67642132419A00507771 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
FB1A67E421324A1F00507771 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; };
FB1A67E921324DF300507771 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; };
FB1D59951BBCCB6400F482CA /* ComputerScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComputerScrollView.h; sourceTree = "<group>"; };
FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComputerScrollView.m; sourceTree = "<group>"; };
FB1D59981BBCCD7E00F482CA /* AppCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppCollectionView.h; sourceTree = "<group>"; };
FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppCollectionView.m; sourceTree = "<group>"; };
FB290CEE19B2C406004C83CF /* VoidLink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoidLink.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -376,8 +372,6 @@
FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWRevealViewController.m; sourceTree = "<group>"; };
FBD349601A0089F6002D2A60 /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataManager.h; path = Database/DataManager.h; sourceTree = "<group>"; };
FBD349611A0089F6002D2A60 /* DataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DataManager.m; path = Database/DataManager.m; sourceTree = "<group>"; };
FBDE86DE19F7A837001C18A8 /* UIComputerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIComputerView.h; sourceTree = "<group>"; };
FBDE86DF19F7A837001C18A8 /* UIComputerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIComputerView.m; sourceTree = "<group>"; };
FBDE86E419F82297001C18A8 /* UIAppView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIAppView.h; sourceTree = "<group>"; };
FBDE86E519F82297001C18A8 /* UIAppView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAppView.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -619,8 +613,6 @@
841BAC912DE35D6B00DA639E /* ThemeManager.m */,
847D2FD82DDA00C800937A62 /* MenuSectionView.h */,
847D2FDA2DDA015F00937A62 /* MenuSectionView.m */,
FBDE86DE19F7A837001C18A8 /* UIComputerView.h */,
FBDE86DF19F7A837001C18A8 /* UIComputerView.m */,
FBDE86E419F82297001C18A8 /* UIAppView.h */,
FBDE86E519F82297001C18A8 /* UIAppView.m */,
1888C1902E24C2C800108730 /* AboutView.swift */,
@@ -628,8 +620,6 @@
FB89463719F6473800339C8A /* Launch Screen.xib */,
FB290D0819B2C406004C83CF /* Images.xcassets */,
FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */,
FB1D59951BBCCB6400F482CA /* ComputerScrollView.h */,
FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */,
FB1D59981BBCCD7E00F482CA /* AppCollectionView.h */,
FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */,
843E93BD2C41528700B1651F /* OrientationHelper.swift */,
@@ -1019,7 +1009,6 @@
84351B112C29ACCB00A77989 /* OSCProfilesManager.m in Sources */,
FB89462919F646E200339C8A /* mkcert.c in Sources */,
FB9AFD281A7C84ED00872C98 /* HttpResponse.m in Sources */,
FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */,
FB89463019F646E200339C8A /* StreamConfiguration.m in Sources */,
FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */,
84203F232C5F24C00032EEED /* OnScreenWidgetView.swift in Sources */,
@@ -1027,7 +1016,6 @@
9777AAF32E2CF7A400D7FBAF /* Shaders-Linear.metal in Sources */,
84FD0B512C31338D006B5BE5 /* LocalizationHelper.m in Sources */,
97E20DEA2E216532000D5DCA /* SceneDelegate.m in Sources */,
FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */,
FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */,
843CC7F02C1E757D00BBC475 /* NativeTouchPointer.m in Sources */,
FB89462C19F646E200339C8A /* HttpManager.m in Sources */,
@@ -1292,7 +1280,7 @@
"$(PROJECT_DIR)/libs/SDL2/lib/iOS-Sim",
"$(PROJECT_DIR)/libs/FFmpeg/lib/iOS-Sim",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.voidlink.tf.debug10.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
@@ -1357,7 +1345,7 @@
"$(PROJECT_DIR)/libs/SDL2/lib/iOS-Sim",
"$(PROJECT_DIR)/libs/FFmpeg/lib/iOS-Sim",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.voidlink.tf.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
+17 -7
View File
@@ -10,8 +10,10 @@
import SwiftUI
@available(iOS 13.0, *)
public struct AboutView: View {
@Environment(\.presentationMode) var presentationMode
public var aboutVC:UIViewController
public var body: some View {
VStack(spacing: 10) {
@@ -56,19 +58,18 @@ public struct AboutView: View {
}
}
// 链接按钮
if #available(iOS 14.0, *) {
if #available(iOS 14.0, *) {
Link(SwiftLocalizationHelper.localizedString(forKey: "Join us"), destination: URL(string: SwiftLocalizationHelper.localizedString(forKey: "supportLink"))!)
.padding(.top, 10)
Spacer()
// OK 按钮
Button("OK") {
presentationMode.wrappedValue.dismiss()
Button(SwiftLocalizationHelper.localizedString(forKey: "OK")) {
aboutVC.dismiss(animated:true)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.frame(height: 46)
//.frame(width: 100)
.cornerRadius(12)
} else {
HStack(spacing: 20) {
@@ -84,8 +85,8 @@ public struct AboutView: View {
.cornerRadius(12)
.frame(height: 33)
.frame(minWidth: 100)
Button("OK") {
/*
Button(SwiftLocalizationHelper.localizedString(forKey: "OK")) {
presentationMode.wrappedValue.dismiss()
}
.padding()
@@ -93,7 +94,15 @@ public struct AboutView: View {
.foregroundColor(.white)
.cornerRadius(12)
.frame(height: 33)
.frame(minWidth: 100)
.frame(minWidth: 100)*/
Button(SwiftLocalizationHelper.localizedString(forKey: "OK")) {
aboutVC.dismiss(animated:true)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(12)
.frame(height: 33)
}
.padding(.top, 10)
}
@@ -101,3 +110,4 @@ public struct AboutView: View {
.padding()
}
}
+3 -3
View File
@@ -13,8 +13,9 @@ import SwiftUI
@objc class AboutViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let hostingVC = UIHostingController(rootView: AboutView())
let hostingVC = UIHostingController(rootView: AboutView( aboutVC: self))
addChild(hostingVC)
hostingVC.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostingVC.view)
@@ -29,7 +30,6 @@ import SwiftUI
// 🎯 设置弹窗大小
preferredContentSize = CGSize(width: 530, height: 450)
//preferredContentSize = CGSize(width:150, height:120 )
// ⚠️ 一定要设置以下样式才会生效 preferredContentSize
modalPresentationStyle = .formSheet
-13
View File
@@ -1,13 +0,0 @@
//
// ComputerScrollView.h
// Moonlight
//
// Created by Diego Waxemberg on 9/30/15.
// Copyright © 2015 Moonlight Stream. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ComputerScrollView : UIScrollView
@end
-28
View File
@@ -1,28 +0,0 @@
//
// ComputerScrollView.m
// Moonlight
//
// Created by Diego Waxemberg on 9/30/15.
// Copyright © 2015 Moonlight Stream. All rights reserved.
//
#import "ComputerScrollView.h"
@implementation ComputerScrollView
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
if ([view isKindOfClass:[UIButton class]]) {
return YES;
}
return [super touchesShouldCancelInContentView:view];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
+2 -2
View File
@@ -505,7 +505,7 @@ static const float REFRESH_CYCLE = 2.0f;
- (void) updateLoop {
// Stop immediately if the view has been detached
if (self.superview == nil) {
if (self.superview == nil || [self.delegate isStreaming]) {
return;
}
@@ -514,7 +514,7 @@ static const float REFRESH_CYCLE = 2.0f;
[self updateContentsForHost:_host];
// Queue the next refresh cycle
if(![self.delegate isStreaming]) [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE];
[self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE];
}
- (void) updateContentsForHost:(TemporaryHost*)host {
+2 -2
View File
@@ -417,7 +417,7 @@ import UIKit
print("No match found for input: \(input)")
return nil
}
print("Regex matched for input: \(input)")
// print("Regex matched for input: \(input)")
let matchedString = (input as NSString).substring(with: match.range(at: 0))
let keyStrings = matchedString.split(separator: "+").map { String($0) }
@@ -475,7 +475,7 @@ import UIKit
print("No match found for input: \(input)")
return nil
}
print("Regex matched for input: \(input)")
// print("Regex matched for input: \(input)")
let matchedString = (input as NSString).substring(with: match.range(at: 0))
let cmdStrings = matchedString.split(separator: "-").map { String($0) }
@@ -22,8 +22,10 @@ NS_ASSUME_NONNULL_BEGIN
@property NSMutableArray <OSCProfile *> *currentProfiles;
+ (OSCProfilesManager *) sharedManager:(CGRect)viewBounds;
+ (void) setOnScreenWidgetViewsSet:(NSMutableSet* )set;
+ (OSCProfilesManager *)sharedManager:(CGRect)viewBounds;
+ (void)setOnScreenWidgetViewsSet:(NSMutableSet* )set;
+ (void)setLayoutViewBounds:(CGRect)bounds;
#pragma mark - Getters
/**
+16 -8
View File
@@ -18,15 +18,15 @@
@implementation OSCProfilesManager
static NSMutableSet *OnScreenWidgetViews;
static CGRect streamViewBounds;
static CGRect layoutViewBounds;
#pragma mark - Initializer
+ (OSCProfilesManager *) sharedManager:(CGRect)viewBounds {
static OSCProfilesManager *_sharedManager = nil;
static dispatch_once_t onceToken;
streamViewBounds = viewBounds;
NSLog(@"bounds width: %f, height: %f", streamViewBounds.size.width, streamViewBounds.size.height);
layoutViewBounds = viewBounds;
// NSLog(@"bounds width: %f, height: %f", layoutViewBounds.size.width, layoutViewBounds.size.height);
dispatch_once(&onceToken, ^{
_sharedManager = [[self alloc] init];
});
@@ -38,10 +38,14 @@ static CGRect streamViewBounds;
#pragma mark - Class Helper Methods
+ (void) setOnScreenWidgetViewsSet:(NSMutableSet* )set{
+ (void)setOnScreenWidgetViewsSet:(NSMutableSet* )set{
OnScreenWidgetViews = set;
}
+ (void)setLayoutViewBounds:(CGRect)bounds{
layoutViewBounds = bounds;
}
/**
* Returns the profile whose 'name' property has a value that is equal to the 'name' passed into the method
*/
@@ -297,17 +301,21 @@ static CGRect streamViewBounds;
}
- (CGPoint)normalizeWidgetPosition:(CGPoint)position {
CGPoint newPosition = position;
if(position.x > 1.0 && position.y >1.0){
position.x = position.x / streamViewBounds.size.width;
position.y = position.y / streamViewBounds.size.height;
position.x = position.x / layoutViewBounds.size.width;
position.y = position.y / layoutViewBounds.size.height;
}
// asdfsda;
NSLog(@"sef.view bounds: %f, %f", layoutViewBounds.size.width, layoutViewBounds.size.height);
NSLog(@"position: %f, %f, denormalized position: %f, %f", position.x, position.y, newPosition.x, newPosition.y);
return position;
}
- (CGPoint)denormalizeWidgetPosition:(CGPoint)position {
if(position.x < 1.0 && position.y < 1.0){
position.x = position.x * streamViewBounds.size.width;
position.y = position.y * streamViewBounds.size.height;
position.x = position.x * layoutViewBounds.size.width;
position.y = position.y * layoutViewBounds.size.height;
}
return position;
}
+11 -15
View File
@@ -35,7 +35,6 @@
NSMutableSet<NSNumber *> *pointerIdPool; //pre-defined pool of pointerIds.
NSMutableSet<NSNumber *> *unassignedPointerIds;
NSMutableSet *blacklistedTouches;
bool doNotBlockTouchEvents;
NSMutableDictionary *pointerObjDict;
@@ -61,7 +60,6 @@
}
self->activePointerIds = [NSMutableSet set];
self->blacklistedTouches = [NSMutableSet set];
doNotBlockTouchEvents = true;
self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff;
@@ -160,15 +158,14 @@
NSNumber* touchAddrObj = @((uintptr_t)touch);
unassignedPointerIds = [pointerIdPool mutableCopy]; //reset unassignedPointerIds
[unassignedPointerIds minusSet:activePointerIds];
uint8_t pointerId = [[unassignedPointerIds anyObject] unsignedIntValue];
[pointerIdDict setObject:@(pointerId) forKey:touchAddrObj];
[activePointerIds addObject:@(pointerId)];
NSNumber* pointerIdObj = @([[unassignedPointerIds anyObject] unsignedIntValue]);
[pointerIdDict setObject:pointerIdObj forKey:touchAddrObj];
[activePointerIds addObject:pointerIdObj];
//check if touch point is spawned on the left or right upper half screen edges, event to remote PC. this is for better handling in-stream slide gesture
CGPoint initialPoint = [touch locationInView:self->streamView];
if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) {
doNotBlockTouchEvents = false;
// [blacklistedTouches addObject:touchAddrObj];
[blacklistedTouches addObject:touchAddrObj];
}
}
@@ -192,8 +189,8 @@
- (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{
//if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash
// if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return;
if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return;
CGPoint targetCoords;
//NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved);
// NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]);
@@ -239,7 +236,7 @@
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
if(self->activateCoordSelector) [self updatePointerObjInDict:touch];
if(self->doNotBlockTouchEvents) [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
[[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service)
usleep(self->touchMoveEventIntervalUs);
}
@@ -265,9 +262,8 @@
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId
[self removePointerId:touch]; //then remove pointerId
if(self->activateCoordSelector) [self removePointerObjFromDict:touch];
//[self->blacklistedTouches removeObject:@((uintptr_t)touch)];
[self->blacklistedTouches removeObject:@((uintptr_t)touch)];
}
self->doNotBlockTouchEvents = true;
//if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count])
});
else{
@@ -297,10 +293,10 @@
}
- (void)removePointerObjFromDict:(UITouch*)touch{
uintptr_t memAddrValue = (uintptr_t)touch;
NativeTouchPointer* pointer = [pointerObjDict objectForKey:@(memAddrValue)];
NSNumber* touchAddrObj = @((uintptr_t)touch);
NativeTouchPointer* pointer = [pointerObjDict objectForKey:touchAddrObj];
if(pointer != nil){
[pointerObjDict removeObjectForKey:@(memAddrValue)];
[pointerObjDict removeObjectForKey:touchAddrObj];
}
}
+3 -3
View File
@@ -343,7 +343,7 @@ static float L3_Y;
if(position.x < 1.0 && position.y < 1.0) {
position.x = position.x * _view.bounds.size.width;
position.y = position.y * _view.bounds.size.height;
NSLog(@"denormalizing position: %f, %f", position.x, position.y);
// NSLog(@"denormalizing position: %f, %f", position.x, position.y);
}
return position;
}
@@ -1560,7 +1560,7 @@ static float L3_Y;
// on screen at the time
// DZ capturing shall be refactored in CustomOSC Mode & non-default profile condition
if(self._level == OnScreenControlsLevelCustom && [profilesManager getIndexOfSelectedProfile] != 0){
if(self._level == OnScreenControlsLevelCustom){
CGPoint touchLocation = [touch locationInView:_view];
for (NSString *key in _activeCustomOscButtonPositionDict) {
NSValue *value = _activeCustomOscButtonPositionDict[key];
@@ -1930,7 +1930,7 @@ static float L3_Y;
if(alpha < 0.23) targetAlpha = 0.23;
if(alpha == 0.0 || alpha == 1.0) targetAlpha = 5.0f/6.0f; // invalid alpha value
NSLog(@"alphas: %f",targetAlpha);
// NSLog(@"alphas: %f",targetAlpha);
if (layer == self._aButton ||
layer == self._bButton ||
+4 -4
View File
@@ -472,7 +472,7 @@ import UIKit
if vibrationOn {
vibrationGenerator.prepare()
vibrationGenerator.impactOccurred()
print("vibrationInstance: \(vibrationGenerator)")
// print("vibrationInstance: \(vibrationGenerator)")
}
return indicatorBorder
@@ -864,7 +864,7 @@ import UIKit
if vibrationOn {
vibrationGenerator.prepare()
vibrationGenerator.impactOccurred()
print("vibrationInstance: \(vibrationGenerator)")
// print("vibrationInstance: \(vibrationGenerator)")
}
CATransaction.commit()
}
@@ -1138,8 +1138,8 @@ import UIKit
let offsetX = currentLocation.x - latestTouchLocation.x;
let offsetY = currentLocation.y - latestTouchLocation.y;
let outOfBoundsX = center.x+offsetX > (self.superview?.bounds.width)! || center.x+offsetX < 0
let outOfBoundsY = center.y+offsetY > (self.superview?.bounds.height)! || center.y+offsetY < 0
let outOfBoundsX = center.x+offsetX >= (self.superview?.bounds.width)! || center.x+offsetX < 0
let outOfBoundsY = center.y+offsetY >= (self.superview?.bounds.height)! || center.y+offsetY < 0
center = CGPoint(x: outOfBoundsX ? center.x : center.x+offsetX, y: outOfBoundsY ? center.y : center.y+offsetY)
+13 -17
View File
@@ -34,7 +34,6 @@
NSMutableSet<NSNumber *> *pointerIdPool; //pre-defined pool of pointerIds.
NSMutableSet<NSNumber *> *unassignedPointerIds;
NSMutableSet *blacklistedTouches;
bool doNotBlockTouchEvents;
NSMutableDictionary *pointerObjDict;
@@ -60,8 +59,7 @@
}
self->activePointerIds = [NSMutableSet set];
self->blacklistedTouches = [NSMutableSet set];
doNotBlockTouchEvents = true;
self->asyncNativeTouch = settings.asyncNativeTouchPriority.intValue != AsyncNativeTouchOff;
switch(settings.asyncNativeTouchPriority.intValue){
@@ -159,15 +157,14 @@
NSNumber* touchAddrObj = @((uintptr_t)touch);
unassignedPointerIds = [pointerIdPool mutableCopy]; //reset unassignedPointerIds
[unassignedPointerIds minusSet:activePointerIds];
uint8_t pointerId = [[unassignedPointerIds anyObject] unsignedIntValue];
[pointerIdDict setObject:@(pointerId) forKey:touchAddrObj];
[activePointerIds addObject:@(pointerId)];
NSNumber* pointerIdObj = @([[unassignedPointerIds anyObject] unsignedIntValue]);
[pointerIdDict setObject:pointerIdObj forKey:touchAddrObj];
[activePointerIds addObject:pointerIdObj];
//check if touch point is spawned on the left or right upper half screen edges, event to remote PC. this is for better handling in-stream slide gesture
CGPoint initialPoint = [touch locationInView:self->streamView];
if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) {
doNotBlockTouchEvents = false;
// [blacklistedTouches addObject:touchAddrObj];
[blacklistedTouches addObject:touchAddrObj];
}
}
@@ -191,8 +188,8 @@
- (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{
//if(touchPointSpawnedAtUpperScreenEdge && touchType != LI_TOUCH_EVENT_UP) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash
// if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return;
if([blacklistedTouches containsObject:@((uintptr_t)touch)]) return;
CGPoint targetCoords;
//NSLog(@"selecting coords: %d", touch.phase == UITouchPhaseMoved);
// NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]);
@@ -235,14 +232,13 @@
for (UITouch* touch in touches){
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
if(self->activateCoordSelector) [self updatePointerObjInDict:touch];
if(self->doNotBlockTouchEvents) [self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
[[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service)
usleep(self->touchMoveEventIntervalUs);
}
});
else {
for (UITouch* touch in touches){
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
if(self->activateCoordSelector) [self updatePointerObjInDict:touch];
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
[[self getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service)
@@ -259,9 +255,8 @@
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId
[self removePointerId:touch]; //then remove pointerId
if(self->activateCoordSelector) [self removePointerObjFromDict:touch];
//[self->blacklistedTouches removeObject:@((uintptr_t)touch)];
[self->blacklistedTouches removeObject:@((uintptr_t)touch)];
}
self->doNotBlockTouchEvents = true;
//if(self->touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count])
});
else{
@@ -290,10 +285,10 @@
}
- (void)removePointerObjFromDict:(UITouch*)touch{
uintptr_t memAddrValue = (uintptr_t)touch;
NativeTouchPointer* pointer = [pointerObjDict objectForKey:@(memAddrValue)];
NSNumber* touchAddrObj = @((uintptr_t)touch);
NativeTouchPointer* pointer = [pointerObjDict objectForKey:touchAddrObj];
if(pointer != nil){
[pointerObjDict removeObjectForKey:@(memAddrValue)];
[pointerObjDict removeObjectForKey:touchAddrObj];
}
}
@@ -308,3 +303,4 @@
}
@end
+2 -1
View File
@@ -36,6 +36,7 @@
@property (assign, nonatomic) UIView* streamFrameTopLayerView;
@property (assign, nonatomic) CGFloat streamAspectRatio;
@property (assign, nonatomic) CGRect originalFrame;
@property (assign, nonatomic) bool widgetToolOpened;
- (void) setupStreamView:(ControllerSupport*)controllerSupport
@@ -50,7 +51,7 @@
andConfig:(StreamConfiguration*)streamConfig;
- (void) reloadOnScreenControlsWith:(ControllerSupport*)controllerSupport
andConfig:(StreamConfiguration*)streamConfig;
- (void) clearOnScreenKeyboardButtons;
- (void) clearOnScreenWidgets;
- (void) reloadOnScreenWidgetViews;
- (CGSize) getVideoAreaSize;
+38 -35
View File
@@ -34,7 +34,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
@implementation StreamView {
UIView* streamFrameTopLayerView;
TemporarySettings* settings;
OnScreenControls* onScreenControls;
KeyboardInputField* keyInputField;
@@ -43,7 +43,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
float streamAspectRatio;
// iOS 13.4 mouse support
NSInteger lastMouseButtonMask;
float lastMouseX;
@@ -56,7 +56,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
double accumulatedMouseDeltaY;
int localMousePointerMode;
UIResponder* touchHandler;
id<UserInteractionDelegate> interactionDelegate;
@@ -72,7 +72,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
#endif
CGFloat HeightViewLiftedTo;
UILabel* keyboardToggleTip;
UIKeyModifierFlags comboKeyModifierFlags;
}
@@ -87,9 +87,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
self->interactionDelegate = interactionDelegate;
self->streamAspectRatio = (float)streamConfig.width / (float)streamConfig.height;
self.streamAspectRatio = self->streamAspectRatio;
settings = [[[DataManager alloc] init] getSettings];
localMousePointerMode = streamConfig.localMousePointerMode;
keysDown = [[NSMutableSet alloc] init];
@@ -114,18 +114,18 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
keyboardToggleTip.layer.cornerRadius = 10;
keyboardToggleTip.clipsToBounds = true;
NSLog(@"setup streamView %f", CACurrentMediaTime());
// if(settings.touchMode.intValue == NativeTouchOnly) [self addGestureRecognizer:keyboardToggleRecognizer]; //keep legacy approach in pure native mode
// else [self->streamFrameTopLayerView addGestureRecognizer:keyboardToggleRecognizer]; //add to the superview in other modes
// [self->streamFrameTopLayerView addGestureRecognizer:keyboardToggleRecognizer]; //add to the superview in other modes
#if TARGET_OS_TV
// tvOS requires RelativeTouchHandler to manage Apple Remote input
self->touchHandler = [[RelativeTouchHandler alloc] initWithView:self];
#else
// iOS uses touch Mode depending on user preference
switch (settings.touchMode.intValue) {
case NativeTouch:
keyboardToggleRecognizer.immediateTriggering = false;
@@ -142,11 +142,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
self->touchHandler = [[AbsoluteTouchHandler alloc] initWithView:self];
keyboardToggleRecognizer.immediateTriggering = true; //triggers signal in touchesBegan callback stage
break;
default:
break;
}
// we'll render on-screen controls on the toplayer too:
onScreenControls = [[OnScreenControls alloc] initWithView:self->streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig]; // don't delete, this is mandatory
/*
@@ -155,15 +155,15 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
RelativeTouchHandler* relativeTouchHandler = (RelativeTouchHandler*) touchHandler;
onScreenControls.mouseRightClickTapRecognizer = relativeTouchHandler.mouseRightClickTapRecognizer;
} */
OnScreenControlsLevel level = (OnScreenControlsLevel)[settings.onscreenControls integerValue];
if (settings.touchMode.intValue != RelativeTouch && settings.touchMode.intValue != NativeTouch ) {
Log(LOG_I, @"On-screen controls disabled in non-relative touch mode");
[onScreenControls setLevel:OnScreenControlsLevelOff];
//pass touchesCaptureByOnScreenButtons Set to the native touchhandler, this NSSet is init witihin onscreencontrols class, don't do it again in native touch handler class
[OnScreenControls.touchAddrsCapturedByOnScreenControls removeAllObjects]; // reset the attribute to nil
/*
if(settings.touchMode.intValue == NativeTouch){
NativeTouchHandler* nativeTouchHandler = (NativeTouchHandler* )touchHandler;
@@ -302,7 +302,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
UIBarButtonItem *altBarButton = [self createButtonWithImageNamed:@"AltIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:0x12 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;
}
@@ -311,7 +311,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
[keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
// Undo causes issues for our state management, so turn it off
[keyInputField.undoManager disableUndoRegistration];
//[keyboardToggleTip removeFromSuperview];
}
}
@@ -350,7 +350,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
- (void) showOnScreenControls {
#if !TARGET_OS_TV
[onScreenControls show];
if(!_widgetToolOpened) [onScreenControls show];
#endif
}
@@ -385,13 +385,13 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
- (void) reloadOnScreenControlsWith:(ControllerSupport*)controllerSupport
andConfig:(StreamConfiguration*)streamConfig {
// we'll render on-screen controllers on the toplayer too.
onScreenControls = [[OnScreenControls alloc] initWithView:self->streamFrameTopLayerView controllerSup:controllerSupport streamConfig:streamConfig];
/*
// pass mouseRightClickTapRecognizer to onScreenControls obj here:
if([self isOscEnabled]){
RelativeTouchHandler* relativeTouchHandler = (RelativeTouchHandler *)touchHandler;
onScreenControls.mouseRightClickTapRecognizer = relativeTouchHandler.mouseRightClickTapRecognizer;
} */
@@ -399,7 +399,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
}
- (void) clearOnScreenKeyboardButtons{
- (void) clearOnScreenWidgets{
for (UIView *subview in self->streamFrameTopLayerView.subviews) {
// 检查子视图是否是特定类型的实例
if ([subview isKindOfClass:[OnScreenWidgetView class]]) {
@@ -411,23 +411,26 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
- (CGPoint)denormalizeWidgetPosition:(CGPoint)position {
if(position.x < 1.0 && position.y < 1.0){
position.x = position.x * self.bounds.size.width;
position.y = position.y * self.bounds.size.height;
position.x = position.x * streamFrameTopLayerView.bounds.size.width;
position.y = position.y * streamFrameTopLayerView.bounds.size.height;
}
else{
NSLog(@"invalid coords");
}
return position;
}
- (void) reloadOnScreenWidgetViews{
// NSLog(@"reload on screen keyboard buttons here");
// remove all keyboard widget views first
[self clearOnScreenKeyboardButtons];
[self clearOnScreenWidgets];
// bool customOscEnabled = [self isOscEnabled] && settings.onscreenControls.intValue == OnScreenControlsLevelCustom;
if(![self isOnScreenButtonEnabled]) return;
OSCProfilesManager* profilesManager = [OSCProfilesManager sharedManager: self.bounds];
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
@@ -630,7 +633,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
}
}
}
#endif
if ([self handleMouseButtonEvent:BUTTON_ACTION_PRESS
forTouches:touches
@@ -710,8 +713,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
}
- (BOOL)handleMouseButtonEvent:(int)buttonAction forTouches:(NSSet *)touches withEvent:(UIEvent *)event {
// NSLog(@"mouse click time: %f", CACurrentMediaTime()*1000);
#if !TARGET_OS_TV
if (@available(iOS 13.4, *)) {
@@ -775,12 +778,12 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
#if !TARGET_OS_TV
if (settings.touchMode.intValue == NativeTouchOnly) {
[touchHandler touchesMoved:touches withEvent:event];
return; //This is a native touch oriented fork, in pure native touch mode, this call back method deals with native touch only.
}
for (UITouch* touch in touches) {
if (touch.type == UITouchTypePencil) {
if ([self sendStylusEvent:touch]) return;
@@ -806,7 +809,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
}
#endif
}
hasUserInteracted = YES;
if(self->settings.touchMode.intValue == NativeTouch || self->settings.touchMode.intValue == RelativeTouch){
@@ -884,7 +887,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
[touchHandler touchesEnded:touches withEvent:event];
return; //This is a native touch oriented fork, in pure native touch mode, this call back method deals with native touch only.
}
if (@available(iOS 13.4, *)){ //now only pencil events are restricted
for (UITouch* touch in touches) {
if (touch.type == UITouchTypePencil) {
@@ -44,7 +44,7 @@
<attribute name="localMousePointerMode" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="mousePointerVelocityFactor" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" 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="onscreenControls" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="oscLayoutToolFingers" optional="YES" attributeType="Integer 16" defaultValueString="4" usesScalarValueType="NO" syncable="YES"/>
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
+66 -43
View File
@@ -295,13 +295,13 @@
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "[ 開啟螢幕小工具 ]"
"value" : "[ 調整螢幕小工具版面配置 ]"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "[ 開啟螢幕小工具 ]"
"value" : "[ 調整螢幕小工具版面配置 ]"
}
}
}
@@ -1457,25 +1457,25 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#"
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#"
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc"
}
}
}
@@ -1705,25 +1705,25 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bu3hki4pezy93g6r?singleDoc# "
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bu3hki4pezy93g6r?singleDoc"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/cqbse6t4o6f9hil1?singleDoc"
}
}
}
@@ -2022,25 +2022,25 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk?singleDoc"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3?singleDoc"
}
}
}
@@ -2454,25 +2454,25 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zdf5l29s3vohs6iq?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zdf5l29s3vohs6iq?singleDoc"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/yfao3s5bhl93rs01?singleDoc"
}
}
}
@@ -2898,6 +2898,29 @@
}
}
},
"Offline\n%@" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "离线\n%@"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "離線\n%@"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "離線\n%@"
}
}
}
},
"Ok" : {
"extractionState" : "manual",
"localizations" : {
@@ -2990,48 +3013,48 @@
}
}
},
"Online - Not Paired" : {
"Online - Not Paired\n%@" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "在线 - 未配对"
"value" : "在线 - 未配对\n%@"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "線上 - 未配對"
"value" : "線上 - 未配對\n%@"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "線上 - 未配對"
"value" : "線上 - 未配對\n%@"
}
}
}
},
"Online - Paired" : {
"Online - Paired\n%@" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "在线 - 已配对"
"value" : "在线 - 已配对\n%@"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "線上 - 已配對"
"value" : "線上 - 已配對\n%@"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "線上 - 已配對"
"value" : "線上 - 已配對\n%@"
}
}
}
@@ -3065,19 +3088,19 @@
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc# "
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc# "
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc# "
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/zrg0ga93avz4s747?singleDoc"
}
}
}
@@ -4240,25 +4263,25 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/rgc9if4xrf352555?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/rgc9if4xrf352555?singleDoc"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc#"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/ok4egvu4lt37wc1f?singleDoc"
}
}
}
@@ -4344,25 +4367,25 @@
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/dhmg3h27ls8mwt4a?singleDoc# 《About VoidLink》"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/dhmg3h27ls8mwt4a?singleDoc"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc# 《关于 VoidLink》"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc# 《关于 VoidLink》"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc# 《关于 VoidLink》"
"value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/fdi4rxkxykhl5zpp?singleDoc"
}
}
}
@@ -4603,25 +4626,25 @@
}
}
},
"Touch & Control" : {
"Touch & Controller" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "触屏与控制"
"value" : "触屏与手柄"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "觸控與控制"
"value" : "觸控與控制器"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "觸控與控制"
"value" : "觸控與控制器"
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@
#import "BandwidthTracker.h"
#import "Plot.h"
#define CONN_TEST_SERVER "ios.conntest.moonlight-stream.org"
#define CONN_TEST_SERVER "www.baidu.com"
@interface Connection : NSOperation <NSStreamDelegate>
+2 -2
View File
@@ -260,7 +260,7 @@ static UIImage* noImage;
- (void) updateLoop {
// Stop immediately if the view has been detached
if (self.superview == nil) {
if (self.superview == nil || ![self.updateLoopDelegate isInAppView]) {
return;
}
@@ -282,7 +282,7 @@ static UIImage* noImage;
[self setAlpha:_app.hidden ? 0.4 : 1.0];
// Queue the next refresh cycle
if([self.updateLoopDelegate isInAppView]) [self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE];
[self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE];
}
@end
-29
View File
@@ -1,29 +0,0 @@
//
// UIComputerView.h
// Moonlight
//
// Created by Diego Waxemberg on 10/22/14.
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TemporaryHost.h"
@protocol HostCallback <NSObject>
- (void) noneUserInitiatedHostAction:(TemporaryHost*)host view:(UIView*)view;
- (void) hostCardLongPressed:(TemporaryHost*)host view:(UIView*)view;
- (void) addHostTapped;
@end
#if !TARGET_OS_TV
@interface UIComputerView : UIButton <UIContextMenuInteractionDelegate>
#else
@interface UIComputerView : UIButton
#endif
- (id) initWithComputer:(TemporaryHost*)host andCallback:(id<HostCallback>)callback;
- (id) initForAddWithCallback:(id<HostCallback>)callback;
@end
-244
View File
@@ -1,244 +0,0 @@
//
// UIComputerView.m
// Moonlight
//
// Created by Diego Waxemberg on 10/22/14.
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
// Modified by True砖家 since 2024.6.30
// Copyright © 2024 True砖家 @ Bilibili. All rights reserved.
//
//
#import "UIComputerView.h"
#import "LocalizationHelper.h"
@implementation UIComputerView {
TemporaryHost* _host;
UIImageView* _hostIcon;
UILabel* _hostLabel;
UIImageView* _hostOverlay;
UIActivityIndicatorView* _hostSpinner;
id<HostCallback> _callback;
CGSize _labelSize;
}
static const float REFRESH_CYCLE = 2.0f;
#if TARGET_OS_TV
static const int ITEM_PADDING = 50;
static const int LABEL_DY = 40;
#else
static const int ITEM_PADDING = 0;
static const int LABEL_DY = 20;
#endif
- (id) init {
self = [super init];
#if TARGET_OS_TV
self.frame = CGRectMake(0, 0, 400, 400);
#else
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
self.frame = CGRectMake(0, 0, 200, 200);
} else {
self.frame = CGRectMake(0, 0, 100, 100);
}
#endif
_hostIcon = [[UIImageView alloc] initWithFrame:self.frame];
[_hostIcon setImage:[UIImage imageNamed:@"Computer"]];
self.layer.shadowColor = [[UIColor blackColor] CGColor];
self.layer.shadowOffset = CGSizeMake(5,8);
self.layer.shadowOpacity = 0.3;
[self addTarget:self action:@selector(hostButtonSelected:) forControlEvents:UIControlEventTouchDown];
[self addTarget:self action:@selector(hostButtonDeselected:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchDragExit];
_hostLabel = [[UILabel alloc] init];
_hostLabel.textColor = [UIColor whiteColor];
_hostOverlay = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width / 3, _hostIcon.frame.size.height / 4, _hostIcon.frame.size.width / 3, self.frame.size.height / 3)];
_hostSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
[_hostSpinner setFrame:_hostOverlay.frame];
_hostSpinner.userInteractionEnabled = NO;
_hostSpinner.hidesWhenStopped = YES;
[self addSubview:_hostLabel];
[self addSubview:_hostIcon];
#if TARGET_OS_TV
_hostIcon.clipsToBounds = NO;
_hostIcon.adjustsImageWhenAncestorFocused = YES;
_hostIcon.masksFocusEffectToContents = YES;
self.adjustsImageWhenHighlighted = NO;
_hostOverlay.masksFocusEffectToContents = YES;
_hostOverlay.adjustsImageWhenAncestorFocused = NO;
[_hostIcon.overlayContentView addSubview:_hostOverlay];
[_hostIcon.overlayContentView addSubview:_hostSpinner];
#else
[self addSubview:_hostOverlay];
[self addSubview:_hostSpinner];
if (@available(iOS 13.4.1, *)) {
// Allow the button style to change when moused over
self.pointerInteractionEnabled = YES;
}
#endif
return self;
}
- (void) hostButtonSelected:(id)sender {
_hostIcon.layer.opacity = 0.5f;
_hostSpinner.layer.opacity = 0.5f;
_hostOverlay.layer.opacity = 0.5f;
}
- (void) hostButtonDeselected:(id)sender {
_hostIcon.layer.opacity = 1.0f;
_hostSpinner.layer.opacity = 1.0f;
_hostOverlay.layer.opacity = 1.0f;
}
- (id) initForAddWithCallback:(id<HostCallback>)callback {
self = [self init];
_callback = callback;
[self addTarget:self action:@selector(addClicked) forControlEvents:UIControlEventPrimaryActionTriggered];
[_hostLabel setText:[LocalizationHelper localizedStringForKey:@"Add Host Manually"]];
[_hostLabel sizeToFit];
[_hostOverlay setImage:[UIImage imageNamed:@"AddOverlayIcon"]];
[self updateBounds];
return self;
}
- (id) initWithComputer:(TemporaryHost*)host andCallback:(id<HostCallback>)callback {
self = [self init];
_host = host;
_callback = callback;
// Use UIContextMenuInteraction on iOS 13.0+ and a standard UILongPressGestureRecognizer
// for tvOS devices and iOS prior to 13.0.
#if !TARGET_OS_TV
if (@available(iOS 13.0, *)) {
UIContextMenuInteraction* rightClickInteraction = [[UIContextMenuInteraction alloc] initWithDelegate:self];
[self addInteraction:rightClickInteraction];
}
else
#endif
{
UILongPressGestureRecognizer* longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(hostLongClicked:)];
[self addGestureRecognizer:longPressRecognizer];
}
[self addTarget:self action:@selector(hostClicked) forControlEvents:UIControlEventPrimaryActionTriggered];
[self updateContentsForHost:host];
return self;
}
- (void)didMoveToSuperview {
// Start our update loop when we are added to our cell
if (self.superview != nil && _host != nil) {
[self updateLoop];
}
}
- (void) updateBounds {
float x = FLT_MAX;
float y = FLT_MAX;
float width = 0;
float height;
float iconX = _hostIcon.frame.origin.x + _hostIcon.frame.size.width / 2;
_hostLabel.center = CGPointMake(iconX, _hostIcon.frame.origin.y + _hostIcon.frame.size.height + LABEL_DY);
x = MIN(x, _hostIcon.frame.origin.x);
x = MIN(x, _hostLabel.frame.origin.x);
y = MIN(y, _hostIcon.frame.origin.y);
y = MIN(y, _hostLabel.frame.origin.y);
width = MAX(width, _hostIcon.frame.size.width);
width = MAX(width, _hostLabel.frame.size.width);
height = _hostIcon.frame.size.height +
_hostLabel.frame.size.height +
LABEL_DY / 2;
self.bounds = CGRectMake(x - ITEM_PADDING, y - ITEM_PADDING, width + 2 * ITEM_PADDING, height + 2 * ITEM_PADDING);
}
- (void) updateContentsForHost:(TemporaryHost*)host {
_hostLabel.text = _host.name;
[_hostLabel sizeToFit];
if (host.state == StateOnline) {
[_hostSpinner stopAnimating];
if (host.pairState == PairStateUnpaired) {
[_hostOverlay setImage:[UIImage imageNamed:@"LockedOverlayIcon"]];
}
else {
[_hostOverlay setImage:nil];
}
}
else if (host.state == StateOffline) {
[_hostSpinner stopAnimating];
[_hostOverlay setImage:[UIImage imageNamed:@"ErrorOverlayIcon"]];
}
else {
[_hostSpinner startAnimating];
}
[self updateBounds];
}
- (void) updateLoop {
// Stop immediately if the view has been detached
if (self.superview == nil) {
return;
}
[self updateContentsForHost:_host];
// Queue the next refresh cycle
[self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE];
}
- (void) hostLongClicked:(UILongPressGestureRecognizer*)gesture {
if (gesture.state == UIGestureRecognizerStateBegan) {
[_callback hostCardLongPressed:_host view:self];
}
}
#if !TARGET_OS_TV
- (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction
configurationForMenuAtLocation:(CGPoint)location {
// We don't want to trigger the primary action at this point, so cancel
// tracking touch on this view now. This will also have the (intended)
// effect of removing the touch highlight on this view.
[self cancelTrackingWithEvent:nil];
[_callback hostCardLongPressed:_host view:self];
return nil;
}
#endif
- (void) hostClicked {
[_callback noneUserInitiatedHostAction:_host view:self];
}
- (void) addClicked {
[_callback addHostTapped];
}
@end
@@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)presentProfilesTableView;
@property LayoutOnScreenControls *layoutOSC; // object that contains a view which contains the on screen controller buttons that allows the user to drag and positions each button on the screen using touch
@property (nonatomic) NSMutableSet* OnScreenWidgetViews;
@property (nonatomic) NSMutableSet* onScreenWidgetViews;
@property int OSCSegmentSelected;
@property (nonatomic, assign) bool quickSwitchEnabled;
@@ -31,6 +31,7 @@
CGRect controllerLoadedBounds;
bool widgetViewSelected;
bool controllerLayerSelected;
bool viewWillBeResized;
__weak IBOutlet NSLayoutConstraint *toolbarTopConstraintiPhone;
__weak IBOutlet NSLayoutConstraint *toolbarTopConstraintiPad;
UIColor* trashCanStoryBoardColor;
@@ -62,7 +63,7 @@
- (void) viewWillDisappear:(BOOL)animated{
OnScreenWidgetView.editMode = false;
for (OnScreenWidgetView* widgetView in self.OnScreenWidgetViews){
for (OnScreenWidgetView* widgetView in self.onScreenWidgetViews){
[widgetView.stickBallLayer removeFromSuperlayer];
[widgetView.crossMarkLayer removeFromSuperlayer];
}
@@ -71,14 +72,23 @@
}
- (CGPoint)denormalizeWidgetPosition:(CGPoint)position {
// NSLog(@"position: %f, %f", position.x, position.y);
CGPoint newPosition = position;
if(position.x < 1.0 && position.y < 1.0){
position.x = position.x * self.view.bounds.size.width;
position.y = position.y * self.view.bounds.size.height;
newPosition.x = position.x * self.view.bounds.size.width;
newPosition.y = position.y * self.view.bounds.size.height;
}
return position;
return newPosition;
}
- (void) reloadOnScreenWidgetViews {
- (void)reloadLegacyOnScreenControls{
[self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the o//ne the user selected on the previous screen
[self addInnerAnalogSticksToOuterAnalogLayers];
[self.layoutOSC.layoutChanges removeAllObjects]; // since a new OSC profile is being loaded, this will remove all previous layout changes made from the array
[self OSCLayoutChanged]; // fades the 'Undo Button' out
}
- (void)reloadOnScreenWidgetViews{
NSLog(@"reloadOnScreenWidgets %f", CACurrentMediaTime());
OnScreenWidgetView.editMode = true;
[self clearStickIndicator];
@@ -89,7 +99,7 @@
}
}
[self.OnScreenWidgetViews removeAllObjects];
[self.onScreenWidgetViews removeAllObjects];
NSLog(@"reload os Key here");
@@ -120,7 +130,7 @@
[widgetView resizeWidgetView]; // resize must be called after relocation
[widgetView adjustTransparencyWithAlpha:buttonState.backgroundAlpha];
[widgetView adjustBorderWithWidth:buttonState.borderWidth];
[self.OnScreenWidgetViews addObject:widgetView];
[self.onScreenWidgetViews addObject:widgetView];
}
}
}
@@ -128,11 +138,12 @@
- (void) viewDidLoad {
[super viewDidLoad];
profilesManager = [OSCProfilesManager sharedManager:self.view.bounds];
self.OnScreenWidgetViews = [[NSMutableSet alloc] init]; // will be revised to read persisted data , somewhere else
[OSCProfilesManager setOnScreenWidgetViewsSet:self.OnScreenWidgetViews]; // pass the keyboard button dict to profiles manager
self.onScreenWidgetViews = [[NSMutableSet alloc] init]; // will be revised to read persisted data , somewhere else
[OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager
//isToolbarHidden = NO; // keeps track if the toolbar is hidden up above the screen so that we know whether to hide or show it when the user taps the toolbar's hide/show button
_quickSwitchEnabled = false;
viewWillBeResized = false;
/* add curve to bottom of chevron tab view */
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.chevronView.bounds byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) cornerRadii:CGSizeMake(10.0, 10.0)];
@@ -155,8 +166,9 @@
self.layoutOSC = [[LayoutOnScreenControls alloc] initWithView:self.view controllerSup:nil streamConfig:nil oscLevel:OSCSegmentSelected];
self.layoutOSC._level = OnScreenControlsLevelCustom;
self.layoutOSC.layoutToolVC = self;
//[self.layoutOSC show]; // draw on screen controls
[self.layoutOSC show]; // draw on screen controls
[self addInnerAnalogSticksToOuterAnalogLayers]; // allows inner and analog sticks to be dragged together around the screen together as one unit which is the expected behavior
self.undoButton.alpha = 0.3; // no changes to undo yet, so fade out the undo button a bit
@@ -238,12 +250,16 @@
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(OSCLayoutChanged) name:@"OSCLayoutChanged" object:nil]; // used to notifiy this view controller that the user made a change to the OSC layout so that the VC can either fade in or out its 'Undo button' which will signify to the user whether there are any OSC layout changes to undo
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleReturnToForeground)
name: UIApplicationDidBecomeActiveNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
selector:@selector(deviceOrientationDidChange) // handle orientation change since i made portrait mode available
name:UIDeviceOrientationDidChangeNotification
object:nil];
OnScreenWidgetView.editMode = true;
[self handleMissingToolBarIcon:toolbarRootView];
[self profileRefresh];
@@ -251,10 +267,33 @@
#pragma mark - Class Helper Functions
- (void)applicationWillResignActive:(NSNotification *)notification {
[self saveTapped:nil];
- (void)handleReturnToForeground {
[OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{
viewWillBeResized = true;
[self clearStickIndicator];
if(!_quickSwitchEnabled) [self saveTapped:nil];
}
- (void)deviceOrientationDidChange{
[self performSelector:@selector(handleOrientationChangeForOnScreenWidgets) withObject:self afterDelay:0.05];
}
- (void)handleOrientationChangeForOnScreenWidgets{
if(!viewWillBeResized) return;
[self setupWidgetPanel];
viewWillBeResized = false;
selectedWidgetView = nil;
selectedControllerLayer = nil;
_oscProfilesTableViewController.layoutViewBounds = self.view.bounds;
[OSCProfilesManager setOnScreenWidgetViewsSet:self.onScreenWidgetViews]; // pass the keyboard button dict to profiles manager
[self reloadOnScreenWidgetViews];
[self reloadLegacyOnScreenControls];
}
/* fades the 'Undo Button' in or out depending on whether the user has any OSC layout changes to undo */
- (void) OSCLayoutChanged {
@@ -379,8 +418,8 @@
UIAlertAction *readInstruction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Read Widget Instruction"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action){
[self saveTapped:nil];
NSURL *url = [NSURL URLWithString:@"https://b23.tv/J8qEXOr"];
//[self saveTapped:nil];
NSURL *url = [NSURL URLWithString:[LocalizationHelper localizedStringForKey:@"onScreenWidgetStackDoc"]];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
@@ -435,7 +474,8 @@
UIAlertAction *readInstruction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Read Widget Instruction"]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action){
NSURL *url = [NSURL URLWithString:@"https://b23.tv/J8qEXOr"];
//[self saveTapped:nil];
NSURL *url = [NSURL URLWithString:[LocalizationHelper localizedStringForKey:@"onScreenWidgetStackDoc"]];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
@@ -503,7 +543,7 @@
textField.spellCheckingType = UITextSpellCheckingTypeNo;
textField.text = self->selectedWidgetView.shape;
if([self->selectedWidgetView.shape isEqualToString: @"largeSquare"]) textField.enabled = false;
}];
}];
UIAlertAction *createNewAction = [UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Create New"]
@@ -587,10 +627,10 @@
[newWidget resizeWidgetView]; // resize must be called after relocation
[newWidget adjustTransparencyWithAlpha:widget.backgroundAlpha];
[newWidget adjustBorderWithWidth:widget.borderWidth];
[self.OnScreenWidgetViews addObject:newWidget];
[self.onScreenWidgetViews addObject:newWidget];
self->selectedWidgetView = newWidget;
if(!createNew){
[self.OnScreenWidgetViews removeObject:widget];
[self.onScreenWidgetViews removeObject:widget];
[widget removeFromSuperview];
}
}
@@ -603,7 +643,7 @@
widgetView.guidelineDelegate = (id<OnScreenWidgetGuidelineUpdateDelegate>)self;
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
widgetView.minStickOffset = [widgetInitParams[@"minStickOffsetString"] floatValue];
[self.OnScreenWidgetViews addObject:widgetView];
[self.onScreenWidgetViews addObject:widgetView];
// Add the widgetView to the view controller's view
[self.view insertSubview:widgetView belowSubview:self.widgetPanelStack];
[widgetView setLocationWithPosition:CGPointMake(90, 130)];
@@ -614,6 +654,7 @@
/* show pop up notification that lets users choose to save the current OSC layout configuration as a profile they can load when they want. User can also choose to cancel out of this pop up */
- (IBAction) saveTapped:(id)sender {
[OSCProfilesManager setLayoutViewBounds:self.view.bounds];
if([self->profilesManager updateSelectedProfile:self.layoutOSC.OSCButtonLayers]){
UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Current profile updated successfully"] preferredStyle:UIAlertControllerStyleAlert];
@@ -1105,12 +1146,9 @@
//this part is just for registration, will not be immediately executed.
self->_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
[self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the one the user selected on the previous screen
[self addInnerAnalogSticksToOuterAnalogLayers];
[self.layoutOSC.layoutChanges removeAllObjects]; // since a new OSC profile is being loaded, this will remove all previous layout changes made from the array
[self OSCLayoutChanged]; // fades the 'Undo Button' out
NSLog(@"profile profile");
[self reloadLegacyOnScreenControls];
self->_oscProfilesTableViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayers; //pass updated OSCLayout to OSCProfileTableView again
//[self reloadOnScreenKeyboardButtons];
};
[self.oscProfilesTableViewController profileViewRefresh]; // execute this will make sure OSCLayout is updated from persisted profile, not any cache.
@@ -1133,16 +1171,10 @@
}
_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
_oscProfilesTableViewController.streamViewBounds = self.view.bounds;
_oscProfilesTableViewController.layoutViewBounds = self.view.bounds;
_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC ofile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the proffile and then hide/show and move each OSC button to their appropriate position
[self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the one the user selected on the previous screen
[self addInnerAnalogSticksToOuterAnalogLayers];
[self.layoutOSC.layoutChanges removeAllObjects]; // since a new OSC profile is being loaded, this will remove all previous layout changes made from the array
[self OSCLayoutChanged]; // fades the 'Undo Button' out
[self reloadLegacyOnScreenControls];
};
self.widgetPanelStack.hidden = YES;
@@ -1252,7 +1284,7 @@
if(!isToolbarHidden && self->selectedWidgetView != nil && [self layerIsOverlappingWithTrashcanButton:selectedWidgetView.layer]){
[self->selectedWidgetView removeFromSuperview];
[self.OnScreenWidgetViews removeObject:self->selectedWidgetView];
[self.onScreenWidgetViews removeObject:self->selectedWidgetView];
[self clearStickIndicator];
[selectedWidgetView.buttonDownVisualEffectLayer removeFromSuperlayer];
}
@@ -30,7 +30,7 @@ typedef NS_ENUM(NSUInteger, FileOperation) {
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, copy) void (^needToUpdateOscLayoutTVC)(void);
@property (nonatomic, assign) NSMutableArray *currentOSCButtonLayers;
@property (nonatomic, assign) CGRect streamViewBounds;
@property (nonatomic, assign) CGRect layoutViewBounds;
@property (weak, nonatomic) IBOutlet UINavigationBar *profileTableViewNavigationBar;
@end
@@ -74,7 +74,7 @@ const double NAV_BAR_HEIGHT = 50;
// self.profileTableViewNavigationBar.layer.cornerRadius = 15; // 设置圆角半径
profilesManager = [OSCProfilesManager sharedManager:_streamViewBounds];
profilesManager = [OSCProfilesManager sharedManager:_layoutViewBounds];
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, NAV_BAR_HEIGHT)];
@@ -13,7 +13,6 @@
#import "DiscoveryManager.h"
#import "PairManager.h"
#import "StreamConfiguration.h"
#import "UIComputerView.h"
#import "HostCardView.h"
#import "UIAppView.h"
#import "AppAssetManager.h"
@@ -21,7 +20,7 @@
#import "HostCollectionViewController.h"
@interface MainFrameViewController : UICollectionViewController <DiscoveryCallback, PairCallback, HostCallback, AppCallback, AppAssetCallback, NSURLConnectionDelegate, SWRevealViewControllerDelegate, HostCardActionDelegate, AppViewUpdateLoopDelegate>
@interface MainFrameViewController : UICollectionViewController <DiscoveryCallback, PairCallback, AppCallback, AppAssetCallback, NSURLConnectionDelegate, SWRevealViewControllerDelegate, HostCardActionDelegate, AppViewUpdateLoopDelegate>
@property (nonatomic, strong) IBOutlet UIBarButtonItem *settingsButton;
#if !TARGET_OS_TV
@@ -16,7 +16,6 @@
#import "Connection.h"
#import "StreamManager.h"
#import "Utils.h"
#import "UIComputerView.h"
#import "UIAppView.h"
#import "DataManager.h"
#import "TemporarySettings.h"
@@ -25,7 +24,6 @@
#import "ServerInfoResponse.h"
#import "StreamFrameViewController.h"
#import "LoadingFrameViewController.h"
#import "ComputerScrollView.h"
#import "TemporaryApp.h"
#import "IdManager.h"
#import "ConnectionHelper.h"
@@ -597,22 +595,24 @@ static NSMutableSet* hostList;
Log(LOG_D, @"Long clicked host: %@", host.name);
NSString* message;
NSString* hostAddress = [host.activeAddress componentsSeparatedByString:@":"].firstObject;
switch (host.state) {
case StateOffline:
message = [LocalizationHelper localizedStringForKey:@"Offline"];
message = [LocalizationHelper localizedStringForKey:@"Offline\n%@", hostAddress ? hostAddress : @"Unknown address"];
break;
case StateOnline:
if (host.pairState == PairStatePaired) {
message = [LocalizationHelper localizedStringForKey:@"Online - Paired"];
message = [LocalizationHelper localizedStringForKey:@"Online - Paired\n%@", hostAddress ? hostAddress : @"Unknown address"];
}
else {
message = [LocalizationHelper localizedStringForKey:@"Online - Not Paired"];
message = [LocalizationHelper localizedStringForKey:@"Online - Not Paired\n%@", hostAddress ? hostAddress : @"Unknown address"];
}
break;
case StateUnknown:
message = [LocalizationHelper localizedStringForKey:@"Connecting"];
message = [LocalizationHelper localizedStringForKey:@"Connecting\n"];
break;
default:
@@ -622,7 +622,7 @@ static NSMutableSet* hostList;
UIAlertController* longClickAlert = [UIAlertController alertControllerWithTitle:host.name message:message preferredStyle:UIAlertControllerStyleActionSheet];
if (host.state != StateOnline) {
/*[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Wake PC"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Wake PC"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
UIAlertController* wolAlert = [UIAlertController alertControllerWithTitle:[LocalizationHelper localizedStringForKey:@"Wake-On-LAN"] message:@"" preferredStyle:UIAlertControllerStyleAlert];
[wolAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:nil]];
if (host.mac == nil || [host.mac isEqualToString:@"00:00:00:00:00:00"]) {
@@ -634,7 +634,7 @@ static NSMutableSet* hostList;
wolAlert.message = [LocalizationHelper localizedStringForKey:@"Successfully sent wake-up request. It may take a few moments for the PC to wake. If it never wakes up, ensure it's properly configured for Wake-on-LAN."];
}
[[self activeViewController] presentViewController:wolAlert animated:YES completion:nil];
}]];*/
}]];
}
else if (host.pairState == PairStatePaired) {
/*
@@ -644,18 +644,14 @@ static NSMutableSet* hostList;
}]]; */
#if !TARGET_OS_TV
if (false) {
[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"NVIDIA GameStream End-of-Service"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
[Utils launchUrl:@"https://github.com/moonlight-stream/moonlight-docs/wiki/NVIDIA-GameStream-End-Of-Service-Announcement-FAQ"];
}]];
}
#endif
}
[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Test Network"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) {
/*[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Test Network"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) {
[self showLoadingFrame:^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Perform the network test on a GCD worker thread. It may take a while.
unsigned int portTestResult = LiTestClientConnectivity(CONN_TEST_SERVER, 443, ML_PORT_FLAG_ALL);
unsigned int portTestResult = LiTestClientConnectivity([host.activeAddress UTF8String], 443, ML_PORT_FLAG_ALL);
dispatch_sync(dispatch_get_main_queue(), ^{
[self hideLoadingFrame:^{
NSString* message;
@@ -681,8 +677,9 @@ static NSMutableSet* hostList;
});
});
}];
}]];
}]];*/
#if !TARGET_OS_TV
/*
if (host.state != StateOnline) {
[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"NVIDIA GameStream End-of-Service"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
[Utils launchUrl:@"https://github.com/moonlight-stream/moonlight-docs/wiki/NVIDIA-GameStream-End-Of-Service-Announcement-FAQ"];
@@ -690,7 +687,7 @@ static NSMutableSet* hostList;
[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Connection Help"] style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
[Utils launchUrl:@"https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting"];
}]];
}
}*/
#endif
[longClickAlert addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Remove Host"] style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) { // host removed here
[self->_discMan removeHostFromDiscovery:host];
@@ -748,7 +745,7 @@ static NSMutableSet* hostList;
}];
});
} else {
unsigned int portTestResults = LiTestClientConnectivity(CONN_TEST_SERVER, 443,
unsigned int portTestResults = LiTestClientConnectivity([host.activeAddress UTF8String], 443,
ML_PORT_FLAG_TCP_47984 | ML_PORT_FLAG_TCP_47989);
if (portTestResults != ML_TEST_RESULT_INCONCLUSIVE && portTestResults != 0) {
error = [error stringByAppendingString:[LocalizationHelper localizedStringForKey:@"!ML_TEST_RESULT_INCONCLUSIVE"]];
@@ -1144,7 +1141,7 @@ static NSMutableSet* hostList;
[settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.audioOnPcStack];
[settingsViewController.codecSelector setEnabled:!_settingsExpandedInStreamView];
[settingsViewController.yuv444Switch setEnabled:!_settingsExpandedInStreamView];
[settingsViewController.hdrSwitch setEnabled:!_settingsExpandedInStreamView];
[settingsViewController.hdrSwitch setEnabled:!_settingsExpandedInStreamView && [settingsViewController hdrSupported]];
[settingsViewController.gyroModeSelector setEnabled:!_settingsExpandedInStreamView || ![streamFrameViewController shallDisableGyroHotSwitch]];
[settingsViewController.emulatedControllerTypeSelector setEnabled:!_settingsExpandedInStreamView];
[settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.framepacingStack];
@@ -1470,7 +1467,7 @@ static NSMutableSet* hostList;
if (@available(iOS 13.0, *)) {
[_upButton setTitle:@""];
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:23 weight:UIImageSymbolWeightMedium ];
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:21.5 weight:UIImageSymbolWeightMedium ];
UIImage *image = [[UIImage systemImageNamed:@"tv" withConfiguration:config] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[_upButton setImage:image];
_upButton.imageInsets = UIEdgeInsetsMake(25, 20, 0, 15);
@@ -1933,7 +1930,7 @@ static NSMutableSet* hostList;
[self updateHostShortcuts];
// Update the title in case we now have a PC
[self updateTitle];
// [self updateTitle];
// Reset state first so we can rediscover hosts that were deleted before
[_discMan resetDiscoveryState];
@@ -1942,7 +1939,7 @@ static NSMutableSet* hostList;
// This function forces immediate decoding of the UIImage, rather
// than the default lazy decoding that results in janky scrolling.
-(void)deviceOrientationDidChange{
if(self.collectionView.superview == nil) return;
if(self.revealViewController.isStreaming || self.collectionView.superview == nil) return;
[NSLayoutConstraint activateConstraints:@[
[self.collectionView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.collectionView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
@@ -133,6 +133,7 @@
#pragma clang diagnostic pop
- (bool)hdrSupported;
- (void)saveSettings;
+ (bool)isLandscapeNow;
- (void)updateResolutionTable;
@@ -287,6 +287,8 @@ BOOL isCustomResolution(CGSize res) {
}
- (void)updateResolutionTable{
if(self.mainFrameViewController.settingsExpandedInStreamView) return;
UIWindow *window = self.view.window;
NSLog(@" window %@", window);
@@ -391,7 +393,7 @@ BOOL isCustomResolution(CGSize res) {
_parentStack.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:_parentStack];
[NSLayoutConstraint activateConstraints:@[
[_parentStack.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor constant: currentSettingsMenuMode == AllSettings ? [self getStandardNavBarHeight] : [self getStandardNavBarHeight]+20],
[_parentStack.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor constant: currentSettingsMenuMode == AllSettings ? [self getStandardNavBarHeight] : [self getStandardNavBarHeight]+10],
[_parentStack.bottomAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.bottomAnchor constant:-20],
[_parentStack.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant: 0], //mark: settingMenuLayout
[_parentStack.widthAnchor constraintEqualToAnchor:self.view.widthAnchor constant:-20] // section width adjusted here //mark: settingMenuLayout
@@ -533,7 +535,7 @@ BOOL isCustomResolution(CGSize res) {
touchAndControlSection = [[MenuSectionView alloc] init];
touchAndControlSection.delegate = self;
touchAndControlSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Touch & Control"];
touchAndControlSection.sectionTitle = [LocalizationHelper localizedStringForKey:@"Touch & Controller"];
if (@available(iOS 13.0, *)) {
[touchAndControlSection setSectionWithIcon:[UIImage imageNamed:@"arcade.stick.console"] andSize:20.5];
}
@@ -1172,6 +1174,20 @@ BOOL isCustomResolution(CGSize res) {
}
}
/*
- (BOOL)isFirstLaunch {
NSString *key = @"appHasLaunchedBefore";
BOOL launchedBefore = [[NSUserDefaults standardUserDefaults] boolForKey:key];
if (!launchedBefore) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize]; // iOS 12+ 可省略
return YES;
}
return NO;
}
*/
- (void)saveFavoriteSettingStackIdentifiers {
if(currentSettingsMenuMode != AllSettings){
@@ -1181,18 +1197,40 @@ BOOL isCustomResolution(CGSize res) {
[_favoriteSettingStackIdentifiers addObject:_parentStack.arrangedSubviews[i].accessibilityIdentifier];
}
}
[[NSUserDefaults standardUserDefaults] setObject:_favoriteSettingStackIdentifiers forKey:@"FavoriteSettingStackIdentifiers"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)loadFavoriteSettingStackIdentifiers {
NSArray *savedArray = [[NSUserDefaults standardUserDefaults] objectForKey:@"FavoriteSettingStackIdentifiers"];
if ([savedArray isKindOfClass:[NSArray class]]) {
_favoriteSettingStackIdentifiers = [savedArray mutableCopy];
} else {
_favoriteSettingStackIdentifiers = [NSMutableArray array];
[_favoriteSettingStackIdentifiers addObject:@"resolutionStack"];
[_favoriteSettingStackIdentifiers addObject:@"fpsStack"];
[_favoriteSettingStackIdentifiers addObject:@"bitrateStack"];
[_favoriteSettingStackIdentifiers addObject:@"framepacingStack"];
[_favoriteSettingStackIdentifiers addObject:@"codecStack"];
[_favoriteSettingStackIdentifiers addObject:@"hdrStack"];
[_favoriteSettingStackIdentifiers addObject:@"yuv444Stack"];
[_favoriteSettingStackIdentifiers addObject:@"pipStack"];
[_favoriteSettingStackIdentifiers addObject:@"touchModeStack"];
[_favoriteSettingStackIdentifiers addObject:@"pointerVelocityDividerStack"];
[_favoriteSettingStackIdentifiers addObject:@"pointerVelocityFactorStack"];
[_favoriteSettingStackIdentifiers addObject:@"mousePointerVelocityStack"];
[_favoriteSettingStackIdentifiers addObject:@"onScreenWidgetStack"];
[_favoriteSettingStackIdentifiers addObject:@"pipStack"];
[_favoriteSettingStackIdentifiers addObject:@"backgroundSessionTimerStack"];
[_favoriteSettingStackIdentifiers addObject:@"statsOverlayStack"];
[_favoriteSettingStackIdentifiers addObject:@"softKeyboardGestureStack"];
[_favoriteSettingStackIdentifiers addObject:@"slideToSettingsScreenEdgeStack"];
[_favoriteSettingStackIdentifiers addObject:@"slideToToolboxScreenEdgeStack"];
[_favoriteSettingStackIdentifiers addObject:@"slideToSettingsDistanceStack"];
[_favoriteSettingStackIdentifiers addObject:@"unlockDisplayOrientationStack"];
}
/*
for(NSString* str in _favoriteSettingStackIdentifiers){
NSLog(@"favarite setting loaded: %@", str);
@@ -1332,7 +1370,7 @@ BOOL isCustomResolution(CGSize res) {
if (![self hdrSupported]) {
[self.hdrSwitch setOn:NO];
[self widget:self.hdrSwitch setEnabled:NO];
[self.hdrSwitch setEnabled:NO];
}
else {
[self.hdrSwitch setOn:currentSettings.enableHdr];
@@ -1508,21 +1546,6 @@ BOOL isCustomResolution(CGSize res) {
[self.enableOswForNativeTouchSwitch addTarget:self action:@selector(enableOswForNativeTouchSwitchFlipped:) forControlEvents:UIControlEventValueChanged];
[self enableOswForNativeTouchSwitchFlipped:self.enableOswForNativeTouchSwitch];
// init CustomOSC stuff
/* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */
// self.layoutOnScreenControlsVC = [[LayoutOnScreenControlsViewController alloc] init];
BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone);
if (isIPhone) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPhone" bundle:nil];
self.layoutOnScreenControlsVC = [storyboard instantiateViewControllerWithIdentifier:@"LayoutOnScreenControlsViewController"];
}
else {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPad" bundle:nil];
self.layoutOnScreenControlsVC = [storyboard instantiateViewControllerWithIdentifier:@"LayoutOnScreenControlsViewController"];
self.layoutOnScreenControlsVC.modalPresentationStyle = UIModalPresentationFullScreen;
}
[self.externalDisplayModeSelector setSelectedSegmentIndex:currentSettings.externalDisplayMode.integerValue];
[self.localMousePointerModeSelector setSelectedSegmentIndex:currentSettings.localMousePointerMode.integerValue];
@@ -1628,6 +1651,20 @@ BOOL isCustomResolution(CGSize res) {
}
- (void)invokeOscLayout{
// init CustomOSC stuff
/* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */
// self.layoutOnScreenControlsVC = [[LayoutOnScreenControlsViewController alloc] init];
BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone);
if (isIPhone) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPhone" bundle:nil];
self.layoutOnScreenControlsVC = [storyboard instantiateViewControllerWithIdentifier:@"LayoutOnScreenControlsViewController"];
}
else {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPad" bundle:nil];
self.layoutOnScreenControlsVC = [storyboard instantiateViewControllerWithIdentifier:@"LayoutOnScreenControlsViewController"];
self.layoutOnScreenControlsVC.modalPresentationStyle = UIModalPresentationFullScreen;
}
self.layoutOnScreenControlsVC.view.backgroundColor = [UIColor colorWithWhite:0.55 alpha:1.0];
[self presentViewController:self.layoutOnScreenControlsVC animated:YES completion:nil];
}
@@ -64,6 +64,7 @@
UIScrollView *_scrollView;
BOOL _userIsInteracting;
bool viewJustLoaded;
bool viewIsBeingResized;
CGSize _keyboardSize;
PlotMetrics _decodeMetrics;
PlotMetrics _frameDropMetrics;
@@ -241,15 +242,8 @@
}
- (void)configOscLayoutTool{
if([self isOscLayoutToolEnabled]){
_oscLayoutTapRecoginizer = [[CustomTapGestureRecognizer alloc] initWithTarget:self action:@selector(openWidgetLayoutTool)];
_oscLayoutTapRecoginizer.numberOfTouchesRequired = _settings.oscLayoutToolFingers.intValue; //tap a predefined number of fingers to open osc layout tool
_oscLayoutTapRecoginizer.tapDownTimeThreshold = 0.2;
_oscLayoutTapRecoginizer.delaysTouchesBegan = NO;
_oscLayoutTapRecoginizer.delaysTouchesEnded = NO;
if(_settings.touchMode.intValue == AbsoluteTouch) _oscLayoutTapRecoginizer.immediateTriggering = true; // make immediate triggering on for absolute touch mode
[self.view addGestureRecognizer:_oscLayoutTapRecoginizer]; //
/* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */
// _layoutOnScreenControlsVC = [[LayoutOnScreenControlsViewController alloc] init];
BOOL isIPhone = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone);
@@ -265,20 +259,22 @@
_layoutOnScreenControlsVC.view.backgroundColor = UIColor.clearColor;
_layoutOnScreenControlsVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
}
//NSLog(@"in osc frameview gestures: %d", (uint32_t)[self.view.gestureRecognizers count]);
//NSLog(@"in osc streamview gestures: %d", (uint32_t)[_streamView.gestureRecognizers count]);
}
- (void)presentToolboxViewController{
[self configOscLayoutTool];
ToolboxViewController* oldToolboxVC = toolBoxViewController;
toolBoxViewController = [[ToolboxViewController alloc] init];
toolBoxViewController.specialEntryDelegate = self;
toolBoxViewController.specialEntries = oldToolboxVC.specialEntries;
[self configOscLayoutTool];
[self presentViewController:toolBoxViewController animated:YES completion:^{
//[self->toolBoxViewController setupConstraints];
}];
}
- (void)configSwipeGestures{
- (void)configGestures{
_slideToSettingsRecognizer = [[CustomEdgeSlideGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)];
_slideToSettingsRecognizer.edges = _settings.slideToSettingsScreenEdge.intValue;
_slideToSettingsRecognizer.normalizedThresholdDistance = _settings.slideToSettingsDistance.floatValue;
@@ -294,6 +290,17 @@
_slideToCmdToolRecognizer.delaysTouchesBegan = NO;
_slideToCmdToolRecognizer.delaysTouchesEnded = NO;
[self.view addGestureRecognizer:_slideToCmdToolRecognizer];
if([self isOscLayoutToolEnabled]){
_oscLayoutTapRecoginizer = [[CustomTapGestureRecognizer alloc] initWithTarget:self action:@selector(handleWidgetLayoutGesture)];
_oscLayoutTapRecoginizer.numberOfTouchesRequired = _settings.oscLayoutToolFingers.intValue; //tap a predefined number of fingers to open osc layout tool
_oscLayoutTapRecoginizer.tapDownTimeThreshold = 0.2;
_oscLayoutTapRecoginizer.delaysTouchesBegan = NO;
_oscLayoutTapRecoginizer.delaysTouchesEnded = NO;
if(_settings.touchMode.intValue == AbsoluteTouch) _oscLayoutTapRecoginizer.immediateTriggering = true; // make immediate triggering on for absolute touch mode
[self.view addGestureRecognizer:_oscLayoutTapRecoginizer]; //
}
}
- (void)configZoomGestureAndAddStreamView{
@@ -337,9 +344,10 @@
_settings = [[[DataManager alloc] init] getSettings]; //StreamFrameViewController retrieve the settings here.
}
overlayLevel = _settings.statsOverlayLevel.intValue;
[self configOscLayoutTool];
if(viewIsBeingResized) viewIsBeingResized = false;
else [self configOscLayoutTool];
[self updateToolboxSpecialEntries];
[self configSwipeGestures];
[self configGestures];
[self configZoomGestureAndAddStreamView];
[self->_streamView disableOnScreenControls]; //don't know why but this must be called outside the streamview class, just put it here. execute in streamview class cause hang
[self.mainFrameViewcontroller reloadStreamConfig]; // reload streamconfig
@@ -542,6 +550,7 @@
- (void)viewDidLoad
{
viewJustLoaded = true;
viewIsBeingResized = false;
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:YES];
@@ -699,10 +708,15 @@
[_streamView keyboardWillHide];
}
- (void)handleWidgetLayoutGesture{
[self configOscLayoutTool];
[self openWidgetLayoutTool];
}
- (void)openWidgetLayoutTool{
_streamView.widgetToolOpened = true;
[self->_streamView disableOnScreenControls];
[self->_streamView clearOnScreenKeyboardButtons]; // clear all onScreenKeyboardButtons before entering edit mode
[self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode
_layoutOnScreenControlsVC.quickSwitchEnabled = false;
_layoutOnScreenControlsVC.toolbarStackView.hidden = false;
_layoutOnScreenControlsVC.toolbarRootView.hidden = false;
@@ -710,8 +724,9 @@
}
- (void)switchWidgetProfile{
_streamView.widgetToolOpened = true;
[self->_streamView disableOnScreenControls];
[self->_streamView clearOnScreenKeyboardButtons]; // clear all onScreenKeyboardButtons before entering edit mode
[self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode
_layoutOnScreenControlsVC.quickSwitchEnabled = true;
_layoutOnScreenControlsVC.toolbarStackView.hidden = true;
_layoutOnScreenControlsVC.toolbarRootView.hidden = true;
@@ -730,6 +745,7 @@
- (void)oscLayoutClosed{
// Handle the callback
_streamView.widgetToolOpened = false;
[self->_streamView disableOnScreenControls]; // add this to get realtime back menu working.
[self->_streamView reloadOnScreenControlsWith:(ControllerSupport*)_controllerSupport
andConfig:(StreamConfiguration*)_streamConfig];
@@ -928,6 +944,7 @@
}
- (void) handleViewResize{
viewIsBeingResized = true;
_streamView.bounds = _deviceWindow.bounds;
_streamView.frame = _deviceWindow.frame;
if(![self isAirPlaying]){