From 698ce11bcdfbbd960cb695fdf7cc9bb004d4158d Mon Sep 17 00:00:00 2001 From: Eilionoir Tunnicliff Date: Tue, 12 Jul 2022 23:37:42 -0400 Subject: [PATCH 01/10] Fix Elucubratus dpkg errors --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3f85c51d..b980da3f 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ PACKAGING = \ cp $(SOURCEDIR)/DEBIAN/postinst DEBIAN/postinst; \ ldid -S$(SOURCEDIR)/entitlements.xml Applications/PojavLauncher.app; \ cd $(OUTPUTDIR); \ - fakeroot dpkg-deb -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)_$(VERSION)_iphoneos-arm; \ + fakeroot dpkg-deb -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)_$(VERSION)_iphoneos-arm -Zlzma; \ mkdir -p $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64; \ cd $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64; \ mkdir -p {DEBIAN,$(IOS15PREF)/{Applications,usr/share/pojavlauncher/{accounts,instances/default,Library/{Application\ Support,Caches}}}}; \ @@ -107,7 +107,7 @@ PACKAGING = \ cp $(SOURCEDIR)/DEBIAN/postinst DEBIAN/postinst; \ ldid -S$(SOURCEDIR)/entitlements.xml $(IOS15PREF)/Applications/PojavLauncher.app; \ cd $(OUTPUTDIR); \ - fakeroot dpkg-deb -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64 + fakeroot dpkg-deb -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64 -Zlzma # Function to check directories DIRCHECK = \ From b497dc244844097db2d2896d76213a46eb59dfb4 Mon Sep 17 00:00:00 2001 From: Eilionoir Tunnicliff Date: Tue, 12 Jul 2022 23:43:58 -0400 Subject: [PATCH 02/10] oops, actually fix Elu dpkg this time --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b980da3f..683e51b9 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ PACKAGING = \ cp $(SOURCEDIR)/DEBIAN/postinst DEBIAN/postinst; \ ldid -S$(SOURCEDIR)/entitlements.xml Applications/PojavLauncher.app; \ cd $(OUTPUTDIR); \ - fakeroot dpkg-deb -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)_$(VERSION)_iphoneos-arm -Zlzma; \ + fakeroot dpkg-deb -Zlzma -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)_$(VERSION)_iphoneos-arm; \ mkdir -p $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64; \ cd $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64; \ mkdir -p {DEBIAN,$(IOS15PREF)/{Applications,usr/share/pojavlauncher/{accounts,instances/default,Library/{Application\ Support,Caches}}}}; \ @@ -107,7 +107,7 @@ PACKAGING = \ cp $(SOURCEDIR)/DEBIAN/postinst DEBIAN/postinst; \ ldid -S$(SOURCEDIR)/entitlements.xml $(IOS15PREF)/Applications/PojavLauncher.app; \ cd $(OUTPUTDIR); \ - fakeroot dpkg-deb -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64 -Zlzma + fakeroot dpkg-deb -Zlzma -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64 # Function to check directories DIRCHECK = \ From 0622423df433915ada00770ae8a95e8ad128314f Mon Sep 17 00:00:00 2001 From: Eilionoir Tunnicliff Date: Tue, 12 Jul 2022 23:47:21 -0400 Subject: [PATCH 03/10] ._. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 683e51b9..3586c8d2 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ PACKAGING = \ cp $(SOURCEDIR)/DEBIAN/postinst DEBIAN/postinst; \ ldid -S$(SOURCEDIR)/entitlements.xml Applications/PojavLauncher.app; \ cd $(OUTPUTDIR); \ - fakeroot dpkg-deb -Zlzma -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)_$(VERSION)_iphoneos-arm; \ + fakeroot dpkg-deb -Zxz -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)_$(VERSION)_iphoneos-arm; \ mkdir -p $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64; \ cd $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64; \ mkdir -p {DEBIAN,$(IOS15PREF)/{Applications,usr/share/pojavlauncher/{accounts,instances/default,Library/{Application\ Support,Caches}}}}; \ @@ -107,7 +107,7 @@ PACKAGING = \ cp $(SOURCEDIR)/DEBIAN/postinst DEBIAN/postinst; \ ldid -S$(SOURCEDIR)/entitlements.xml $(IOS15PREF)/Applications/PojavLauncher.app; \ cd $(OUTPUTDIR); \ - fakeroot dpkg-deb -Zlzma -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64 + fakeroot dpkg-deb -Zxz -b $(OUTPUTDIR)/net.kdt.pojavlauncher.$(1)-rootless_$(VERSION)_iphoneos-arm64 # Function to check directories DIRCHECK = \ From 5d8a136470be8155e047571518fb1cab6bac7aee Mon Sep 17 00:00:00 2001 From: khanhduytran0 Date: Sat, 16 Jul 2022 13:27:25 +0700 Subject: [PATCH 04/10] SurfaceViewController is now always the root vc Before launching game, SurfaceViewController is set to UIWindow.rootViewController, making it similar to "Restart before launching" in order to *attempt* to find out why touchscreen input does not work on certain devices. The restart option is still kept for debugging purpose. Some other tiny code tweaks --- Natives/MinecraftResourceUtils.m | 11 ++++------- Natives/SurfaceViewController.m | 9 +++------ Natives/ios_uikit_bridge.m | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Natives/MinecraftResourceUtils.m b/Natives/MinecraftResourceUtils.m index 743245de..dd2d5bc4 100644 --- a/Natives/MinecraftResourceUtils.m +++ b/Natives/MinecraftResourceUtils.m @@ -39,14 +39,11 @@ static AFURLSessionManager* manager; // Check if the account has permission to download + (BOOL)checkAccessWithDialog:(BOOL)show { // for now - if ([BaseAuthenticator.current.authData[@"username"] hasPrefix:@"Demo."] || BaseAuthenticator.current.authData[@"xboxGamertag"] != nil) { - return YES; - } else { - if (show) { - showDialog(viewController, @"Error", @"Minecraft can't be legally installed when logged in with a local account. Please switch to an online account to continue."); - } - return NO; + BOOL accessible = [BaseAuthenticator.current.authData[@"username"] hasPrefix:@"Demo."] || BaseAuthenticator.current.authData[@"xboxGamertag"] != nil; + if (!accessible && show) { + showDialog(viewController, @"Error", @"Minecraft can't be legally installed when logged in with a local account. Please switch to an online account to continue."); } + return accessible; } // Check SHA of the file diff --git a/Natives/SurfaceViewController.m b/Natives/SurfaceViewController.m index 60d5adfe..a98ef45b 100644 --- a/Natives/SurfaceViewController.m +++ b/Natives/SurfaceViewController.m @@ -113,8 +113,6 @@ BOOL slideableHotbar; CGRect screenBounds = [[UIScreen mainScreen] bounds]; CGFloat screenScale = [[UIScreen mainScreen] scale]; - UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; - resolutionScale = ((NSNumber *)getPreference(@"resolution")).floatValue / 100.0; slideableHotbar = [getPreference(@"slideable_hotbar") boolValue]; @@ -630,10 +628,8 @@ CGPoint lastCenterPoint; - (void)surfaceOnHover:(UIHoverGestureRecognizer *)sender API_AVAILABLE(ios(13.0)) { if (@available(iOS 13.0, *)) { - if (@available(iOS 14.0, *)) { - if(isGrabbing) { // Can't put into above if statement - return; - } + if (@available(iOS 14.0, *) && isGrabbing) { + return; } CGPoint point = [sender locationInView:self.rootView]; // NSLog(@"Mouse move!!"); @@ -864,6 +860,7 @@ int currentVisibility = 1; if (!held) { CallbackBridge_nativeSendScroll(0.0, -1.0); } + break; case SPECIALBTN_VIRTUALMOUSE: if (!isGrabbing && !held) { diff --git a/Natives/ios_uikit_bridge.m b/Natives/ios_uikit_bridge.m index 93d9861a..86ccb40e 100644 --- a/Natives/ios_uikit_bridge.m +++ b/Natives/ios_uikit_bridge.m @@ -121,9 +121,18 @@ void UIKit_launchMinecraftSurfaceVC() { usleep(1000); exit(0); } else { - SurfaceViewController *vc = [[SurfaceViewController alloc] init]; - vc.modalPresentationStyle = UIModalPresentationFullScreen; - [viewController.navigationController pushViewController:vc animated:YES]; + id delegate = UIApplication.sharedApplication.delegate; + if (@available(iOS 13.0, *)) { + delegate = UIApplication.sharedApplication.connectedScenes.anyObject.delegate; + } + UIWindow *window = [delegate window]; + [UIView animateWithDuration:0.2 animations:^{ + window.rootViewController.view.alpha = 0; + } completion:^(BOOL b){ + [window resignKeyWindow]; + window.rootViewController = [[SurfaceViewController alloc] init]; + [window makeKeyAndVisible]; + }]; } }); } From d8459effc08d041b87acf19c66ebd8bb28588955 Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh Date: Fri, 22 Jul 2022 08:02:40 +0700 Subject: [PATCH 05/10] WIP: New User interface UISplitViewController is being used to create split menu. Currently, autolayout is not yet done. Added account switcher to the split view. Fixes: - auth_session parameter for legacy Minecraft versions - Updating self.view background color is now automated by UIColor.systemBackgroundColor --- .../main/java/net/kdt/pojavlaunch/Tools.java | 1 + Natives/AboutLauncherViewController.m | 19 +-- Natives/AccountListViewController.h | 3 +- Natives/AccountListViewController.m | 9 +- Natives/CMakeLists.txt | 3 + Natives/CustomControlsViewController.m | 22 +-- Natives/JavaGUIViewController.m | 20 +-- Natives/LauncherFAQViewController.m | 18 +-- Natives/LauncherMenuViewController.h | 7 + Natives/LauncherMenuViewController.m | 138 ++++++++++++++++++ Natives/LauncherPreferencesViewController.m | 20 +-- Natives/LauncherSplitViewController.h | 5 + Natives/LauncherSplitViewController.m | 42 ++++++ Natives/LauncherViewController.m | 39 +---- Natives/LoginViewController.m | 53 +++---- Natives/SceneDelegate.m | 1 - Natives/SurfaceViewController.m | 22 ++- Natives/UpdateHistoryViewController.m | 21 +-- Natives/ios_uikit_bridge.m | 5 +- Natives/utils.h | 1 + Natives/utils.m | 8 + 21 files changed, 268 insertions(+), 189 deletions(-) create mode 100644 Natives/LauncherMenuViewController.h create mode 100644 Natives/LauncherMenuViewController.m create mode 100644 Natives/LauncherSplitViewController.h create mode 100644 Natives/LauncherSplitViewController.m diff --git a/JavaApp/src/main/java/net/kdt/pojavlaunch/Tools.java b/JavaApp/src/main/java/net/kdt/pojavlaunch/Tools.java index 12eb1a3e..e3965723 100644 --- a/JavaApp/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/JavaApp/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -158,6 +158,7 @@ public final class Tools gameDir.mkdirs(); Map varArgMap = new ArrayMap(); + varArgMap.put("auth_session", profile.accessToken); // For legacy versions of MC varArgMap.put("auth_access_token", profile.accessToken); varArgMap.put("auth_player_name", username); varArgMap.put("auth_uuid", profile.profileId); diff --git a/Natives/AboutLauncherViewController.m b/Natives/AboutLauncherViewController.m index b3066cff..5f9d9204 100644 --- a/Natives/AboutLauncherViewController.m +++ b/Natives/AboutLauncherViewController.m @@ -26,7 +26,7 @@ - (void)viewDidLoad { [super viewDidLoad]; - + setViewBackgroundColor(self.view); [self setTitle:@"About PojavLauncher"]; CGRect screenBounds = [[UIScreen mainScreen] bounds]; @@ -40,13 +40,6 @@ [self.view addSubview:scrollView]; scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - // Update color mode once - if(@available(iOS 13.0, *)) { - [self traitCollectionDidChange:nil]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - // TODO: Move this into utils? struct utsname systemInfo; uname(&systemInfo); @@ -125,14 +118,4 @@ [self.navigationController pushViewController:vc animated:YES]; } --(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - if(@available(iOS 13.0, *)) { - if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - self.view.backgroundColor = [UIColor blackColor]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - } -} - @end diff --git a/Natives/AccountListViewController.h b/Natives/AccountListViewController.h index 7ab063c2..4d1f418a 100644 --- a/Natives/AccountListViewController.h +++ b/Natives/AccountListViewController.h @@ -1,6 +1,7 @@ #import -@interface AccountListViewController : UITableViewController +@interface AccountListViewController: UITableViewController + @property (nonatomic, copy) void (^whenDelete)(NSString* name); @property(nonatomic, copy) void (^whenItemSelected)(NSString* name); diff --git a/Natives/AccountListViewController.m b/Natives/AccountListViewController.m index d8247888..6b444dfb 100644 --- a/Natives/AccountListViewController.m +++ b/Natives/AccountListViewController.m @@ -58,8 +58,10 @@ } NSDictionary *selected = self.accountList[indexPath.row]; + // By default, display the saved username cell.textLabel.text = selected[@"username"]; if ([selected[@"username"] hasPrefix:@"Demo."]) { + // Remove the prefix "Demo." cell.textLabel.text = [selected[@"username"] substringFromIndex:5]; cell.detailTextLabel.text = NSLocalizedString(@"login.option.demo", nil); } else if (selected[@"xboxGamertag"] == nil) { @@ -85,7 +87,7 @@ if (editingStyle == UITableViewCellEditingStyleDelete) { // TODO: invalidate token - NSString *str = [self.accountList objectAtIndex:indexPath.row][@"username"]; + NSString *str = self.accountList[indexPath.row][@"username"]; NSFileManager *fm = [NSFileManager defaultManager]; NSString *path = [NSString stringWithFormat:@"%s/accounts/%@.json", getenv("POJAV_HOME"), str]; if (self.whenDelete != nil) { @@ -97,4 +99,9 @@ } } +#pragma mark - UIPopoverPresentationControllerDelegate +- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { + return UIModalPresentationNone; +} + @end diff --git a/Natives/CMakeLists.txt b/Natives/CMakeLists.txt index 89b1b8b3..1de7337a 100644 --- a/Natives/CMakeLists.txt +++ b/Natives/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable(PojavLauncher external/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m external/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m external/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m + external/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m external/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m main.m @@ -87,8 +88,10 @@ add_executable(PojavLauncher JavaGUIViewController.m KeyboardInput.m LauncherFAQViewController.m + LauncherMenuViewController.m LauncherPreferences.m LauncherPreferencesViewController.m + LauncherSplitViewController.m LauncherViewController.m LoginViewController.m MinecraftResourceUtils.m diff --git a/Natives/CustomControlsViewController.m b/Natives/CustomControlsViewController.m index 6a5d7f6d..72a4d9bf 100644 --- a/Natives/CustomControlsViewController.m +++ b/Natives/CustomControlsViewController.m @@ -86,19 +86,12 @@ NSMutableArray *keyCodeMap, *keyValueMap; - (void)viewDidLoad { [super viewDidLoad]; + viewController = self; isControlModifiable = YES; - [self.navigationController setNavigationBarHidden:YES animated:YES]; [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; [self setNeedsUpdateOfHomeIndicatorAutoHidden]; - // Update color mode once - if(@available(iOS 13.0, *)) { - [self traitCollectionDidChange:nil]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - CGRect screenBounds = [[UIScreen mainScreen] bounds]; UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; @@ -203,8 +196,7 @@ NSMutableArray *keyCodeMap, *keyValueMap; } - (void)actionMenuExit { - [self.navigationController setNavigationBarHidden:NO animated:YES]; - [self.navigationController popViewControllerAnimated:YES]; + [self dismissViewControllerAnimated:YES completion:nil]; } - (void)actionMenuSave { @@ -437,16 +429,6 @@ NSMutableArray *keyCodeMap, *keyValueMap; } } --(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - if(@available(iOS 13.0, *)) { - if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - self.view.backgroundColor = [UIColor blackColor]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - } -} - #pragma mark - UIPopoverPresentationControllerDelegate - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { return UIModalPresentationNone; diff --git a/Natives/JavaGUIViewController.m b/Natives/JavaGUIViewController.m index 883a9dfe..6e113a4f 100644 --- a/Natives/JavaGUIViewController.m +++ b/Natives/JavaGUIViewController.m @@ -69,7 +69,8 @@ const void * _CGDataProviderGetBytePointerCallbackAWT(void *info) { - (void)viewDidLoad { [super viewDidLoad]; - + viewController = self; + self.view.backgroundColor = UIColor.blackColor; [self.navigationController setNavigationBarHidden:YES animated:NO]; [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; [self setNeedsUpdateOfHomeIndicatorAutoHidden]; @@ -95,13 +96,6 @@ const void * _CGDataProviderGetBytePointerCallbackAWT(void *info) { [self.view addSubview:scrollView]; - // Update color mode once - if(@available(iOS 13.0, *)) { - [self traitCollectionDidChange:nil]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(surfaceOnClick:)]; tapGesture.delegate = self; @@ -164,14 +158,4 @@ const void * _CGDataProviderGetBytePointerCallbackAWT(void *info) { return NO; } --(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - if(@available(iOS 13.0, *)) { - if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - self.view.backgroundColor = [UIColor blackColor]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - } -} - @end diff --git a/Natives/LauncherFAQViewController.m b/Natives/LauncherFAQViewController.m index 6717c4e0..7b64adb8 100644 --- a/Natives/LauncherFAQViewController.m +++ b/Natives/LauncherFAQViewController.m @@ -12,7 +12,7 @@ CGFloat faqcurrY = 4.0; - (void)viewDidLoad { [super viewDidLoad]; - + setViewBackgroundColor(self.view); [self setTitle:@"Launcher FAQ"]; CGRect screenBounds = [[UIScreen mainScreen] bounds]; @@ -25,12 +25,6 @@ CGFloat faqcurrY = 4.0; [self.view addSubview:scrollView]; scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - // Update color mode once - if(@available(iOS 13.0, *)) { - [self traitCollectionDidChange:nil]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } NSString *snapheadingtext = @"Vanilla versions after 21w08b"; faqcurrY = 4.0; @@ -67,14 +61,4 @@ CGFloat faqcurrY = 4.0; return label; } --(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - if(@available(iOS 13.0, *)) { - if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - self.view.backgroundColor = [UIColor blackColor]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - } -} - @end diff --git a/Natives/LauncherMenuViewController.h b/Natives/LauncherMenuViewController.h new file mode 100644 index 00000000..ff66f69a --- /dev/null +++ b/Natives/LauncherMenuViewController.h @@ -0,0 +1,7 @@ +#import + +@interface LauncherMenuViewController : UITableViewController + +@property NSString* listPath; + +@end diff --git a/Natives/LauncherMenuViewController.m b/Natives/LauncherMenuViewController.m new file mode 100644 index 00000000..0a3c15f9 --- /dev/null +++ b/Natives/LauncherMenuViewController.m @@ -0,0 +1,138 @@ +#import "authenticator/BaseAuthenticator.h" +#import "AccountListViewController.h" +#import "LauncherMenuViewController.h" +#import "LauncherPreferencesViewController.h" +#import "LauncherViewController.h" +#import "UIButton+AFNetworking.h" +#import "UIImageView+AFNetworking.h" + +@interface LauncherMenuViewController () { +} + +@property NSArray *options; +@end + +@implementation LauncherMenuViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + // View controllers are put into an array to keep its state + self.options = @[ + [[LauncherViewController alloc] init], + [[LauncherPreferencesViewController alloc] init] + ]; + self.options[0].title = @"PojavLauncher"; + self.options[1].title = @"Preferences"; + //@[@"News", @"Development Console", @"Crash logs"]; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + + UIButton *titleButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [titleButton addTarget:self action:@selector(selectAccount:) forControlEvents:UIControlEventTouchUpInside]; + titleButton.frame = self.navigationController.navigationBar.frame; + titleButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; + if (@available(iOS 14.0, tvOS 14.0, *)) { + // On iOS 14+, a special button is displayed to toggle the visibility of the master view controller. + // It creates a bit more offset while in the process of swipe/animation. Therefore the left inset + // has to be increased a bit for this button. + titleButton.contentEdgeInsets = UIEdgeInsetsMake(4, 4, 4, 8); + } else { + titleButton.contentEdgeInsets = UIEdgeInsetsMake(4, 0, 4, 8); + } + titleButton.titleEdgeInsets = UIEdgeInsetsMake(0, 4, 0, -4); + titleButton.imageView.contentMode = UIViewContentModeScaleAspectFit; + titleButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping; + self.navigationItem.titleView = titleButton; + + [self updateAccountInfo]; + [self tableView:self.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + // workaround while investigating for issue + if (self.splitViewController.viewControllers.count < 2) return; + + // Restore the selected row when the view appears again + int index = [self.options indexOfObject:[self.splitViewController.viewControllers[1] viewControllers][0]]; + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; + [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.options.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; + if (cell == nil) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; + } + + cell.textLabel.text = [self.options[indexPath.row] title]; + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + UIViewController *selected = self.options[indexPath.row]; + [self.splitViewController.viewControllers[1] setViewControllers:@[selected] animated:NO]; //YES? + + if (@available(iOS 14.0, tvOS 14.0, *)) { + // It is unnecessary to put the toggle button as it is automated on iOS 14+ + } else { + selected.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem; + selected.navigationItem.leftItemsSupplementBackButton = true; + } + + // This button is placed temporarily while migrating to the new UI + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"❌" style:UIBarButtonItemStyleDone target:self.splitViewController action:@selector(dismissViewController)]; +} + +- (void)selectAccount:(UIButton *)sender { + AccountListViewController *vc = [[AccountListViewController alloc] init]; + vc.whenItemSelected = ^void(NSString* name) { + // TODO: perform token refreshing + [BaseAuthenticator loadSavedName:name]; + [self updateAccountInfo]; + }; + vc.modalPresentationStyle = UIModalPresentationPopover; + vc.preferredContentSize = CGSizeMake(350, 250); + + UIPopoverPresentationController *popoverController = vc.popoverPresentationController; + popoverController.sourceView = sender; + popoverController.sourceRect = sender.bounds; + popoverController.permittedArrowDirections = UIPopoverArrowDirectionAny; + popoverController.delegate = vc; + [self presentViewController:vc animated:YES completion:nil]; +} + +- (void)updateAccountInfo { + NSDictionary *selected = BaseAuthenticator.current.authData; + BOOL isDemo = [selected[@"username"] hasPrefix:@"Demo."]; + NSMutableAttributedString *title = [[NSMutableAttributedString alloc] initWithString:[selected[@"username"] substringFromIndex:(isDemo?5:0)]]; + id subtitle; + if (isDemo) { + // Remove the prefix "Demo." + subtitle = NSLocalizedString(@"login.option.demo", nil); + } else if (selected[@"xboxGamertag"] == nil) { + subtitle = NSLocalizedString(@"login.option.local", nil); + } else { + // Display the Xbox gamertag for online accounts + subtitle = selected[@"xboxGamertag"]; + } + subtitle = [[NSAttributedString alloc] initWithString:subtitle attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12]}]; + [title appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:nil]]; + [title appendAttributedString:subtitle]; + [(UIButton *)self.navigationItem.titleView setAttributedTitle:title forState:UIControlStateNormal]; + + NSURL *url = [NSURL URLWithString:[selected[@"profilePicURL"] stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"]]; + UIImage *placeholder = [UIImage imageNamed:@"default_account_pfp"]; + [(UIButton *)self.navigationItem.titleView setImageForState:UIControlStateNormal withURL:url placeholderImage:placeholder]; + [((UIButton *)self.navigationItem.titleView).imageView setImageWithURL:url placeholderImage:placeholder]; +} + +@end diff --git a/Natives/LauncherPreferencesViewController.m b/Natives/LauncherPreferencesViewController.m index a5d76b9c..19898195 100644 --- a/Natives/LauncherPreferencesViewController.m +++ b/Natives/LauncherPreferencesViewController.m @@ -102,8 +102,7 @@ int tempIndex; - (void)viewDidLoad { [super viewDidLoad]; - - [self setTitle:@"Preferences"]; + setViewBackgroundColor(self.view); CGRect screenBounds = [[UIScreen mainScreen] bounds]; CGFloat screenScale = [[UIScreen mainScreen] scale]; @@ -137,13 +136,6 @@ int tempIndex; blur = [UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular]; blurView = [[UIVisualEffectView alloc] initWithEffect:blur]; - - // Update color mode once - if(@available(iOS 13.0, *)) { - [self traitCollectionDidChange:nil]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } UILabel *btnsizeTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY, 0.0, 30.0)]; btnsizeTextView.text = @"Button scale (%)"; @@ -1018,16 +1010,6 @@ int tempIndex; } } --(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - if(@available(iOS 13.0, *)) { - if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - self.view.backgroundColor = [UIColor blackColor]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - } -} - #pragma mark - UIPopoverPresentationControllerDelegate - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { return UIModalPresentationNone; diff --git a/Natives/LauncherSplitViewController.h b/Natives/LauncherSplitViewController.h new file mode 100644 index 00000000..19515fcf --- /dev/null +++ b/Natives/LauncherSplitViewController.h @@ -0,0 +1,5 @@ +#import + +@interface LauncherSplitViewController : UISplitViewController + +@end diff --git a/Natives/LauncherSplitViewController.m b/Natives/LauncherSplitViewController.m new file mode 100644 index 00000000..f27c3a66 --- /dev/null +++ b/Natives/LauncherSplitViewController.m @@ -0,0 +1,42 @@ +#import "LauncherSplitViewController.h" +#import "LauncherMenuViewController.h" +#import "LauncherViewController.h" +#import "utils.h" + +@interface LauncherSplitViewController () { +} + +@end + +@implementation LauncherSplitViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + viewController = self; + setViewBackgroundColor(self.view); + + //TODO: maximumPrimaryColumnWidth + + LauncherMenuViewController *masterVc = [[LauncherMenuViewController alloc] init]; + UINavigationController *detailVc = [[UINavigationController alloc] init]; + + self.presentsWithGesture = YES; + self.preferredDisplayMode = UISplitViewControllerDisplayModeOneBesideSecondary; + self.viewControllers = @[[[UINavigationController alloc] initWithRootViewController:masterVc], detailVc]; + if (@available(iOS 14.0, tvOS 14.0, *)) { + self.preferredSplitBehavior = UISplitViewControllerSplitBehaviorTile; + } +} + +- (UITraitCollection *)traitCollection { + // Allows splitting on compact-sized iPhones + UITraitCollection *collection = super.traitCollection; + UITraitCollection *horizontalCollection = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular]; + return [UITraitCollection traitCollectionWithTraitsFromCollections:@[collection, horizontalCollection]]; +} + +- (void)dismissViewController { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +@end diff --git a/Natives/LauncherViewController.m b/Natives/LauncherViewController.m index 8c538980..8097dc4f 100644 --- a/Natives/LauncherViewController.m +++ b/Natives/LauncherViewController.m @@ -28,8 +28,6 @@ int versionSelectedAt = 0; [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; [self setNeedsUpdateOfHomeIndicatorAutoHidden]; - - [self setTitle:@"PojavLauncher"]; CGRect screenBounds = [[UIScreen mainScreen] bounds]; CGFloat screenScale = [[UIScreen mainScreen] scale]; @@ -41,13 +39,6 @@ int versionSelectedAt = 0; [self.view addSubview:scrollView]; scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - // Update color mode once - if(@available(iOS 13.0, *)) { - [self traitCollectionDidChange:nil]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - UILabel *versionTextView = [[UILabel alloc] initWithFrame:CGRectMake(4.0, 4.0, 0.0, 0.0)]; versionTextView.text = @"Minecraft version: "; versionTextView.numberOfLines = 0; @@ -82,10 +73,8 @@ int versionSelectedAt = 0; 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];}]; - UIAction *option3 = [UIAction actionWithTitle:@"Preferences" image:[[UIImage systemImageNamed:@"wrench.and.screwdriver"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil - handler:^(__kindof UIAction * _Nonnull action) {[self enterPreferences];}]; UIMenu *menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil - options:UIMenuOptionsDisplayInline children:@[option1, option2, option3]]; + options:UIMenuOptionsDisplayInline children:@[option1, option2]]; self.navigationItem.rightBarButtonItem.action = nil; self.navigationItem.rightBarButtonItem.primaryAction = nil; self.navigationItem.rightBarButtonItem.menu = menu; @@ -201,20 +190,17 @@ int versionSelectedAt = 0; handler:^(UIAlertAction * _Nonnull action) {[self enterModInstaller:self.navigationItem.rightBarButtonItem];}]; UIAlertAction *option2 = [UIAlertAction actionWithTitle:@"Custom controls" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self enterCustomControls];}]; - UIAlertAction *option3 = [UIAlertAction actionWithTitle:@"Preferences" style:UIAlertActionStyleDefault - handler:^(UIAlertAction * _Nonnull action) {[self enterPreferences];}]; fullAlert.popoverPresentationController.barButtonItem = sender; [self presentViewController:fullAlert animated:YES completion:nil]; [fullAlert addAction:option1]; [fullAlert addAction:option2]; - [fullAlert addAction:option3]; } } - (void)enterCustomControls { if (![getPreference(@"customctrl_warn") boolValue]) { CustomControlsViewController *vc = [[CustomControlsViewController alloc] init]; - [self.navigationController pushViewController:vc animated:YES]; + [self presentViewController:vc animated:YES completion:nil]; return; } setPreference(@"customctrl_warn", @(NO)); @@ -225,7 +211,7 @@ int versionSelectedAt = 0; } UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { CustomControlsViewController *vc = [[CustomControlsViewController alloc] init]; - [self.navigationController pushViewController:vc animated:YES]; + [self presentViewController:vc animated:YES completion:nil]; }]; [self presentViewController:alert animated:YES completion:nil]; [alert addAction:ok]; @@ -257,18 +243,12 @@ int versionSelectedAt = 0; JavaGUIViewController *vc = [[JavaGUIViewController alloc] init]; vc.filepath = url.path; NSLog(@"ModInstaller: launching %@", vc.filepath); - [self.navigationController pushViewController:vc animated:YES]; + [self presentViewController:vc animated:YES completion:nil]; } } -- (void)enterPreferences { - LauncherPreferencesViewController *vc = [[LauncherPreferencesViewController alloc] init]; - [self.navigationController pushViewController:vc animated:YES]; -} - - (void)launchMinecraft:(UIButton *)sender { sender.enabled = NO; - [self.navigationItem setHidesBackButton:YES animated:YES]; NSObject *object = [versionList objectAtIndex:[versionPickerView selectedRowInComponent:0]]; @@ -282,7 +262,6 @@ int versionSelectedAt = 0; = progress; if (stage == nil) { sender.enabled = YES; - [self.navigationItem setHidesBackButton:NO animated:YES]; if (mainProgress != nil) { UIKit_launchMinecraftSurfaceVC(); } @@ -335,14 +314,4 @@ int versionSelectedAt = 0; return NO; } -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - if(@available(iOS 13.0, *)) { - if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - self.view.backgroundColor = [UIColor blackColor]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - } -} - @end diff --git a/Natives/LoginViewController.m b/Natives/LoginViewController.m index 418baea8..46eb2faa 100644 --- a/Natives/LoginViewController.m +++ b/Natives/LoginViewController.m @@ -2,27 +2,26 @@ #include #import - #import "authenticator/BaseAuthenticator.h" #import "AppDelegate.h" -#import "LauncherViewController.h" -#import "LoginViewController.h" #import "AboutLauncherViewController.h" #import "AccountListViewController.h" #import "LauncherFAQViewController.h" -#import "LauncherPreferences.h" +#import "LauncherSplitViewController.h" +#import "LoginViewController.h" #import "UpdateHistoryViewController.h" -#include "ios_uikit_bridge.h" -#include "utils.h" +#import "LauncherPreferences.h" +#import "ios_uikit_bridge.h" +#import "utils.h" #define TYPE_SELECTACC 0 #define TYPE_MICROSOFT 1 #define TYPE_OFFLINE 2 #pragma mark - LoginViewController -@interface LoginViewController () { +@interface LoginViewController () { } @property (nonatomic, strong) ASWebAuthenticationSession *authVC; @property (nonatomic, strong) UIActivityViewController *activityViewController; @@ -34,12 +33,10 @@ - (void)viewDidLoad { [super viewDidLoad]; + setViewBackgroundColor(self.view); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(msaLoginCallback:) name:@"MSALoginCallback" object:nil]; - UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain target:nil action:nil]; - [self.navigationItem setBackBarButtonItem:backItem]; - CGRect screenBounds = [[UIScreen mainScreen] bounds]; int width = (int) roundf(screenBounds.size.width); @@ -50,12 +47,6 @@ scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; [self.view addSubview:scrollView]; - if(@available(iOS 13.0, *)) { - [self traitCollectionDidChange:nil]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - if(getenv("POJAV_DETECTEDJB")) { if(strcmp(getenv("POJAV_DETECTEDJB"), "Other") == 0 && [getPreference(@"jb_warn") boolValue] == YES) { UIAlertController *jbAlert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"login.warn.title.otherjb", nil) message:NSLocalizedString(@"login.warn.message.otherjb", nil) preferredStyle:UIAlertControllerStyleAlert]; @@ -85,7 +76,7 @@ [dateFormatter setDateFormat:@"MM-dd"]; NSString* date = [dateFormatter stringFromDate:[NSDate date]]; - if(@available (iOS 14.0, *)) { + if(@available(iOS 14.0, *)) { self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[[UIImage systemImageNamed:@"info.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] style:UIBarButtonItemStyleDone target:self action:@selector(aboutLauncher)]; UIAction *option1 = [UIAction actionWithTitle:NSLocalizedString(@"login.menu.about", nil) image:[[UIImage systemImageNamed:@"eyes.inverse"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self aboutLauncher];}]; @@ -191,16 +182,6 @@ } } -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - if (@available(iOS 13.0, *)) { - if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - self.view.backgroundColor = [UIColor blackColor]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - } -} - - (void)displayProgress:(NSString *)title { self.title = title; UIActivityIndicatorViewStyle style; @@ -260,10 +241,15 @@ id callback = ^(BOOL success) { self.title = @""; self.navigationItem.leftBarButtonItem = nil; - if (success) { - LauncherViewController *vc = [[LauncherViewController alloc] init]; - [self.navigationController pushViewController:vc animated:YES]; + if (!success) return; + + LauncherSplitViewController *splitVc; + if (@available(iOS 14.0, tvOS 14.0, *)) { + splitVc = [[LauncherSplitViewController alloc] initWithStyle:UISplitViewControllerStyleDoubleColumn]; + } else { + splitVc = [[LauncherSplitViewController alloc] init]; } + [self presentViewController:splitVc animated:YES completion:nil]; }; if (type == TYPE_SELECTACC) { [auth refreshTokenWithCallback:callback]; @@ -376,7 +362,7 @@ UIPopoverPresentationController *popoverController = [vc popoverPresentationController]; [self setPopoverProperties:popoverController sender:sender]; popoverController.permittedArrowDirections = UIPopoverArrowDirectionAny; - popoverController.delegate = self; + popoverController.delegate = vc; [self presentViewController:vc animated:YES completion:nil]; } @@ -413,11 +399,6 @@ } } -#pragma mark - UIPopoverPresentationControllerDelegate -- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { - return UIModalPresentationNone; -} - #pragma mark - ASWebAuthenticationPresentationContextProviding - (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session API_AVAILABLE(ios(13.0)){ return UIApplication.sharedApplication.windows.firstObject; diff --git a/Natives/SceneDelegate.m b/Natives/SceneDelegate.m index 032fecf9..e4c7bce9 100644 --- a/Natives/SceneDelegate.m +++ b/Natives/SceneDelegate.m @@ -16,7 +16,6 @@ - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)) API_AVAILABLE(ios(13.0)) API_AVAILABLE(ios(13.0)){ - if (@available(iOS 13.0, *)) { UIWindowScene *windowScene = (UIWindowScene *)scene; self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; diff --git a/Natives/SurfaceViewController.m b/Natives/SurfaceViewController.m index a98ef45b..efb813cc 100644 --- a/Natives/SurfaceViewController.m +++ b/Natives/SurfaceViewController.m @@ -60,6 +60,20 @@ BOOL slideableHotbar; return [self positionFromPosition:self.beginningOfDocument offset:clamp(start, 20, self.text.length - 20)]; } +- (void)deleteBackward { + [super deleteBackward]; + +} + +- (BOOL)hasText { + return YES; +} + +- (BOOL)insertText:(NSString *)text { + [super insertText:text]; + +} + - (void)setText:(NSString *)text { [super setText:text]; self.lockPos = [self positionFromPosition:self.beginningOfDocument offset:20]; @@ -362,6 +376,9 @@ CGPoint lastCenterPoint; self.rootView.transform = CGAffineTransformScale(CGAffineTransformIdentity, scale, scale); self.menuView.frame = CGRectMake(self.rootView.frame.size.width, self.rootView.frame.origin.y, self.menuView.frame.size.width, self.menuView.frame.size.height); + // scale is in range of 0.7-1 + // 1.1 - scale produces in range of 0.4-0.1 + // result in transform scale range of 1-0.25 self.menuView.transform = CGAffineTransformScale(CGAffineTransformIdentity, (1.1-scale)*2.5, (1.1-scale)*2.5); } else { CGPoint velocity = [sender velocityInView:sender.view]; @@ -370,6 +387,7 @@ CGPoint lastCenterPoint; // calculate duration to produce smooth movement // FIXME: any better way? CGFloat duration = fabs(self.rootView.center.x - centerX * scale) / centerX + 0.1; + duration = MIN(0.4, duration); //(110 - MIN(100, fabs(velocity.x))) / 100 [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseOut @@ -628,8 +646,8 @@ CGPoint lastCenterPoint; - (void)surfaceOnHover:(UIHoverGestureRecognizer *)sender API_AVAILABLE(ios(13.0)) { if (@available(iOS 13.0, *)) { - if (@available(iOS 14.0, *) && isGrabbing) { - return; + if (@available(iOS 14.0, *)) { + if (isGrabbing) return; } CGPoint point = [sender locationInView:self.rootView]; // NSLog(@"Mouse move!!"); diff --git a/Natives/UpdateHistoryViewController.m b/Natives/UpdateHistoryViewController.m index b353cc34..d00d898d 100644 --- a/Natives/UpdateHistoryViewController.m +++ b/Natives/UpdateHistoryViewController.m @@ -18,6 +18,7 @@ CGFloat uhcurrY = 4.0; - (void)viewDidLoad { [super viewDidLoad]; + setViewBackgroundColor(self.view); [self setTitle:@"Update History"]; @@ -32,15 +33,8 @@ CGFloat uhcurrY = 4.0; [self.view addSubview:scrollView]; scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - // Update color mode once - if(@available(iOS 13.0, *)) { - [self traitCollectionDidChange:nil]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - uhcurrY = 4.0; - + [scrollView addSubview:[self uhContent:true size:25 text:@"Current version" width:width]]; [scrollView addSubview:[self uhContent:true size:20 text:[NSString stringWithFormat:@"2.1 (%s)", CONFIG_TYPE] width:width]]; [scrollView addSubview:[self uhContent:true size:17 text:@"Changes" width:width]]; @@ -136,15 +130,4 @@ CGFloat uhcurrY = 4.0; return label; } - --(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { - if(@available(iOS 13.0, *)) { - if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - self.view.backgroundColor = [UIColor blackColor]; - } else { - self.view.backgroundColor = [UIColor whiteColor]; - } - } -} - @end diff --git a/Natives/ios_uikit_bridge.m b/Natives/ios_uikit_bridge.m index 86ccb40e..b748deb6 100644 --- a/Natives/ios_uikit_bridge.m +++ b/Natives/ios_uikit_bridge.m @@ -127,10 +127,11 @@ void UIKit_launchMinecraftSurfaceVC() { } UIWindow *window = [delegate window]; [UIView animateWithDuration:0.2 animations:^{ - window.rootViewController.view.alpha = 0; + window.alpha = 0; } completion:^(BOOL b){ [window resignKeyWindow]; - window.rootViewController = [[SurfaceViewController alloc] init]; + window.alpha = 1; + window.rootViewController = [[SurfaceViewController alloc] init]; [window makeKeyAndVisible]; }]; } diff --git a/Natives/utils.h b/Natives/utils.h index a2bf0d9e..941fd5fd 100644 --- a/Natives/utils.h +++ b/Natives/utils.h @@ -64,6 +64,7 @@ CGFloat MathUtils_dist(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2); CGFloat MathUtils_map(CGFloat x, CGFloat in_min, CGFloat in_max, CGFloat out_min, CGFloat out_max); CGFloat dpToPx(CGFloat dp); CGFloat pxToDp(CGFloat px); +void setViewBackgroundColor(UIView* view); void _CGDataProviderReleaseBytePointerCallback(void *info,const void *pointer); jboolean attachThread(bool isAndroid, JNIEnv** secondJNIEnvPtr); diff --git a/Natives/utils.m b/Natives/utils.m index dd81a401..e65462c6 100644 --- a/Natives/utils.m +++ b/Natives/utils.m @@ -102,6 +102,14 @@ CGFloat pxToDp(CGFloat px) { return px / screenScale; } +void setViewBackgroundColor(UIView* view) { + if(@available(iOS 13.0, *)) { + view.backgroundColor = UIColor.systemBackgroundColor; + } else { + view.backgroundColor = UIColor.whiteColor; + } +} + jstring convertStringJVM(JNIEnv* srcEnv, JNIEnv* dstEnv, jstring srcStr) { if (srcStr == NULL) { return NULL; From 47c8874cae88d2520f905d8ec41a05779a584da1 Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh <40482367+khanhduytran0@users.noreply.github.com> Date: Sat, 23 Jul 2022 21:54:48 +0700 Subject: [PATCH 06/10] Bug fix: UI completely freezes on launch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root cause is that the JVM has taken away the main thread, caused the UIKit’s queue to stop. This env var exists in my dev environment but has never been added here. --- Natives/JavaLauncher.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Natives/JavaLauncher.m b/Natives/JavaLauncher.m index 8f382ea1..21ebbf77 100644 --- a/Natives/JavaLauncher.m +++ b/Natives/JavaLauncher.m @@ -58,6 +58,9 @@ void init_loadCustomEnv() { // Override OpenGL version to 4.1 for Zink setenv("MESA_GL_VERSION_OVERRIDE", "4.1", 1); + // Runs JVM in a separate thread + setenv("HACK_IGNORE_START_ON_FIRST_THREAD", "1", 1); + /* Load custom env */ FILE *envFile = fopen([NSString stringWithFormat:@"%s/custom_env.txt", getenv("POJAV_HOME")].UTF8String, "r"); From 5261b41d3f9b87360a14073364cab3b13329c64f Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh <40482367+khanhduytran0@users.noreply.github.com> Date: Sun, 24 Jul 2022 09:37:06 +0700 Subject: [PATCH 07/10] Force the nav bar height same across size classes --- Natives/utils.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Natives/utils.m b/Natives/utils.m index e65462c6..0649d692 100644 --- a/Natives/utils.m +++ b/Natives/utils.m @@ -8,6 +8,13 @@ #include "utils.h" +// This forces the navigation bar to keep its height (44dp) in landscape +@implementation UINavigationBar(forceFullHeightInLandscape) +- (BOOL)forceFullHeightInLandscape { + return YES; +} +@end + NSMutableDictionary* parseJSONFromFile(NSString *path) { NSError *error; From b880f49b234104272b90996c6ba66d639d7426f5 Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh Date: Sun, 24 Jul 2022 20:56:53 +0700 Subject: [PATCH 08/10] Improved text input handling for autocomplete Text input is now far more stable with keyboard autocompletion and typing extra stuff (marks). Improved cursor movement: allows dragging farther. Additionally, the text field is now made visible on the side menu. Known issue that still persists: if the trackpad's cursor (by 3D Touch/spacebar) is moved too fast, it will not synchronize with game In other side, a bug fix: symlink fails, because the `Application Support` folder was not created. --- Natives/SurfaceViewController.m | 253 ++++++++++++++++---------------- Natives/main.m | 1 + 2 files changed, 126 insertions(+), 128 deletions(-) diff --git a/Natives/SurfaceViewController.m b/Natives/SurfaceViewController.m index efb813cc..a9a47947 100644 --- a/Natives/SurfaceViewController.m +++ b/Natives/SurfaceViewController.m @@ -26,10 +26,6 @@ // #define DEBUG_VISIBLE_TEXT_FIELD // #define DEBUG_VISIBLE_TOUCH -#define INPUT_SPACE_CHAR @" " -#define INPUT_FULL_LENGTH 40 -#define INPUT_SPACE_LENGTH 20 - #ifdef DEBUG_VISIBLE_TEXT_FIELD UILabel *inputLengthView; #endif @@ -41,44 +37,141 @@ BOOL slideableHotbar; // TODO: key modifiers impl +// There are private functions that we are unable to find public replacements +// (Both are found by placing breakpoints) +@interface UITextField(private) +- (NSRange)insertFilteredText:(NSString *)text; +- (id) replaceRangeWithTextWithoutClosingTyping:(UITextRange *)range replacementText:(NSString *)text; +@end + + #pragma mark Class TrackedTextField @interface TrackedTextField : UITextField -@property int lastPosition; -@property UITextPosition* lockPos; +@property int lastTextPos; +@property CGFloat lastPointX; @end @implementation TrackedTextField + +- (void)sendMultiBackspaces:(int)times { + for (int i = 0; i < times; i++) { + CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 1, 0); + CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 0, 0); + } +} + +- (void)sendText:(NSString *)text { + for (int i = 0; i < text.length; i++) { + // Directly convert unichar to jchar since both are in UTF-16 encoding. + jchar theChar = (jchar) [text characterAtIndex:i]; + if (isUseStackQueueCall) { + CallbackBridge_nativeSendCharMods(theChar, 0); + } else { + CallbackBridge_nativeSendChar(theChar); + } +#ifdef DEBUG_VISIBLE_TEXT_FIELD + inputLengthView.text = [NSString stringWithFormat:@"length=%lu", self.text.length]; +#endif + } +} + +- (void)beginFloatingCursorAtPoint:(CGPoint)point { + [super beginFloatingCursorAtPoint:point]; + self.lastPointX = point.x; +} + +// Handle cursor movement in the empty space +- (void)updateFloatingCursorAtPoint:(CGPoint)point { + [super updateFloatingCursorAtPoint:point]; + + if (self.lastPointX == 0 || (self.lastTextPos > 0 && self.lastTextPos < self.text.length)) { + // This is handled in -[TrackedTextField closestPositionToPoint:] + return; + } + +#ifdef DEBUG_VISIBLE_TEXT_FIELD + inputLengthView.text = [NSString stringWithFormat:@"updateFloatingCursorAtPoint lastPointX=%f, lastTextPos=%d\n", self.lastPointX, self.lastTextPos]; +#endif + + CGFloat diff = point.x - self.lastPointX; + if (ABS(diff) < 8) { + return; + } + self.lastPointX = point.x; + + int key = (diff > 0) ? GLFW_KEY_DPAD_RIGHT : GLFW_KEY_DPAD_LEFT; + CallbackBridge_nativeSendKey(key, 0, 1, 0); + CallbackBridge_nativeSendKey(key, 0, 0, 0); +} + +- (void)endFloatingCursor { + [super endFloatingCursor]; + self.lastPointX = 0; +} + - (UITextPosition *)closestPositionToPoint:(CGPoint)point { + // Handle cursor movement between characters UITextPosition *position = [super closestPositionToPoint:point]; int start = [self offsetFromPosition:self.beginningOfDocument toPosition:position]; - if (start - self.lastPosition != 0) { - int key = (start - self.lastPosition > 0) ? GLFW_KEY_DPAD_RIGHT : GLFW_KEY_DPAD_LEFT; + if (start - self.lastTextPos != 0) { + int key = (start - self.lastTextPos > 0) ? GLFW_KEY_DPAD_RIGHT : GLFW_KEY_DPAD_LEFT; CallbackBridge_nativeSendKey(key, 0, 1, 0); CallbackBridge_nativeSendKey(key, 0, 0, 0); } - self.lastPosition = start; - return [self positionFromPosition:self.beginningOfDocument offset:clamp(start, 20, self.text.length - 20)]; + self.lastTextPos = start; + return position; } - (void)deleteBackward { - [super deleteBackward]; - + if (self.text.length > 1) { + // Keep the first character (a space) + [super deleteBackward]; + } else { + self.text = @" "; + } + self.lastTextPos = [super offsetFromPosition:self.beginningOfDocument toPosition:self.selectedTextRange.start]; + + [self sendMultiBackspaces:1]; } - (BOOL)hasText { + self.lastTextPos = MAX(self.lastTextPos, 1); return YES; } -- (BOOL)insertText:(NSString *)text { - [super insertText:text]; - +// Old name: insertText +- (NSRange)insertFilteredText:(NSString *)text { + int cursorPos = [super offsetFromPosition:self.beginningOfDocument toPosition:self.selectedTextRange.start]; + + // This also makes sure that lastTextPos != cursorPos (text should never be empty) + if (self.lastTextPos - cursorPos == text.length) { + // Handle text markup by first deleting N amount of characters equal to the replaced text + [self sendMultiBackspaces:text.length]; + } + // What else is done by past-autocomplete (insert a space after autocompletion) + // See -[TrackedTextField replaceRangeWithTextWithoutClosingTyping:replacementText:] + + self.lastTextPos = cursorPos + text.length; + + [self sendText:text]; + + NSRange range = [super insertFilteredText:text]; + return range; } -- (void)setText:(NSString *)text { - [super setText:text]; - self.lockPos = [self positionFromPosition:self.beginningOfDocument offset:20]; - self.selectedTextRange = [self textRangeFromPosition:self.lockPos toPosition:self.lockPos]; +- (id) replaceRangeWithTextWithoutClosingTyping:(UITextRange *)range replacementText:(NSString *)text +{ + int length = [super offsetFromPosition:range.start toPosition:range.end]; + + // Delete the range of needs for autocompletion + [self sendMultiBackspaces:length]; + + // Insert the autocompleted text + [self sendText:text]; + + return [super replaceRangeWithTextWithoutClosingTyping:range replacementText:text]; } + @end @@ -234,22 +327,21 @@ self.view.frame.size.width * 0.3 - 36.0 * 0.7, self.view.frame.size.height)]; self.mousePointerView.userInteractionEnabled = NO; [self.rootView addSubview:self.mousePointerView]; -#ifndef DEBUG_VISIBLE_TEXT_FIELD - self.inputView = [[TrackedTextField alloc] initWithFrame:CGRectMake(0, -1, 1, 1)]; -#else - self.inputView = [[TrackedTextField alloc] initWithFrame:CGRectMake(5 * 2 + 160.0, 5 * 2 + 30.0, 200.0, 30.0)]; - self.inputView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.5f]; - self.inputView.font = [self.inputView.font fontWithSize:20]; - - inputLengthView = [[UILabel alloc] initWithFrame:CGRectMake(5 * 2 + 80.0, 5 * 2 + 30.0, 80.0, 30.0)]; + self.inputView = [[TrackedTextField alloc] initWithFrame:CGRectMake(0, -32.0, self.view.frame.size.width, 30.0)]; +#ifdef DEBUG_VISIBLE_TEXT_FIELD + inputLengthView = [[UILabel alloc] initWithFrame:CGRectMake(0, -62.0, self.view.frame.size.width, 30.0)]; inputLengthView.text = @"length=?"; inputLengthView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.6f]; [self.rootView addSubview:inputLengthView]; #endif - //self.inputView.autocorrectionType = UITextAutocorrectionTypeNo; - self.inputView.delegate = self; - self.inputView.lastPosition = 20; - [self.inputView addTarget:self action:@selector(inputViewDidChange) forControlEvents:UIControlEventEditingChanged]; + if (@available(iOS 13.0, *)) { + self.inputView.backgroundColor = UIColor.secondarySystemBackgroundColor; + } else { + self.inputView.backgroundColor = UIColor.groupTableViewBackgroundColor; + } + self.inputView.font = [UIFont fontWithName:@"Menlo-Regular" size:20]; + self.inputView.clearsOnBeginEditing = YES; + self.inputView.textAlignment = NSTextAlignmentCenter; NSString *controlFilePath = [NSString stringWithFormat:@"%s/controlmap/%@", getenv("POJAV_HOME"), (NSString *)getPreference(@"default_ctrl")]; @@ -618,7 +710,6 @@ CGPoint lastCenterPoint; if (sender.state == UIGestureRecognizerStateRecognized) { if (currentHotbarSlot == -1) { - self.inputView.text = INPUT_SPACE_CHAR; inputTextLength = 0; CallbackBridge_nativeSendMouseButton( @@ -680,7 +771,6 @@ CGPoint lastCenterPoint; if (sender.state == UIGestureRecognizerStateBegan) { self.shouldTriggerClick = NO; if (currentHotbarSlot == -1) { - self.inputView.text = INPUT_SPACE_CHAR; inputTextLength = 0; CallbackBridge_nativeSendMouseButton(GLFW_MOUSE_BUTTON_LEFT, 1, 0); @@ -695,9 +785,6 @@ CGPoint lastCenterPoint; || sender.state == UIGestureRecognizerStateEnded) { if (currentHotbarSlot == -1) { - self.inputView.text = INPUT_SPACE_CHAR; - inputTextLength = 0; - CallbackBridge_nativeSendMouseButton(GLFW_MOUSE_BUTTON_LEFT, 0, 0); } else { @@ -732,90 +819,6 @@ CallbackBridge_nativeSendKey(GLFW_KEY_Q, 0, 0, 0); #pragma mark - Input view stuff -NSString* inputStringBefore; -int inputStringLength = INPUT_FULL_LENGTH; --(void)inputViewDidChange { - int typedLength = (int)self.inputView.text.length - inputStringLength; - if (typedLength < 0) { - for (int i = 0; i < -typedLength; i++) { - if (self.inputView.text.length < INPUT_FULL_LENGTH) { - self.inputView.text = [@" " stringByAppendingString:self.inputView.text]; - } - CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 1, 0); - CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 0, 0); - if (inputTextLength > 0) { - --inputTextLength; - } - } - -#ifdef DEBUG_VISIBLE_TEXT_FIELD - inputLengthView.text = [@"length=" stringByAppendingFormat:@"%i", - inputTextLength]; -#endif - } else if (typedLength > 0) { - int index = [self.inputView offsetFromPosition:self.inputView.beginningOfDocument toPosition:self.inputView.selectedTextRange.start]; - NSString *newText = [self.inputView.text substringWithRange:NSMakeRange(index - typedLength, typedLength)]; - int charLength = (int) [newText length]; - for (int i = 0; i < charLength; i++) { - // Directly convert unichar to jchar since both are in UTF-16 encoding. - - jchar theChar = (jchar) [newText characterAtIndex:i]; - if (isUseStackQueueCall) { - CallbackBridge_nativeSendCharMods(theChar, /* mods */ 0); - } else { - CallbackBridge_nativeSendChar(theChar); - } - - ++inputTextLength; -#ifdef DEBUG_VISIBLE_TEXT_FIELD - inputLengthView.text = [@"length=" stringByAppendingFormat:@"%i", - inputTextLength]; -#endif - } - inputStringBefore = self.inputView.text; - // [self.inputView.text substringFromIndex:inputTextLength - 1]; - } else { -#ifdef DEBUG_VISIBLE_TEXT_FIELD - NSLog(@"Compare \"%@\" vs \"%@\"", self.inputView.text, inputStringBefore); -#endif - for (int i = 0; i < INPUT_FULL_LENGTH; i++) { - if ([self.inputView.text characterAtIndex:i] != [inputStringBefore characterAtIndex:i]) { - NSString *inputStringNow = [self.inputView.text substringFromIndex:i]; -/* - self.inputView.text = [self.inputView.text substringToIndex:i]; - // self notify - [self inputViewDidChange]; - - self.inputView.text = [self.inputView.text stringByAppendingString:inputStringNow]; - // self notify - [self inputViewDidChange]; -*/ - - for (int i2 = 0; i2 < [inputStringNow length]; i2++) { - CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 1, 0); - CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 0, 0); - } - - for (int i2 = 0; i2 < [inputStringNow length]; i2++) { - CallbackBridge_nativeSendCharMods((jchar) [inputStringNow characterAtIndex:i2], /* mods */ 0); - } - - break; - } - } - -#ifdef DEBUG_VISIBLE_TEXT_FIELD - inputLengthView.text = @"length ="; -#endif - inputStringBefore = self.inputView.text; - } - - inputStringLength = (int)self.inputView.text.length; - - // Reset to default value - // self.inputView.text = INPUT_SPACE_CHAR; -} - -(BOOL)textFieldShouldReturn:(UITextField *)textField { CallbackBridge_nativeSendKey(GLFW_KEY_ENTER, 0, 1, 0); CallbackBridge_nativeSendKey(GLFW_KEY_ENTER, 0, 0, 0); @@ -837,16 +840,10 @@ int currentVisibility = 1; if (self.inputView.isFirstResponder) { [self.inputView resignFirstResponder]; self.inputView.alpha = 1.0f; - self.inputView.text = @""; } else { [self.inputView becomeFirstResponder]; - // Empty the input field so that user will no longer able to select text inside. - self.inputView.text = INPUT_SPACE_CHAR; - inputStringLength = INPUT_FULL_LENGTH; - -#ifndef DEBUG_VISIBLE_TEXT_FIELD - self.inputView.alpha = 0.0f; -#endif + // Insert an undeletable space + self.inputView.text = @" "; inputTextLength = 0; } } diff --git a/Natives/main.m b/Natives/main.m index f60fbdc1..a5f7ccd3 100644 --- a/Natives/main.m +++ b/Natives/main.m @@ -178,6 +178,7 @@ void init_setupMultiDir() { [fm createDirectoryAtPath:multidirPath withIntermediateDirectories:YES attributes:nil error:nil]; [fm removeItemAtPath:lasmPath error:nil]; + [fm createDirectoryAtPath:lasmPath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:nil error:nil]; [fm createSymbolicLinkAtPath:lasmPath withDestinationPath:multidirPath error:nil]; setenv("POJAV_GAME_DIR", lasmPath.UTF8String, 1); From d311a03628c9122634bcd67afbf3ec4e5bb1ae85 Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh Date: Mon, 25 Jul 2022 06:27:40 +0700 Subject: [PATCH 09/10] iPadOS: view ctrls should now be fullscreen again --- Natives/CustomControlsViewController.m | 1 + Natives/LauncherViewController.m | 5 +++-- Natives/LoginViewController.m | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Natives/CustomControlsViewController.m b/Natives/CustomControlsViewController.m index 72a4d9bf..32a8becd 100644 --- a/Natives/CustomControlsViewController.m +++ b/Natives/CustomControlsViewController.m @@ -86,6 +86,7 @@ NSMutableArray *keyCodeMap, *keyValueMap; - (void)viewDidLoad { [super viewDidLoad]; + self.view.backgroundColor = [UIColor colorWithWhite:0.2 alpha:0.5]; viewController = self; isControlModifiable = YES; diff --git a/Natives/LauncherViewController.m b/Natives/LauncherViewController.m index 8097dc4f..330134a7 100644 --- a/Natives/LauncherViewController.m +++ b/Natives/LauncherViewController.m @@ -200,6 +200,7 @@ int versionSelectedAt = 0; - (void)enterCustomControls { if (![getPreference(@"customctrl_warn") boolValue]) { CustomControlsViewController *vc = [[CustomControlsViewController alloc] init]; + vc.modalPresentationStyle = UIModalPresentationOverFullScreen; [self presentViewController:vc animated:YES completion:nil]; return; } @@ -210,8 +211,7 @@ int versionSelectedAt = 0; 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) { - CustomControlsViewController *vc = [[CustomControlsViewController alloc] init]; - [self presentViewController:vc animated:YES completion:nil]; + [self enterCustomControls]; }]; [self presentViewController:alert animated:YES completion:nil]; [alert addAction:ok]; @@ -241,6 +241,7 @@ int versionSelectedAt = 0; - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url { if (controller.documentPickerMode == UIDocumentPickerModeImport) { JavaGUIViewController *vc = [[JavaGUIViewController alloc] init]; + vc.modalPresentationStyle = UIModalPresentationFullScreen; vc.filepath = url.path; NSLog(@"ModInstaller: launching %@", vc.filepath); [self presentViewController:vc animated:YES completion:nil]; diff --git a/Natives/LoginViewController.m b/Natives/LoginViewController.m index 46eb2faa..b7bde543 100644 --- a/Natives/LoginViewController.m +++ b/Natives/LoginViewController.m @@ -249,6 +249,7 @@ } else { splitVc = [[LauncherSplitViewController alloc] init]; } + splitVc.modalPresentationStyle = UIModalPresentationFullScreen; [self presentViewController:splitVc animated:YES completion:nil]; }; if (type == TYPE_SELECTACC) { From ad9c6d482a869ac74f00ac29bea28b140b691b28 Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh Date: Mon, 25 Jul 2022 20:54:41 +0700 Subject: [PATCH 10/10] Allow embedding JREs to PojavLauncher.app/jvm for IPA builds The old IPA creation process: wrap every dylib files into framework; this appears to be unnecessary, dylib files can be put outside without breaking codesigning and validation (only tested with AltStore: it scans and signs every Mach-O files). Therefore, IPA builds now allow putting Java Runtimes to `PojavLauncher.app/jvm`. Makefile is yet tweaked to adapt this change, will be done later. Other tweaks - Disabled autocorrection for text fields in preferences - The preferences now searches for installed JRE versions instead of hardcoding them - Bug fix: "accounts" folder was not created initially, leading to "username.json does not exist" error --- Natives/JavaLauncher.m | 116 +++--------------- Natives/LauncherPreferences.h | 1 + Natives/LauncherPreferences.m | 10 +- Natives/LauncherPreferencesViewController.m | 70 +++-------- Natives/egl_bridge.m | 4 +- Natives/log.m | 7 +- Natives/main.m | 7 +- Natives/main_hook.m | 5 + .../libjnidispatch.dylib.framework/Info.plist | 2 +- .../Info.plist | 2 +- 10 files changed, 55 insertions(+), 169 deletions(-) diff --git a/Natives/JavaLauncher.m b/Natives/JavaLauncher.m index 21ebbf77..c1bb0cfd 100644 --- a/Natives/JavaLauncher.m +++ b/Natives/JavaLauncher.m @@ -106,22 +106,24 @@ void init_loadCustomJvmFlags() { } NSString* environmentFailsafes(int minVersion) { + NSString *jvmPath = getenv("POJAV_DETECTEDJB") ? @"/usr/lib/jvm" : [NSString stringWithFormat:@"%s/jvm", getenv("BUNDLE_PATH")]; NSString *javaHome = nil; - if (getenv("POJAV_DETECTEDJB")) { - BOOL foundJava8 = [fm fileExistsAtPath:JRE8_HOME_JB]; - if (!foundJava8) { - regLog("[JavaLauncher] Java 8 wasn't found on your device. Install Java 8 for more compatibility and the mod installer."); - } - if (foundJava8 && minVersion <= 8) { - javaHome = JRE8_HOME_JB; - } else if ([fm fileExistsAtPath:JRE16_HOME_JB] && minVersion <= 16) { - javaHome = JRE16_HOME_JB; - } else if ([fm fileExistsAtPath:JRE17_HOME_JB] && minVersion <= 17) { - javaHome = JRE17_HOME_JB; - } - } else if (minVersion <= 8) { - javaHome = [NSString stringWithFormat:@"%s/jre", getenv("POJAV_HOME")]; + NSString *jre8Path = [NSString stringWithFormat:@"%@/java-8-openjdk", jvmPath]; + NSString *jre16Path = [NSString stringWithFormat:@"%@/java-16-openjdk", jvmPath]; + NSString *jre17Path = [NSString stringWithFormat:@"%@/java-17-openjdk", jvmPath]; + + BOOL foundJava8 = [fm fileExistsAtPath:jre8Path]; + if (!foundJava8) { + regLog("[JavaLauncher] Java 8 wasn't found on your device. Install Java 8 for more compatibility and the mod installer."); + } + + if (foundJava8 && minVersion <= 8) { + javaHome = jre8Path; + } else if ([fm fileExistsAtPath:jre16Path] && minVersion <= 16) { + javaHome = jre16Path; + } else if ([fm fileExistsAtPath:jre17Path] && minVersion <= 17) { + javaHome = jre17Path; } if (javaHome == nil) { @@ -166,92 +168,6 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi javaHome = javaHome_pre.UTF8String; setenv("JAVA_HOME", javaHome, 1); - if (!getenv("POJAV_DETECTEDJB")) { - char src[2048], dst[2048]; - - // Symlink frameworks -> dylibs on jailed environment - mkdir(javaHome, 755); - - // Symlink the skeleton part of JRE - sprintf((char *)src, "%s/jre/man", getenv("BUNDLE_PATH")); - sprintf((char *)dst, "%s/man", javaHome); - symlink(src, dst); - - sprintf((char *)src, "%s/jre/lib", getenv("BUNDLE_PATH")); - sprintf((char *)dst, "%s/lib", javaHome); - mkdir(dst, 755); - - DIR *d; - struct dirent *dir; - d = opendir(src); - assert(d); - int i = 0; - while ((dir = readdir(d)) != NULL) { - // Skip "." and ".." - if (i < 2) { - i++; - continue; - } else if (!strncmp(dir->d_name, "jli", 3)) { - sprintf((char *)dst, "%s/lib/jli", javaHome); - mkdir(dst, 755); - - // libjli.dylib - sprintf((char *)src, "%s/Frameworks/libjli.dylib.framework/libjli.dylib", getenv("BUNDLE_PATH")); - sprintf((char *)dst, "%s/lib/jli/libjli.dylib", javaHome); - symlink(src, dst); - } else if (!strncmp(dir->d_name, "server", 6)) { - sprintf((char *)dst, "%s/lib/server", javaHome); - mkdir(dst, 755); - - // libjsig.dylib - sprintf((char *)src, "%s/Frameworks/libjsig.dylib.framework/libjsig.dylib", getenv("BUNDLE_PATH")); - sprintf((char *)dst, "%s/lib/server/libjsig.dylib", javaHome); - symlink(src, dst); - - // libjvm.dylib - sprintf((char *)src, "%s/Frameworks/libjvm.dylib.framework/libjvm.dylib", getenv("BUNDLE_PATH")); - sprintf((char *)dst, "%s/lib/server/libjvm.dylib", javaHome); - symlink(src, dst); - - // Xusage.txt - sprintf((char *)src, "%s/jre/lib/server/Xusage.txt", getenv("BUNDLE_PATH")); - sprintf((char *)dst, "%s/lib/server/Xusage.txt", javaHome); - symlink(src, dst); - } else { - sprintf((char *)src, "%s/jre/lib/%s", getenv("BUNDLE_PATH"), dir->d_name); - sprintf((char *)dst, "%s/lib/%s", javaHome, dir->d_name); - symlink(src, dst); - } - } - closedir(d); - - // Symlink dylibs - sprintf((char *)src, "%s/Frameworks", getenv("BUNDLE_PATH")); - d = opendir(src); - assert(d); - i = 0; - while ((dir = readdir(d)) != NULL) { - // Skip "." and ".." - if (i < 2) { - i++; - continue; - } else if (!strncmp(dir->d_name, "lib", 3) && strlen(dir->d_name) > 12) { - char *dylibName = strdup(dir->d_name); - dylibName[strlen(dylibName) - 10] = '\0'; - sprintf((char *)src, "%s/Frameworks/%s/%s", getenv("BUNDLE_PATH"), dir->d_name, dylibName); - if (!strncmp(dir->d_name, "libjvm.dylib", 12)) { - sprintf((char *)dst, "%s/lib/server/%s", javaHome, dylibName); - } else { - sprintf((char *)dst, "%s/lib/%s", javaHome, dylibName); - } - symlink(src, dst); - dylibName[strlen(dylibName) - 11] = '.'; - free(dylibName); - } - } - closedir(d); - } - renderer_pre = getPreference(@"renderer"); renderer = renderer_pre.UTF8String; if (renderer_pre.length == 0) { diff --git a/Natives/LauncherPreferences.h b/Natives/LauncherPreferences.h index 5675ab1e..c135bd7f 100644 --- a/Natives/LauncherPreferences.h +++ b/Natives/LauncherPreferences.h @@ -7,6 +7,7 @@ void loadPreferences(); id getPreference(NSString* key); NSMutableDictionary* getDictionary(NSString *type); +int getJavaVersion(NSString* java); int getSelectedJavaVersion(); void setDefaultValueForPref(NSMutableDictionary *dict, NSString* key, id value); void setPreference(NSString* key, id value); diff --git a/Natives/LauncherPreferences.m b/Natives/LauncherPreferences.m index b0ccb682..52f0ce93 100644 --- a/Natives/LauncherPreferences.m +++ b/Natives/LauncherPreferences.m @@ -46,21 +46,21 @@ NSMutableDictionary *getDictionary(NSString *type) { return nil; } -int getSelectedJavaVersion() { - NSString *java = [getPreference(@"java_home") lastPathComponent]; +int getJavaVersion(NSString* java) { if (java.length == 0) { return 0; } else if ([java hasPrefix:@"java-"] && [java hasSuffix:@"-openjdk"]) { return [java substringWithRange:NSMakeRange(5, java.length - 13)].intValue; - } else if ([java isEqualToString:@"jre"]) { - // TODO for jailbroken: jre8 and jre17 - return 8; } else { NSLog(@"FIXME: What is the Java version of %@?", java); return 0; } } +int getSelectedJavaVersion() { + return getJavaVersion([getPreference(@"java_home") lastPathComponent]); +} + void setDefaultValueForPref(NSMutableDictionary *dict, NSString* key, id value) { if (!dict[key]) { dict[key] = value; diff --git a/Natives/LauncherPreferencesViewController.m b/Natives/LauncherPreferencesViewController.m index 19898195..781b8a06 100644 --- a/Natives/LauncherPreferencesViewController.m +++ b/Natives/LauncherPreferencesViewController.m @@ -84,7 +84,7 @@ UITextField *gdirTextField; UITextField *activeField; NSArray* rendererList; NSMutableArray* gdirList; -NSMutableArray* jhomeList; +NSArray* jhomeList; UIPickerView* rendPickerView; UIPickerView* gdirPickerView; UIPickerView* jhomePickerView; @@ -95,8 +95,6 @@ UIScrollView *scrollView; NSDictionary *rendererDict; -NSString *JRE8_HOME_SB; - int tempIndex; - (void)viewDidLoad @@ -110,14 +108,7 @@ int tempIndex; int width = (int) roundf(screenBounds.size.width); int height = (int) roundf(screenBounds.size.height) - self.navigationController.navigationBar.frame.size.height; CGFloat currY = 8.0; -/* - javaDict = @{ - @"/usr/lib/jvm/java-8-openjdk": @"Java 8", - @"/usr/lib/jvm/java-16-openjdk": @"Java 16", - @"/usr/lib/jvm/java-17-openjdk": @"Java 17", - [NSString stringWithFormat:@"%s/jre8", getenv("POJAV_HOME")], @"Java 8 (sandbox)" - }; -*/ + rendererDict = @{ @"gl4es 1.1.4 - exports OpenGL 2.1": @"libgl4es_114.dylib", @"tinygl4angle (1.17+) - exports OpenGL 3.2 (Core Profile, limited)": @"libtinygl4angle.dylib", @@ -211,8 +202,7 @@ int tempIndex; jargsTextField.delegate = self; jargsTextField.placeholder = @"Specify arguments..."; jargsTextField.text = (NSString *) getPreference(@"java_args"); - jargsTextField.autocorrectionType = UITextAutocorrectionTypeNo -; + jargsTextField.autocorrectionType = UITextAutocorrectionTypeNo; jargsTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop; jargsTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; [tableView addSubview:jargsTextField]; @@ -226,6 +216,7 @@ int tempIndex; rendTextField = [[UITextField alloc] initWithFrame:CGRectMake(buttonSizeSlider.frame.origin.x + 3, currY, width - jargsTextView.bounds.size.width - 28.0, 30)]; [rendTextField addTarget:rendTextField action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; rendTextField.tag = TAG_REND; + rendTextField.autocorrectionType = UITextAutocorrectionTypeNo; rendTextField.delegate = self; rendTextField.placeholder = @"Override renderer..."; //rendTextField.text = self.rendererDict[getPreference(@"renderer")]; @@ -262,45 +253,26 @@ int tempIndex; jhomeTextField = [[UITextField alloc] initWithFrame:CGRectMake(buttonSizeSlider.frame.origin.x + 3, currY, width - jargsTextView.bounds.size.width - 28.0, 30)]; [jhomeTextField addTarget:jhomeTextField action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; jhomeTextField.tag = TAG_JHOME; + jhomeTextField.autocorrectionType = UITextAutocorrectionTypeNo; jhomeTextField.delegate = self; jhomeTextField.placeholder = @"Override Java path..."; - JRE8_HOME_SB = [NSString stringWithFormat:@"%s/jre8", getenv("POJAV_HOME")]; - if(getenv("POJAV_DETECTEDJB")) { - switch (getSelectedJavaVersion()) { - case 8: jhomeTextField.text = JRE8_NAME_JB; break; - case 16: jhomeTextField.text = JRE16_NAME_JB; break; - case 17: jhomeTextField.text = JRE17_NAME_JB; break; - } - } else { - jhomeTextField.text = JRE8_NAME_SB; - } - + jhomeTextField.text = [getPreference(@"java_home") lastPathComponent]; + jhomeTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop; jhomeTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; [tableView addSubview:jhomeTextField]; - - jhomeList = [[NSMutableArray alloc] init]; - if ([[NSFileManager defaultManager] fileExistsAtPath:JRE8_HOME_JB]) { - [jhomeList addObject:JRE8_NAME_JB]; - } - if ([[NSFileManager defaultManager] fileExistsAtPath:JRE16_HOME_JB]) { - [jhomeList addObject:JRE16_NAME_JB]; - } - if ([[NSFileManager defaultManager] fileExistsAtPath:JRE17_HOME_JB]) { - [jhomeList addObject:JRE17_NAME_JB]; - } - if ([[NSFileManager defaultManager] fileExistsAtPath:JRE8_HOME_SB]) { - [jhomeList addObject:JRE8_NAME_SB]; - } - + + NSString *listPath = getenv("POJAV_DETECTEDJB") ? @"/usr/lib/jvm" : [NSString stringWithFormat:@"%s/jvm", getenv("BUNDLE_PATH")]; + jhomeList = [NSFileManager.defaultManager contentsOfDirectoryAtPath:listPath error:nil]; + jhomePickerView = [[UIPickerView alloc] init]; jhomePickerView.delegate = self; jhomePickerView.dataSource = self; jhomePickerView.tag = TAG_PICKER_JHOME; [jhomePickerView reloadAllComponents]; for (int i = 0; i < jhomeList.count; i++) { - if ([jhomeList[i] isEqualToString:jhomeTextField.text]) { + if ([jhomeTextField.text isEqualToString:jhomeList[i]]) { [jhomePickerView selectRow:i inComponent:0 animated:NO]; break; } @@ -608,24 +580,14 @@ int tempIndex; - (void)textFieldDidEndEditing:(UITextField *)textField { activeField = nil; if (textField.tag == TAG_JARGS) { - setPreference(@"java_args", textField.text); + setPreference(@"java_args", textField.text); } else if (textField.tag == TAG_REND) { setPreference(@"renderer", rendererDict[textField.text]); setenv("POJAV_RENDERER", [getPreference(@"renderer") UTF8String], 1); } else if (textField.tag == TAG_JHOME) { - if ([textField.text isEqualToString:JRE8_NAME_JB]) { - setPreference(@"java_home", JRE8_HOME_JB); - setenv("JAVA_HOME", [JRE8_HOME_JB cStringUsingEncoding:NSUTF8StringEncoding], 1); - } else if ([textField.text isEqualToString:JRE16_NAME_JB]) { - setPreference(@"java_home", JRE16_HOME_JB); - setenv("JAVA_HOME", [JRE16_HOME_JB cStringUsingEncoding:NSUTF8StringEncoding], 1); - } else if ([textField.text isEqualToString:JRE17_NAME_JB]) { - setPreference(@"java_home", JRE17_HOME_JB); - setenv("JAVA_HOME", [JRE17_HOME_JB cStringUsingEncoding:NSUTF8StringEncoding], 1); - } else if ([textField.text isEqualToString:JRE8_NAME_SB]) { - setPreference(@"java_home", JRE8_HOME_SB); - setenv("JAVA_HOME", [JRE8_HOME_SB cStringUsingEncoding:NSUTF8StringEncoding], 1); - } + NSString *listPath = getenv("POJAV_DETECTEDJB") ? @"/usr/lib/jvm" : [NSString stringWithFormat:@"%s/jvm", getenv("BUNDLE_PATH")]; + setPreference(@"java_home", [NSString stringWithFormat:@"%@/%@", listPath, textField.text]); + setenv("JAVA_HOME", [getPreference(@"java_home") UTF8String], 1); if (![textField.text containsString:JRE8_NAME_JB] && ![textField.text containsString:JRE8_NAME_SB] && [getPreference(@"java_warn") boolValue] == YES) { UIAlertController *javaAlert = [UIAlertController alertControllerWithTitle:@"Java version is not Java 8" message:@"Minecraft versions below 1.6, modded below 1.16.4, and the mod installer will not work unless you have Java 8 installed on your device." preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; diff --git a/Natives/egl_bridge.m b/Natives/egl_bridge.m index 3690d891..8d813cab 100644 --- a/Natives/egl_bridge.m +++ b/Natives/egl_bridge.m @@ -80,9 +80,6 @@ void* egl_make_current(void* window); int config_renderer; -typedef void gl4esInitialize_func(); -// typedef void gl4esSwapBuffers_func(); -// gl4esSwapBuffers_func *gl4esSwapBuffers; typedef jint RegalMakeCurrent_func(EGLContext context); // Called from JNI_OnLoad of liblwjgl_opengl, TODO: check if unused @@ -381,6 +378,7 @@ void pojavSwapBuffers() { case RENDERER_MTL_ANGLE: { if (!eglSwapBuffers_p(potatoBridge.eglDisplay, potatoBridge.eglSurface)) { if (eglGetError_p() == EGL_BAD_SURFACE) { + NSLog(@"eglSwapBuffers error 0x%x", eglGetError_p()); stopSwapBuffers = true; closeGLFWWindow(); } diff --git a/Natives/log.m b/Natives/log.m index 2f33e4c6..8b62e957 100644 --- a/Natives/log.m +++ b/Natives/log.m @@ -17,10 +17,9 @@ void debugLog(const char *message,...) NSLog(@"%@",[[NSString alloc] initWithFormat:[NSString stringWithUTF8String:message] arguments:args]); va_end(args); } else { + static BOOL debugLog_warned = NO; + if (debugLog_warned) return; + debugLog_warned = YES; NSLog(@"WARNING!!! DEBUG LOG DISABLED"); - va_list args; - va_start(args, message); - NSLog(@"%@",[[NSString alloc] initWithFormat:[NSString stringWithUTF8String:message] arguments:args]); - va_end(args); } } diff --git a/Natives/main.m b/Natives/main.m index a5f7ccd3..edd7f7ea 100644 --- a/Natives/main.m +++ b/Natives/main.m @@ -141,6 +141,11 @@ void init_redirectStdio() { }); } +void init_setupAccounts() { + NSString *controlPath = [@(getenv("POJAV_HOME")) stringByAppendingPathComponent:@"accounts"]; + [fm createDirectoryAtPath:controlPath withIntermediateDirectories:NO attributes:nil error:nil]; +} + void init_setupCustomControls() { NSString *controlPath = [@(getenv("POJAV_HOME")) stringByAppendingPathComponent:@"controlmap"]; [fm createDirectoryAtPath:controlPath withIntermediateDirectories:NO attributes:nil error:nil]; @@ -231,7 +236,7 @@ int main(int argc, char * argv[]) { loadPreferences(); init_setupMultiDir(); init_setupLauncherProfiles(); - + init_setupAccounts(); init_setupCustomControls(); init_migrateToPlist("selected_version", "config_ver.txt"); diff --git a/Natives/main_hook.m b/Natives/main_hook.m index 56b65b98..00b0dba4 100644 --- a/Natives/main_hook.m +++ b/Natives/main_hook.m @@ -44,6 +44,11 @@ int hooked_dladdr(const void* addr, Dl_info* info) { } void* hooked_dlopen(const char* path, int mode) { + // Avoid loading the executable itself twice + if (path && [@(path) hasSuffix:@"PojavLauncher"]) { + return orig_dlopen(NULL, mode); + } + void *handle = orig_dlopen(path, mode); if (handle) { return handle; diff --git a/Natives/resources/Frameworks/libjnidispatch.dylib.framework/Info.plist b/Natives/resources/Frameworks/libjnidispatch.dylib.framework/Info.plist index 5e115da6..bec2cc7a 100644 --- a/Natives/resources/Frameworks/libjnidispatch.dylib.framework/Info.plist +++ b/Natives/resources/Frameworks/libjnidispatch.dylib.framework/Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - + dylib.java.libjnidispatch CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/Natives/resources/Frameworks/libvirgl_test_server.dylib.framework/Info.plist b/Natives/resources/Frameworks/libvirgl_test_server.dylib.framework/Info.plist index ca9f1a9c..290fe10a 100644 --- a/Natives/resources/Frameworks/libvirgl_test_server.dylib.framework/Info.plist +++ b/Natives/resources/Frameworks/libvirgl_test_server.dylib.framework/Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - + dylib.mesa3d.libvirgl_test_server CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType