diff --git a/Natives/CMakeLists.txt b/Natives/CMakeLists.txt index 20bbf03d..8f312300 100644 --- a/Natives/CMakeLists.txt +++ b/Natives/CMakeLists.txt @@ -91,6 +91,7 @@ add_executable(PojavLauncher log.m JavaLauncher.m external/fishhook/fishhook.c + UIKit+hook.m authenticator/BaseAuthenticator.m authenticator/LocalAuthenticator.m diff --git a/Natives/Info.plist b/Natives/Info.plist index 6ba134c4..c5093966 100644 --- a/Natives/Info.plist +++ b/Natives/Info.plist @@ -115,5 +115,7 @@ UIViewControllerBasedStatusBarAppearance + UISupportsDocumentBrowser + diff --git a/Natives/LauncherPreferences.m b/Natives/LauncherPreferences.m index b0f218db..65d5e88c 100644 --- a/Natives/LauncherPreferences.m +++ b/Natives/LauncherPreferences.m @@ -3,13 +3,17 @@ NSMutableDictionary *prefDict; NSString* prefPath; +// developer debug dict +NSMutableDictionary *debugPrefDict; // environment variables dict NSMutableDictionary *envPrefDict; // warnings dict NSMutableDictionary *warnPrefDict; id getPreference(NSString* key) { - if (!(envPrefDict[key] == [NSNull null] || envPrefDict[key] == nil)) { + if (!(debugPrefDict[key] == [NSNull null] || debugPrefDict[key] == nil)) { + return debugPrefDict[key]; + } else if (!(envPrefDict[key] == [NSNull null] || envPrefDict[key] == nil)) { return envPrefDict[key]; } else if (!(warnPrefDict[key] == [NSNull null] || warnPrefDict[key] == nil)) { return warnPrefDict[key]; @@ -58,7 +62,10 @@ void setDefaultValueForPref(NSMutableDictionary *dict, NSString* key, id value) } void setPreference(NSString* key, id value) { - if (!(envPrefDict[key] == [NSNull null] || envPrefDict[key] == nil)) { + if (!(debugPrefDict[key] == [NSNull null] || debugPrefDict[key] == nil)) { + debugPrefDict[key] = value; + prefDict[@"debugs"] = debugPrefDict; + } else if (!(envPrefDict[key] == [NSNull null] || envPrefDict[key] == nil)) { envPrefDict[key] = value; prefDict[@"env_vars"] = envPrefDict; } else if (!(warnPrefDict[key] == [NSNull null] || warnPrefDict[key] == nil)) { @@ -79,11 +86,17 @@ void fillDefaultWarningDict() { setDefaultValueForPref(warnPrefDict, @"java_warn", @YES); setDefaultValueForPref(warnPrefDict, @"demo_warn", @YES); setDefaultValueForPref(warnPrefDict, @"jb_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"customctrl_warn", @YES); setDefaultValueForPref(warnPrefDict, @"int_warn", @YES); setDefaultValueForPref(warnPrefDict, @"auto_ram_warn", @YES); } +NSMutableDictionary* getDictionarySafe(NSString* key) { + if (prefDict[key] == nil) { + return [[NSMutableDictionary alloc] init]; + } + return prefDict[key]; +} + void loadPreferences(BOOL reset) { assert(getenv("POJAV_HOME")); prefPath = [@(getenv("POJAV_HOME")) @@ -91,16 +104,14 @@ void loadPreferences(BOOL reset) { NSFileManager *fileManager = [NSFileManager defaultManager]; prefDict = [NSMutableDictionary dictionaryWithContentsOfFile:prefPath]; - if (reset || ![fileManager fileExistsAtPath:prefPath]) { + if (reset) { + [prefDict removeAllObjects]; + } else if (![fileManager fileExistsAtPath:prefPath]) { prefDict = [[NSMutableDictionary alloc] init]; - envPrefDict = [[NSMutableDictionary alloc] init]; - warnPrefDict = [[NSMutableDictionary alloc] init]; - } else { - envPrefDict = prefDict[@"env_vars"]; - warnPrefDict = prefDict[@"warnings"]; } - - assert(prefDict); + debugPrefDict = getDictionarySafe(@"debugs"); + envPrefDict = getDictionarySafe(@"env_vars"); + warnPrefDict = getDictionarySafe(@"warnings"); // set default value setDefaultValueForPref(envPrefDict, @"resolution", @(100)); @@ -132,9 +143,15 @@ void loadPreferences(BOOL reset) { setDefaultValueForPref(prefDict, @"disable_home_symlink", @YES); } + // Debug settings + setDefaultValueForPref(debugPrefDict, @"debug_ipad_ui", @(UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)); + setDefaultValueForPref(debugPrefDict, @"debug_show_layout_bounds", @NO); + setDefaultValueForPref(debugPrefDict, @"debug_show_layout_overlap", @NO); + // Migrate some prefs setPreference(@"java_home", [getPreference(@"java_home") lastPathComponent]); + prefDict[@"debugs"] = debugPrefDict; prefDict[@"env_vars"] = envPrefDict; prefDict[@"warnings"] = warnPrefDict; diff --git a/Natives/LauncherPreferencesViewController2.m b/Natives/LauncherPreferencesViewController2.m index 3240cd98..c952de60 100644 --- a/Natives/LauncherPreferencesViewController2.m +++ b/Natives/LauncherPreferencesViewController2.m @@ -41,7 +41,7 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); return self.navigationController != nil; }; - self.prefSections = @[@"general", @"video", @"control", @"java"]; + self.prefSections = @[@"general", @"video", @"control", @"java", @"debug"]; self.prefContents = @[ @[ @@ -209,6 +209,28 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); }, @"warnKey": @"mem_warn" } + ], @[ + @{@"key": @"debug_ipad_ui", + @"hasDetail": @YES, + @"icon": @"ipad", + @"type": self.typeSwitch, + @"enableCondition": whenNotInGame + }, + @{@"key": @"debug_show_layout_bounds", + @"hasDetail": @YES, + @"icon": @"square.dashed", + @"type": self.typeSwitch, + @"enableCondition": whenNotInGame, + @"requestReload": @YES + }, + @{@"key": @"debug_show_layout_overlap", + @"hasDetail": @YES, + @"icon": @"square.on.square", + @"type": self.typeSwitch, + @"enableCondition": ^BOOL(){ + return [getPreference(@"debug_show_layout_bounds") boolValue] && whenNotInGame(); + } + } ] ]; diff --git a/Natives/LauncherSplitViewController.m b/Natives/LauncherSplitViewController.m index a1d49227..a6f6fc6e 100644 --- a/Natives/LauncherSplitViewController.m +++ b/Natives/LauncherSplitViewController.m @@ -18,6 +18,7 @@ LauncherMenuViewController *masterVc = [[LauncherMenuViewController alloc] init]; UINavigationController *detailVc = [[UINavigationController alloc] init]; + detailVc.toolbarHidden = NO; self.presentsWithGesture = YES; self.preferredDisplayMode = UISplitViewControllerDisplayModeOneBesideSecondary; diff --git a/Natives/LauncherViewController.m b/Natives/LauncherViewController.m index 4d5fd2de..7a15649d 100644 --- a/Natives/LauncherViewController.m +++ b/Natives/LauncherViewController.m @@ -8,6 +8,8 @@ #include "utils.h" +#define AUTORESIZE_MASKS UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin + @interface LauncherViewController () { } @@ -29,28 +31,25 @@ int versionSelectedAt = 0; [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; [self setNeedsUpdateOfHomeIndicatorAutoHidden]; - CGRect screenBounds = self.view.frame; - - int width = (int) roundf(screenBounds.size.width); - int height = (int) roundf(screenBounds.size.height) - self.navigationController.navigationBar.frame.size.height; + UIToolbar *toolbar = self.navigationController.toolbar; + CGFloat splitWidth = toolbar.frame.size.width / 3; UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame]; [self.view addSubview:scrollView]; scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; UILabel *versionTextView = [[UILabel alloc] initWithFrame:CGRectMake(4.0, 4.0, 0.0, 0.0)]; - versionTextView.text = @"Minecraft version: "; + versionTextView.text = @"[under construction]"; versionTextView.numberOfLines = 0; [versionTextView sizeToFit]; [scrollView addSubview:versionTextView]; - versionTextField = [[UITextField alloc] initWithFrame:CGRectMake(versionTextView.bounds.size.width + 4.0, 4.0, width - versionTextView.bounds.size.width - 8.0, height - 58.0)]; + versionTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 4, splitWidth, toolbar.frame.size.height/2 - 4)]; [versionTextField addTarget:versionTextField action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; + versionTextField.autoresizingMask = AUTORESIZE_MASKS; versionTextField.placeholder = @"Specify version..."; versionTextField.text = (NSString *) getPreference(@"selected_version"); - versionTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop; - versionTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; - + versionTextField.textAlignment = NSTextAlignmentCenter; self.versionList = [[NSMutableArray alloc] init]; versionPickerView = [[UIPickerView alloc] init]; @@ -75,40 +74,47 @@ int versionSelectedAt = 0; versionTextField.inputAccessoryView = versionPickToolbar; versionTextField.inputView = versionPickerView; - [scrollView addSubview:versionTextField]; + [toolbar addSubview:versionTextField]; - if (@available(iOS 14.0, *)) { - // use UIMenu - self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[[UIImage systemImageNamed:@"ellipsis.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] style:UIBarButtonItemStyleDone target:self action:@selector(displayOptions:)]; - UIAction *option1 = [UIAction actionWithTitle:@"Launch a mod installer" image:[[UIImage systemImageNamed:@"internaldrive"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil - handler:^(__kindof UIAction * _Nonnull action) {[self enterModInstaller:self.navigationItem.rightBarButtonItem];}]; - UIAction *option2 = [UIAction actionWithTitle:@"Custom controls" image:[[UIImage systemImageNamed:@"dpad.right.fill"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil - handler:^(__kindof UIAction * _Nonnull action) {[self enterCustomControls];}]; - UIMenu *menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil - options:UIMenuOptionsDisplayInline children:@[option1, option2]]; - self.navigationItem.rightBarButtonItem.action = nil; - self.navigationItem.rightBarButtonItem.primaryAction = nil; - self.navigationItem.rightBarButtonItem.menu = menu; - } else { - self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Options" style:UIBarButtonItemStyleDone target:self action:@selector(displayOptions:)]; - } + self.progressViewMain = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, toolbar.frame.size.width, 4.0)]; + self.progressViewSub = [[UIProgressView alloc] initWithFrame:CGRectMake(0, toolbar.frame.size.height - 4.0, toolbar.frame.size.width, 4.0)]; + self.progressViewMain.autoresizingMask = self.progressViewSub.autoresizingMask = AUTORESIZE_MASKS; + self.progressViewMain.hidden = self.progressViewSub.hidden = YES; + [toolbar addSubview:self.progressViewMain]; + [toolbar addSubview:self.progressViewSub]; - self.progressViewMain = [[UIProgressView alloc] initWithFrame:CGRectMake(4.0, height - 58.0, width - 8.0, 6.0)]; - self.progressViewSub = [[UIProgressView alloc] initWithFrame:CGRectMake(4.0, height - 58.0, width - 8.0, 6.0)]; - self.progressViewMain.alpha = 0.6; - self.progressViewSub.alpha = 0.4; - [scrollView addSubview:self.progressViewSub]; - [scrollView addSubview:self.progressViewMain]; - - self.buttonInstall = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + self.buttonInstall = [UIButton buttonWithType:UIButtonTypeSystem]; self.buttonInstall.enabled = NO; - [self.buttonInstall setTitle:@"Play" forState:UIControlStateNormal]; - self.buttonInstall.frame = CGRectMake(10.0, height - 54.0, 100.0, 50.0); + [self.buttonInstall setTitle:NSLocalizedString(@"Play", nil) forState:UIControlStateNormal]; + self.buttonInstall.autoresizingMask = AUTORESIZE_MASKS; + self.buttonInstall.backgroundColor = [UIColor colorWithRed:54/255.0 green:176/255.0 blue:48/255.0 alpha:1.0]; + self.buttonInstall.layer.cornerRadius = 5; + self.buttonInstall.frame = CGRectMake(splitWidth, 6.0, splitWidth, toolbar.frame.size.height - 12.0); + self.buttonInstall.tintColor = UIColor.whiteColor; [self.buttonInstall addTarget:self action:@selector(launchMinecraft:) forControlEvents:UIControlEventTouchUpInside]; - [scrollView addSubview:self.buttonInstall]; - - self.progressText = [[UILabel alloc] initWithFrame:CGRectMake(120.0, height - 54.0, width - 124.0, 50.0)]; - [scrollView addSubview:self.progressText]; + [toolbar addSubview:self.buttonInstall]; + + UIButton *buttonCustomControls = [UIButton buttonWithType:UIButtonTypeSystem]; + [buttonCustomControls setTitle:NSLocalizedString(@"launcher.menu.custom_controls", nil) forState:UIControlStateNormal]; + buttonCustomControls.autoresizingMask = AUTORESIZE_MASKS; + buttonCustomControls.frame = CGRectMake(0, toolbar.frame.size.height/2, splitWidth, toolbar.frame.size.height/2); + [buttonCustomControls addTarget:self action:@selector(enterCustomControls) forControlEvents:UIControlEventTouchUpInside]; + [toolbar addSubview:buttonCustomControls]; + + UIButton *buttonInstallJar = [UIButton buttonWithType:UIButtonTypeSystem]; + [buttonInstallJar setTitle:NSLocalizedString(@"launcher.menu.install_jar", nil) forState:UIControlStateNormal]; + buttonInstallJar.autoresizingMask = AUTORESIZE_MASKS; + buttonInstallJar.frame = CGRectMake(splitWidth*2, toolbar.frame.size.height/2, splitWidth, toolbar.frame.size.height/2); + [buttonInstallJar addTarget:self action:@selector(enterModInstaller) forControlEvents:UIControlEventTouchUpInside]; + [toolbar addSubview:buttonInstallJar]; + + self.progressText = [[UILabel alloc] initWithFrame:CGRectMake(0, 4, toolbar.frame.size.width, 20)]; + self.progressText.adjustsFontSizeToFitWidth = YES; + self.progressText.autoresizingMask = AUTORESIZE_MASKS; + self.progressText.font = [self.progressText.font fontWithSize:16]; + self.progressText.textAlignment = NSTextAlignmentCenter; + self.progressText.userInteractionEnabled = NO; + [toolbar addSubview:self.progressText]; } - (void)viewWillAppear:(BOOL)animated { @@ -216,45 +222,14 @@ int versionSelectedAt = 0; [self reloadVersionList:sender.selectedSegmentIndex]; } -#pragma mark - Button click events -- (void)displayOptions:(UIBarButtonItem*)sender { - if (@available(iOS 14.0, *)) { - // use UIMenu - } else { - // use UIAlertController - UIAlertController *fullAlert = [UIAlertController alertControllerWithTitle:@"Options" message:@"" preferredStyle:UIAlertControllerStyleActionSheet]; - UIAlertAction *option1 = [UIAlertAction actionWithTitle:@"Launch a mod installer" style:UIAlertActionStyleDefault - handler:^(UIAlertAction * _Nonnull action) {[self enterModInstaller:self.navigationItem.rightBarButtonItem];}]; - UIAlertAction *option2 = [UIAlertAction actionWithTitle:@"Custom controls" style:UIAlertActionStyleDefault - handler:^(UIAlertAction * _Nonnull action) {[self enterCustomControls];}]; - fullAlert.popoverPresentationController.barButtonItem = sender; - [self presentViewController:fullAlert animated:YES completion:nil]; - [fullAlert addAction:option1]; - [fullAlert addAction:option2]; - } -} - +#pragma mark - Options - (void)enterCustomControls { - if (![getPreference(@"customctrl_warn") boolValue]) { - CustomControlsViewController *vc = [[CustomControlsViewController alloc] init]; - vc.modalPresentationStyle = UIModalPresentationOverFullScreen; - [self presentViewController:vc animated:YES completion:nil]; - return; - } - setPreference(@"customctrl_warn", @(NO)); - UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Warning" message:@"This option is unfinished, some might be incomplete or missing." preferredStyle:UIAlertControllerStyleActionSheet]; - if (alert.popoverPresentationController != nil) { - alert.popoverPresentationController.sourceView = self.view; - alert.popoverPresentationController.sourceRect = CGRectMake(self.view.frame.size.width-10.0, 0, 10, 10); - } - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { - [self enterCustomControls]; - }]; - [self presentViewController:alert animated:YES completion:nil]; - [alert addAction:ok]; + CustomControlsViewController *vc = [[CustomControlsViewController alloc] init]; + vc.modalPresentationStyle = UIModalPresentationOverFullScreen; + [self presentViewController:vc animated:YES completion:nil]; } -- (void)enterModInstaller:(UIBarButtonItem*)sender { +- (void)enterModInstaller { UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"com.sun.java-archive"] inMode:UIDocumentPickerModeImport]; documentPicker.delegate = self; @@ -277,7 +252,9 @@ int versionSelectedAt = 0; return; } + sender.alpha = 0.5; sender.enabled = NO; + self.progressViewMain.hidden = self.progressViewSub.hidden = NO; NSObject *object = [self.versionList objectAtIndex:[versionPickerView selectedRowInComponent:0]]; @@ -288,7 +265,10 @@ int versionSelectedAt = 0; self.progressViewMain.observedProgress = mainProgress; self.progressViewSub.observedProgress = progress; if (stage == nil) { + sender.alpha = 1; sender.enabled = YES; + self.progressText.text = nil; + self.progressViewMain.hidden = self.progressViewSub.hidden = YES; if (mainProgress != nil) { UIKit_launchMinecraftSurfaceVC(); } diff --git a/Natives/LoginViewController.m b/Natives/LoginViewController.m index 10115a5a..af0bb581 100644 --- a/Natives/LoginViewController.m +++ b/Natives/LoginViewController.m @@ -24,15 +24,10 @@ extern NSMutableDictionary *prefDict; #pragma mark - LoginViewController -@interface LoginViewController () { -} -@property (nonatomic, strong) ASWebAuthenticationSession *authVC; -@property (nonatomic, strong) UIActivityViewController *activityViewController; +@interface LoginViewController () {} @end @implementation LoginViewController -@synthesize authVC; -@synthesize activityViewController; - (void)viewDidLoad { [super viewDidLoad]; @@ -268,7 +263,7 @@ extern NSMutableDictionary *prefDict; - (void)loginMicrosoft { NSURL *url = [NSURL URLWithString:@"https://login.live.com/oauth20_authorize.srf?client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_url=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"]; - authVC = + ASWebAuthenticationSession *authVC = [[ASWebAuthenticationSession alloc] initWithURL:url callbackURLScheme:@"ms-xal-00000000402b5328" completionHandler:^(NSURL * _Nullable callbackURL, @@ -434,7 +429,8 @@ extern NSMutableDictionary *prefDict; { NSString *latestlogPath = [NSString stringWithFormat:@"file://%s/latestlog.old.txt", getenv("POJAV_HOME")]; NSLog(@"Path is %@", latestlogPath); - activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[@"latestlog.txt", [NSURL URLWithString:latestlogPath]] applicationActivities:nil]; + UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[@"latestlog.txt", [NSURL URLWithString:latestlogPath]] applicationActivities:nil]; + activityViewController.popoverPresentationController.barButtonItem = self.navigationItem.rightBarButtonItems[0]; [self presentViewController:activityViewController animated:YES completion:nil]; } diff --git a/Natives/MinecraftResourceUtils.m b/Natives/MinecraftResourceUtils.m index c076276d..37fe1b27 100644 --- a/Natives/MinecraftResourceUtils.m +++ b/Natives/MinecraftResourceUtils.m @@ -283,6 +283,7 @@ static AFURLSessionManager* manager; library[@"skip"] = @( // Exclude platform-dependant libraries library[@"downloads"][@"classifiers"] != nil || + library[@"natives"] != nil || // Exclude LWJGL libraries [library[@"name"] hasPrefix:@"org.lwjgl"] ); @@ -379,12 +380,15 @@ static AFURLSessionManager* manager; dispatch_group_t group = dispatch_group_create(); int downloadIndex = -1; - __block int jobsAvailable = 20; + __block int jobsAvailable = 10; for (NSString *name in assets[@"objects"]) { if (jobsAvailable < 0) { break; } else if (jobsAvailable == 0) { - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + //dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + while (jobsAvailable == 0) { + usleep(50000); + } } NSString *hash = assets[@"objects"][name][@"hash"]; diff --git a/Natives/UIKit+hook.m b/Natives/UIKit+hook.m new file mode 100644 index 00000000..1035043a --- /dev/null +++ b/Natives/UIKit+hook.m @@ -0,0 +1,124 @@ +#import +#import +#import "LauncherPreferences.h" + +void swizzle(Class class, SEL originalAction, SEL swizzledAction) { + method_exchangeImplementations(class_getInstanceMethod(class, originalAction), class_getInstanceMethod(class, swizzledAction)); +} + +__attribute__((constructor)) void hookConstructor(void) { + swizzle(UIDevice.class, @selector(userInterfaceIdiom), @selector(hook_userInterfaceIdiom)); + swizzle(UIToolbar.class, @selector(sizeThatFits:), @selector(hook_sizeThatFits:)); + swizzle(UIView.class, @selector(didMoveToSuperview), @selector(hook_didMoveToSuperview)); + swizzle(UIView.class, @selector(setFrame:), @selector(hook_setFrame:)); +} + +@implementation UIDevice(hook) + +- (UIUserInterfaceIdiom)hook_userInterfaceIdiom { + id value = getPreference(@"debug_ipad_ui"); + if (value == nil) { + return self.hook_userInterfaceIdiom; + } + return [value boolValue] ? UIUserInterfaceIdiomPad : UIUserInterfaceIdiomPhone; +} + +@end + +@implementation UIToolbar(hook) + +- (CGSize)hook_sizeThatFits:(CGSize)size { + // Make the toolbar taller + CGSize ret = [self hook_sizeThatFits:size]; + ret.height += 50; + return ret; +} + +@end + +@implementation UIView(hook) +const NSString *cornerLayerKey = @"cornerLayer"; + +- (void)updateCornerLayer { + CAShapeLayer *cornerLayer = objc_getAssociatedObject(self, &cornerLayerKey); + NSNumber *cornerWidth = @5; + NSNumber *cornerSpaceW = @(self.frame.size.width-10); + NSNumber *cornerSpaceH = @(self.frame.size.height-10); + cornerLayer.lineDashPattern = @[cornerWidth,cornerSpaceW,cornerWidth,@0,cornerWidth,cornerSpaceH,cornerWidth,@0,cornerWidth,cornerSpaceW,cornerWidth,@0,cornerWidth,cornerSpaceH,cornerWidth]; + cornerLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)].CGPath; + if ([getPreference(@"debug_show_layout_overlap") boolValue]) { + cornerLayer.fillColor = [UIColor.yellowColor colorWithAlphaComponent:0.1].CGColor; + } else { + cornerLayer.fillColor = nil; + } +} + +- (void)hook_didMoveToSuperview { + if ([getPreference(@"debug_show_layout_bounds") boolValue]) { + self.layer.borderWidth = 1; + self.layer.borderColor = UIColor.redColor.CGColor; + if (self.layer.sublayers.count == 0) { + CAShapeLayer *cornerLayer = [CAShapeLayer layer]; + cornerLayer.strokeColor = UIColor.blueColor.CGColor; + cornerLayer.lineWidth = 1; + [self.layer addSublayer:cornerLayer]; + objc_setAssociatedObject(self, &cornerLayerKey, cornerLayer, OBJC_ASSOCIATION_ASSIGN); + [self updateCornerLayer]; + } + } + [self hook_didMoveToSuperview]; +} + +- (void)hook_setFrame:(CGRect)frame { + [self hook_setFrame:frame]; + [self updateCornerLayer]; +} + +@end + +@implementation UIWindow(hook) + +// Simulate safe area on iPhones without notch +/* +- (UIEdgeInsets)safeAreaInsets { + return UIEdgeInsetsMake(0, 44, 21, 44); +} +*/ + +- (UIViewController *)visibleViewController { + UIViewController *current = self.rootViewController; + while (current.presentedViewController) { + if ([current.presentedViewController isKindOfClass:UIAlertController.class] || [current.presentedViewController isKindOfClass:NSClassFromString(@"UIInputWindowController")]) { + break; + } + current = current.presentedViewController; + } + if ([current isKindOfClass:UINavigationController.class]) { + return [(UINavigationController *)self.rootViewController visibleViewController]; + } else { + return current; + } +} + +@end + + +// This forces the navigation bar to keep its height (44dp) in landscape +@implementation UINavigationBar(forceFullHeightInLandscape) +- (BOOL)forceFullHeightInLandscape { + return YES; + //UIScreen.mainScreen.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPhone; +} +@end + +UIWindow* currentWindow() { + id delegate = UIApplication.sharedApplication.delegate; + if (@available(iOS 13.0, *)) { + delegate = UIApplication.sharedApplication.connectedScenes.anyObject.delegate; + } + return [delegate window]; +} + +UIViewController* currentVC() { + return currentWindow().visibleViewController; +} diff --git a/Natives/en.lproj/Localizable.strings b/Natives/en.lproj/Localizable.strings index d4cd14cf..bcf57199 100644 --- a/Natives/en.lproj/Localizable.strings +++ b/Natives/en.lproj/Localizable.strings @@ -8,6 +8,12 @@ "game.menu.log_output" = "Log output"; "game.menu.custom_controls" = "Custom controls"; +// The options button at the top-right corner on iOS 12 +"Options" = "Options"; + +// The play button +"Play" = "Play"; + // Version categories "Installed" = "Installed"; "Snapshot" = "Snapshot"; @@ -17,6 +23,9 @@ "init.migrateDir" = "The PojavLauncher data directory is now %@. All of your existing data has been moved to the new location."; +"launcher.menu.custom_controls" = "Custom controls"; +"launcher.menu.install_jar" = "Install .jar"; + "login.error.username.outOfRange" = "Username must be at least 3 characters and maximum 16 characters"; "login.alert.field.username" = "Username"; @@ -80,6 +89,8 @@ "preference.section.video" = "Video Settings"; "preference.section.footer.video" = "Auto renderer allows Minecraft to decide which renderer to use. Decreasing resolution reduces GPU workload for better performance."; "preference.section.control" = "Control customization"; +"preference.section.debug" = "UI Debugging settings"; +"preference.section.footer.debug" = "Aside from debugging, these settings may be useful for custom controls. You may need to restart the launcher for changes to take effect."; //"preference.section.footer.control" = ""; "preference.section.java" = "Java Tweaks"; //"preference.section.footer.java" = ""; @@ -141,3 +152,10 @@ "preference.warn.allocated_memory" = "Due to limitations in the operating system itself, this option may cause instability at this and higher values. Proceed with caution."; "preference.title.custom_env" = "Environment variables"; "preference.detail.custom_env" = "This option is under construction."; + +"preference.title.debug_ipad_ui" = "Unlock iPadOS UI"; +"preference.detail.debug_ipad_ui" = "Unlock iPad-exlusive UI (alert, keyboard, etc.) if enabled or iPhone UI if disabled."; +"preference.title.debug_show_layout_bounds" = "Show layout bounds"; +"preference.detail.debug_show_layout_bounds" = "Show clip bounds, margins"; +"preference.title.debug_show_layout_overlap" = "Show layout overlapping"; +"preference.detail.debug_show_layout_overlap" = "Overlay every view with a transparent color to reveal overlapping views."; diff --git a/Natives/main.m b/Natives/main.m index ebab390a..8c780267 100644 --- a/Natives/main.m +++ b/Natives/main.m @@ -124,7 +124,7 @@ void init_logDeviceAndVer(char *argument) { regLog("[Pre-init] Entitlements availability:"); printEntitlementAvailability(@"com.apple.developer.kernel.extended-virtual-addressing"); printEntitlementAvailability(@"com.apple.developer.kernel.increased-memory-limit"); - printEntitlementAvailability(@"com.apple.private.security.no-container"); + printEntitlementAvailability(@"com.apple.private.security.no-sandbox"); printEntitlementAvailability(@"dynamic-codesigning"); } @@ -341,7 +341,7 @@ int main(int argc, char * argv[]) { init_migrateToPlist("java_args", "overrideargs.txt"); // If sandbox is disabled, W^X JIT can be enabled by PojavLauncher itself - if (getEntitlementValue(@"com.apple.private.security.no-container")) { + if (getEntitlementValue(@"com.apple.private.security.no-sandbox")) { NSLog(@"[Pre-init] Sandbox is disabled, trying to enable JIT"); int pid; int ret = posix_spawnp(&pid, argv[0], NULL, NULL, argv, environ); diff --git a/Natives/utils.m b/Natives/utils.m index 20097f6e..a112ef57 100644 --- a/Natives/utils.m +++ b/Natives/utils.m @@ -9,52 +9,6 @@ BOOL getEntitlementValue(NSString *key); -@implementation UIWindow(ext) - -// Simulate safe area on iPhones without notch -/* -- (UIEdgeInsets)safeAreaInsets { - return UIEdgeInsetsMake(0, 44, 21, 44); -} -*/ - -- (UIViewController *)visibleViewController { - UIViewController *current = self.rootViewController; - while (current.presentedViewController) { - if ([current.presentedViewController isKindOfClass:UIAlertController.class] || [current.presentedViewController isKindOfClass:NSClassFromString(@"UIInputWindowController")]) { - break; - } - current = current.presentedViewController; - } - if ([current isKindOfClass:UINavigationController.class]) { - return [(UINavigationController *)self.rootViewController visibleViewController]; - } else { - return current; - } -} - -@end - - -// This forces the navigation bar to keep its height (44dp) in landscape -@implementation UINavigationBar(forceFullHeightInLandscape) -- (BOOL)forceFullHeightInLandscape { - return UIScreen.mainScreen.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPhone; -} -@end - -UIWindow* currentWindow() { - id delegate = UIApplication.sharedApplication.delegate; - if (@available(iOS 13.0, *)) { - delegate = UIApplication.sharedApplication.connectedScenes.anyObject.delegate; - } - return [delegate window]; -} - -UIViewController* currentVC() { - return currentWindow().visibleViewController; -} - BOOL isJITEnabled() { if (getEntitlementValue(@"dynamic-codesigning")) { return YES; diff --git a/README.md b/README.md index c17b1078..1fc5eaf5 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ The [PojavLauncher Website](https://pojav.ml) has extensive documentation on how Note: This is experimental, although game works smoothly, you should not set Render distance too much. 1. Setup the sideload app -- For iOS 14.0-15.5beta4 on compatible devices, [TrollStore](https://github.com/opa334/TrollStore) is recommended to keep PojavLauncher permanently signed and have JIT automatically enabled. +- For iOS 14.0-15.5beta4 on compatible devices, [TrollStore](https://github.com/opa334/TrollStore) is recommended to keep PojavLauncher permanently signed and have JIT enabled by itself. - Otherwise, install [AltStore](https://altstore.io). Others will also work. 2. Download an IPA build of PojavLauncher in the Actions tab. 3. Open the downloaded IPA in the sideload app to install. diff --git a/entitlements_ipa.xml b/entitlements_ipa.xml index 217ba4f1..24444c91 100644 --- a/entitlements_ipa.xml +++ b/entitlements_ipa.xml @@ -18,7 +18,7 @@ - com.apple.private.security.no-container + com.apple.private.security.no-sandbox