diff --git a/.gitmodules b/.gitmodules index 324d8d9e..c7a66354 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "Natives/external/DBNumberedSlider"] path = Natives/external/DBNumberedSlider - url = https://github.com/immago/DBNumberedSlider + url = https://github.com/khanhduytran0/DBNumberedSlider [submodule "Natives/external/fishhook"] path = Natives/external/fishhook url = https://github.com/khanhduytran0/fishhook @@ -8,3 +8,6 @@ [submodule "Natives/external/AFNetworking"] path = Natives/external/AFNetworking url = https://github.com/AFNetworking/AFNetworking +[submodule "Natives/external/TOInsetGroupedTableView"] + path = Natives/external/TOInsetGroupedTableView + url = https://github.com/TimOliver/TOInsetGroupedTableView diff --git a/JavaApp/src/main/java/net/kdt/pojavlaunch/PojavLauncher.java b/JavaApp/src/main/java/net/kdt/pojavlaunch/PojavLauncher.java index eb4716bf..1e7fd4c5 100644 --- a/JavaApp/src/main/java/net/kdt/pojavlaunch/PojavLauncher.java +++ b/JavaApp/src/main/java/net/kdt/pojavlaunch/PojavLauncher.java @@ -74,7 +74,6 @@ public class PojavLauncher { } } - System.setProperty("org.lwjgl.opengl.libname", System.getenv("POJAV_RENDERER")); System.setProperty("org.lwjgl.vulkan.libname", "libMoltenVK.dylib"); Tools.mGLFWWindowWidth = Integer.parseInt(size[0]); diff --git a/JavaApp/src/main/java/org/lwjgl/opengl/GL.java b/JavaApp/src/main/java/org/lwjgl/opengl/GL.java index bbcf4d1b..7c91ea73 100644 --- a/JavaApp/src/main/java/org/lwjgl/opengl/GL.java +++ b/JavaApp/src/main/java/org/lwjgl/opengl/GL.java @@ -108,7 +108,9 @@ public final class GL { GL = Library.loadNative(GL.class, "org.lwjgl.opengl", Configuration.OPENGL_LIBRARY_NAME, "libGL.so.1", "libGL.so"); break; case MACOSX: - String override = Configuration.OPENGL_LIBRARY_NAME.get(); + // Configuration does not get updated if the value changes, so we have to use System.getProperty() + String override = System.getProperty("org.lwjgl.opengl.libname"); + //Configuration.OPENGL_LIBRARY_NAME.get(); GL = override != null ? Library.loadNative(GL.class, "org.lwjgl.opengl", override) : MacOSXLibrary.getWithIdentifier("com.apple.opengl"); diff --git a/Natives/BKSSystemService.h b/Natives/BKSSystemService.h deleted file mode 100644 index b44e4088..00000000 --- a/Natives/BKSSystemService.h +++ /dev/null @@ -1,9 +0,0 @@ -#import - -@interface BKSSystemService : NSObject - -- (unsigned int)createClientPort; -- (void)openApplication:(NSString *)app options:(id)arg2 withResult:(id)arg3 ; -- (void)openURL:(NSURL *)url application:(NSString *)app options:(id)options clientPort:(unsigned int)port withResult:(id)result; - -@end diff --git a/Natives/CMakeLists.txt b/Natives/CMakeLists.txt index ec3ed0a1..c78d0180 100644 --- a/Natives/CMakeLists.txt +++ b/Natives/CMakeLists.txt @@ -15,6 +15,7 @@ include_directories( "external/AFNetworking/AFNetworking" "external/AFNetworking/UIKit+AFNetworking" "external/DBNumberedSlider/Classes" + "external/TOInsetGroupedTableView/TOInsetGroupedTableView" "external/fishhook" "external/mesa" "external/Apple" @@ -101,6 +102,7 @@ add_executable(PojavLauncher customcontrols/CustomControlsUtils.m external/DBNumberedSlider/Classes/DBNumberedSlider.m + external/TOInsetGroupedTableView/TOInsetGroupedTableView/TOInsetGroupedTableView.m input/ControllerInput.m input/KeyboardInput.m @@ -115,7 +117,8 @@ add_executable(PojavLauncher LauncherFAQViewController.m LauncherMenuViewController.m LauncherPreferences.m - LauncherPreferencesViewController.m + LauncherPreferencesViewController2.m + LauncherPrefGameDirViewController.m LauncherSplitViewController.m LauncherViewController.m LoginViewController.m diff --git a/Natives/JavaLauncher.m b/Natives/JavaLauncher.m index 3c554b51..78eee735 100644 --- a/Natives/JavaLauncher.m +++ b/Natives/JavaLauncher.m @@ -24,25 +24,16 @@ static char args_path[2048]; extern char **environ; -static const char* const_progname = "java"; -static const char* const_launcher = "openjdk"; -static const char** const_jargs = NULL; -static const char** const_appclasspath = NULL; -static const jboolean const_javaw = JNI_FALSE; -static const jboolean const_cpwildcard = JNI_TRUE; -static const jint const_ergo_class = 0; // DEFAULT_POLICY - static int margc = -1; static char* margv[1000]; const char *javaHome; -const char *renderer; const char *allocmem; const char *multidir; char *homeDir; -NSString *renderer_pre; +NSString *renderer; NSString *allocmem_pre; NSString *multidir_pre; @@ -138,6 +129,9 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi regLog("[JavaLauncher] Beginning JVM launch\n"); NSString *javaHome_pre = getPreference(@"java_home"); + if (![javaHome_pre hasPrefix:@"/"]) { + javaHome_pre = [NSString stringWithFormat:@"%s/jvm/%@", getenv("POJAV_DETECTEDJB") ? "/usr/lib" : getenv("BUNDLE_PATH"), javaHome_pre]; + } // We handle unset JAVA_HOME right there if (getSelectedJavaVersion() < minVersion) { @@ -149,7 +143,7 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi setPreference(@"java_home", javaHome_pre); NSLog(@"[JavaLauncher] JAVA_HOME environment variable was not set. Default to %@ for future use.\n", javaHome_pre); } */ else { - if (![NSFileManager.defaultManager fileExistsAtPath:javaHome_pre]) { + if (![fm fileExistsAtPath:javaHome_pre]) { javaHome_pre = environmentFailsafes(minVersion); setPreference(@"java_home", javaHome_pre); NSLog(@"[JavaLauncher] Failed to locate %@. Restored default value for JAVA_HOME.", javaHome_pre); @@ -165,17 +159,15 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi javaHome = javaHome_pre.UTF8String; setenv("JAVA_HOME", javaHome, 1); - renderer_pre = getPreference(@"renderer"); - renderer = renderer_pre.UTF8String; - if (renderer_pre.length == 0) { - renderer_pre = @"libgl4es_114.dylib"; - setPreference(@"renderer", renderer_pre); - renderer = [renderer_pre cStringUsingEncoding:NSUTF8StringEncoding]; - regLog("[JavaLauncher] RENDERER environment variable was not set. Defaulting to %s for future use.\n", renderer); + renderer = getPreference(@"renderer"); + if (renderer.length == 0) { + renderer = @"auto"; + setPreference(@"renderer", renderer); + NSLog(@"[JavaLauncher] RENDERER environment variable was not set. Defaulting to %@ for future use.\n", renderer); } else { - regLog("[JavaLauncher] Restored preference: RENDERER is set to %s\n", renderer); + NSLog(@"[JavaLauncher] Restored preference: RENDERER is set to %@\n", renderer); } - setenv("POJAV_RENDERER", renderer, 1); + setenv("POJAV_RENDERER", renderer.UTF8String, 1); allocmem_pre = [getPreference(@"allocated_memory") stringValue]; allocmem = [allocmem_pre cStringUsingEncoding:NSUTF8StringEncoding]; diff --git a/Natives/LauncherMenuViewController.m b/Natives/LauncherMenuViewController.m index 0a3c15f9..00dda033 100644 --- a/Natives/LauncherMenuViewController.m +++ b/Natives/LauncherMenuViewController.m @@ -1,7 +1,7 @@ #import "authenticator/BaseAuthenticator.h" #import "AccountListViewController.h" #import "LauncherMenuViewController.h" -#import "LauncherPreferencesViewController.h" +#import "LauncherPreferencesViewController2.h" #import "LauncherViewController.h" #import "UIButton+AFNetworking.h" #import "UIImageView+AFNetworking.h" @@ -20,10 +20,10 @@ // View controllers are put into an array to keep its state self.options = @[ [[LauncherViewController alloc] init], - [[LauncherPreferencesViewController alloc] init] + [[LauncherPreferencesViewController2 alloc] init] ]; self.options[0].title = @"PojavLauncher"; - self.options[1].title = @"Preferences"; + self.options[1].title = NSLocalizedString(@"Settings", nil); //@[@"News", @"Development Console", @"Crash logs"]; self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; @@ -88,7 +88,7 @@ selected.navigationItem.leftItemsSupplementBackButton = true; } - // This button is placed temporarily while migrating to the new UI + // Put a close button, as iOS does not have a dedicated back button self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"❌" style:UIBarButtonItemStyleDone target:self.splitViewController action:@selector(dismissViewController)]; } diff --git a/Natives/LauncherPrefGameDirViewController.h b/Natives/LauncherPrefGameDirViewController.h new file mode 100644 index 00000000..9ef15fc1 --- /dev/null +++ b/Natives/LauncherPrefGameDirViewController.h @@ -0,0 +1,6 @@ +#import + +@interface LauncherPrefGameDirViewController : UITableViewController + +@end + diff --git a/Natives/LauncherPrefGameDirViewController.m b/Natives/LauncherPrefGameDirViewController.m new file mode 100644 index 00000000..04ee7397 --- /dev/null +++ b/Natives/LauncherPrefGameDirViewController.m @@ -0,0 +1,238 @@ +#import "LauncherPreferences.h" +#import "LauncherPrefGameDirViewController.h" +#import "TOInsetGroupedTableView.h" +#import "ios_uikit_bridge.h" + +@interface LauncherPrefGameDirViewController () +@property NSMutableArray *array; +@end + +@implementation LauncherPrefGameDirViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + [self setTitle:NSLocalizedString(@"preference.title.game_directory", nil)]; + + self.array = [[NSMutableArray alloc] init]; + [self.array addObject:@"default"]; + + self.tableView = [[TOInsetGroupedTableView alloc] init]; + self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; + self.tableView.sectionFooterHeight = 50; + + NSString *path = [NSString stringWithFormat:@"%s/instances", getenv("POJAV_HOME")]; + + NSFileManager *fm = NSFileManager.defaultManager; + NSArray *files = [fm contentsOfDirectoryAtPath:path error:nil]; + BOOL isDir; + for (NSString *file in files) { + [fm fileExistsAtPath:path isDirectory:(&isDir)]; + if (isDir && ![file isEqualToString:@"default"]) { + [self.array addObject:file]; + } + } +} + +- (void)changeSelectionTo:(NSString *)name { + setPreference(@"game_directory", name); + NSString *multidirPath = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), name]; + NSString *lasmPath = [NSString stringWithFormat:@"%s/Library/Application Support/minecraft", getenv("POJAV_HOME")]; + [NSFileManager.defaultManager removeItemAtPath:lasmPath error:nil]; + [NSFileManager.defaultManager createSymbolicLinkAtPath:lasmPath withDestinationPath:multidirPath error:nil]; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.array.count; +} + +- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITextField *view; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; + if (cell == nil) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; + + if (indexPath.row == 0) { + cell.textLabel.text = self.array[indexPath.row]; + } else { + view = [[UITextField alloc] initWithFrame:CGRectMake(20, 10, (cell.bounds.size.width-40)/2, cell.bounds.size.height-20)]; + [view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; + view.autocorrectionType = UITextAutocorrectionTypeNo; + view.autocapitalizationType = UITextAutocapitalizationTypeNone; + view.autoresizingMask = UIViewAutoresizingFlexibleWidth; + view.delegate = self; + view.returnKeyType = UIReturnKeyDone; + [cell.contentView addSubview:view]; + } + } + if (indexPath.row > 0) { + view = cell.contentView.subviews.lastObject; + view.placeholder = self.array[indexPath.row]; + view.text = self.array[indexPath.row]; + } + + if ([getPreference(@"game_directory") isEqualToString:self.array[indexPath.row]]) { + cell.accessoryType = UITableViewCellAccessoryCheckmark; + } else { + cell.accessoryType = UITableViewCellAccessoryNone; + } + + // TODO: display size in the detail label + return cell; +} + +- (UIView *)tableView:(UITableView *)tableView +viewForFooterInSection:(NSInteger)section +{ + UITextField *view = [[UITextField alloc] init]; + [view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; + view.autocorrectionType = UITextAutocorrectionTypeNo; + view.autocapitalizationType = UITextAutocapitalizationTypeNone; + view.delegate = self; + view.placeholder = NSLocalizedString(@"preference.multidir.add_directory", nil); + view.returnKeyType = UIReturnKeyDone; + return view; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [self changeSelectionTo:self.array[indexPath.row]]; + [tableView deselectRowAtIndexPath:indexPath animated:NO]; + for (int i = 0; i < self.array.count; i++) { + UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]]; + if (i == indexPath.row) { + cell.accessoryType = UITableViewCellAccessoryCheckmark; + } else { + cell.accessoryType = UITableViewCellAccessoryNone; + } + } +} + +- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0)) +{ + // You can't delete or rename the default instance, though there will be a reset action (TODO) + if (indexPath.row == 0) { + return nil; + } + + if (@available(iOS 13.0, *)) { + UIAction *delete = [UIAction actionWithTitle:@"Delete" image:[UIImage systemImageNamed:@"trash"] identifier:@"deleteAction" handler:^(__kindof UIAction * _Nonnull action) { + [self actionDeleteAtIndexPath:indexPath]; + }]; + delete.attributes = UIMenuElementAttributesDestructive; + + UIAction *rename = [UIAction actionWithTitle:@"Rename" image:[UIImage systemImageNamed:@"pencil"] identifier:@"editAction" handler:^(__kindof UIAction * _Nonnull action) { + UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath]; + [view.contentView.subviews.lastObject becomeFirstResponder]; + }]; + + return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu * _Nullable(NSArray * _Nonnull suggestedActions) { + return [UIMenu menuWithTitle:self.array[indexPath.row] children:@[delete, rename]]; + }]; + } + return nil; +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (editingStyle == UITableViewCellEditingStyleDelete) { + [self actionDeleteAtIndexPath:indexPath]; + } +} + +- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.row == 0) { + // TODO: Reset action? + return UITableViewCellEditingStyleNone; + } else { + return UITableViewCellEditingStyleDelete; + } +} + +- (void)actionDeleteAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath]; + NSString *title = NSLocalizedString(@"preference.title.confirm", nil); + NSString *message = [NSString stringWithFormat:NSLocalizedString(@"preference.title.confirm.delete_game_directory", nil), self.array[indexPath.row]]; + UIAlertController *confirmAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; + confirmAlert.popoverPresentationController.sourceView = view; + confirmAlert.popoverPresentationController.sourceRect = view.bounds; + UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { + NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), self.array[indexPath.row]]; + NSError *error; + if([NSFileManager.defaultManager removeItemAtPath:directory error:&error]) { + if ([getPreference(@"game_directory") isEqualToString:self.array[indexPath.row]]) { + [self changeSelectionTo:self.array[0]]; + [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].accessoryType = UITableViewCellAccessoryCheckmark; + } + [self.array removeObjectAtIndex:indexPath.row]; + [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; + } else { + showDialog(self, NSLocalizedString(@"Error", nil), error.localizedDescription); + } + }]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]; + [confirmAlert addAction:cancel]; + [confirmAlert addAction:ok]; + [self presentViewController:confirmAlert animated:YES completion:nil]; +} + +#pragma mark UITextField + +- (void)textFieldDidEndEditing:(UITextField *)sender { + BOOL isFooterView = sender.superview == self.tableView; + if (!sender.hasText || [sender.text isEqualToString:sender.placeholder]) { + if (isFooterView) { + return; + } + sender.text = sender.placeholder; + return; + } + + NSError *error; + + NSString *dest = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), sender.text]; + if (isFooterView) { + [NSFileManager.defaultManager createDirectoryAtPath:dest withIntermediateDirectories:NO attributes:nil error:&error]; + } else { + NSString *source = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), sender.placeholder]; + [NSFileManager.defaultManager moveItemAtPath:source toPath:dest error:&error]; + } + + if (error == nil) { + [self changeSelectionTo:sender.text]; + if (isFooterView) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.array.count inSection:0]; + [self.array addObject:sender.text]; + [self.tableView beginUpdates]; + [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.tableView endUpdates]; + [self tableView:self.tableView didSelectRowAtIndexPath:indexPath]; + // Clear text + sender.text = @""; + } else { + int index = [self.array indexOfObject:sender.placeholder]; + self.array[index] = sender.placeholder = sender.text; + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; + [self tableView:self.tableView didSelectRowAtIndexPath:indexPath]; + } + } else { + // Restore to the previous name if we encounter an error + if (!isFooterView) { + sender.text = sender.placeholder; + } + showDialog(self, NSLocalizedString(@"Error", nil), error.localizedDescription); + } +} + +- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + [textField invalidateIntrinsicContentSize]; + CGRect frame = textField.frame; + frame.size.width = MAX(50, textField.intrinsicContentSize.width + 10); + textField.frame = frame; + return YES; +} + +@end diff --git a/Natives/LauncherPreferences.h b/Natives/LauncherPreferences.h index 89dab22d..711ba25e 100644 --- a/Natives/LauncherPreferences.h +++ b/Natives/LauncherPreferences.h @@ -4,13 +4,14 @@ #define JRE16_HOME_JB @"/usr/lib/jvm/java-16-openjdk" #define JRE17_HOME_JB @"/usr/lib/jvm/java-17-openjdk" -void loadPreferences(); +void loadPreferences(BOOL reset); 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); +void resetWarnings(); CGRect getDefaultSafeArea(); BOOL getEntitlementValue(NSString *key); diff --git a/Natives/LauncherPreferences.m b/Natives/LauncherPreferences.m index de7122cc..5636dc8d 100644 --- a/Natives/LauncherPreferences.m +++ b/Natives/LauncherPreferences.m @@ -78,18 +78,30 @@ void setPreference(NSString* key, id value) { [prefDict writeToFile:prefPath atomically:YES]; } -void loadPreferences() { +void fillDefaultWarningDict() { + setDefaultValueForPref(warnPrefDict, @"option_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"local_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"mem_warn", @YES); + 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); +} + +void loadPreferences(BOOL reset) { assert(getenv("POJAV_HOME")); prefPath = [@(getenv("POJAV_HOME")) stringByAppendingPathComponent:@"launcher_preferences.plist"]; NSFileManager *fileManager = [NSFileManager defaultManager]; - if (![fileManager fileExistsAtPath:prefPath]) { + prefDict = [NSMutableDictionary dictionaryWithContentsOfFile:prefPath]; + if (reset || ![fileManager fileExistsAtPath:prefPath]) { prefDict = [[NSMutableDictionary alloc] init]; envPrefDict = [[NSMutableDictionary alloc] init]; warnPrefDict = [[NSMutableDictionary alloc] init]; } else { - prefDict = [NSMutableDictionary dictionaryWithContentsOfFile:prefPath]; envPrefDict = prefDict[@"env_vars"]; warnPrefDict = prefDict[@"warnings"]; } @@ -101,30 +113,23 @@ void loadPreferences() { setDefaultValueForPref(prefDict, @"button_scale", @(100)); setDefaultValueForPref(prefDict, @"selected_version", @"1.7.10"); setDefaultValueForPref(prefDict, @"selected_version_type", @(0)); - setDefaultValueForPref(envPrefDict, @"time_longPressTrigger", @(400)); + setDefaultValueForPref(envPrefDict, @"press_duration", @(400)); + setDefaultValueForPref(envPrefDict, @"mouse_scale", @(100)); + setDefaultValueForPref(envPrefDict, @"mouse_speed", @(100)); setDefaultValueForPref(envPrefDict, @"default_ctrl", @"default.json"); setDefaultValueForPref(envPrefDict, @"game_directory", @"default"); setDefaultValueForPref(envPrefDict, @"java_args", @""); - setDefaultValueForPref(envPrefDict, @"allocated_memory", [NSNumber numberWithFloat:roundf(([[NSProcessInfo processInfo] physicalMemory] / 1048576) * 0.30)]); - setDefaultValueForPref(prefDict, @"restart_before_launch", @NO); + setDefaultValueForPref(envPrefDict, @"allocated_memory", [NSNumber numberWithFloat:roundf(([[NSProcessInfo processInfo] physicalMemory] / 1048576) * 0.25)]); setDefaultValueForPref(prefDict, @"debug_logging", CONFIG_TYPE); setDefaultValueForPref(prefDict, @"arccapes_enable", @YES); setDefaultValueForPref(envPrefDict, @"java_home", @""); - setDefaultValueForPref(envPrefDict, @"renderer", @"libgl4es_114.dylib"); - setDefaultValueForPref(warnPrefDict, @"option_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"local_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"mem_warn", @YES); - 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, @"ram_unjb_warn", @YES); + setDefaultValueForPref(envPrefDict, @"renderer", @"auto"); + fillDefaultWarningDict(); setDefaultValueForPref(prefDict, @"a7_allow", @NO); setDefaultValueForPref(prefDict, @"slideable_hotbar", @NO); setDefaultValueForPref(prefDict, @"virtmouse_enable", @NO); setDefaultValueForPref(prefDict, @"check_sha", @YES); - setDefaultValueForPref(prefDict, @"ram_unjb_enable", @NO); + setDefaultValueForPref(prefDict, @"auto_ram", @(getenv("POJAV_DETECTEDJB") == NULL)); if (0 != [fileManager fileExistsAtPath:@"/var/mobile/Documents/.pojavlauncher"]) { setDefaultValueForPref(prefDict, @"disable_home_symlink", @NO); @@ -132,7 +137,8 @@ void loadPreferences() { setDefaultValueForPref(prefDict, @"disable_home_symlink", @YES); } - setDefaultValueForPref(prefDict, @"control_safe_area", NSStringFromCGRect(getDefaultSafeArea())); + // Migrate some prefs + setPreference(@"java_home", [getPreference(@"java_home") lastPathComponent]); prefDict[@"env_vars"] = envPrefDict; prefDict[@"warnings"] = warnPrefDict; @@ -140,6 +146,13 @@ void loadPreferences() { [prefDict writeToFile:prefPath atomically:YES]; } +void resetWarnings() { + [warnPrefDict removeAllObjects]; + fillDefaultWarningDict(); + prefDict[@"warnings"] = warnPrefDict; + [prefDict writeToFile:prefPath atomically:YES]; +} + CGRect getDefaultSafeArea() { CGRect defaultSafeArea = UIScreen.mainScreen.bounds; UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; diff --git a/Natives/LauncherPreferencesViewController.m b/Natives/LauncherPreferencesViewController.m deleted file mode 100644 index 95e2a7b3..00000000 --- a/Natives/LauncherPreferencesViewController.m +++ /dev/null @@ -1,1023 +0,0 @@ -#import "DBNumberedSlider.h" -#import "LauncherPreferences.h" -#import "LauncherPreferencesViewController.h" -#import "LauncherViewController.h" - -#include "utils.h" - -#define BTNSCALE 0 -#define RESOLUTION 1 -#define ALLOCMEM 2 -#define JARGS 3 -#define REND 4 -#define JHOME 5 -#define GDIRECTORY 6 - -#define RESETWARN 8 -#define TYPESEL 9 -#define DEBUGLOG 10 -#define MKGDIR 11 -#define RMGDIR 12 -#define SLIDEHOTBAR 13 -#define SAFEAREA 14 -#define HOMESYM 15 -#define RELAUNCH 16 -#define ERASEPREF 17 -#define ARCCAPES 18 -#define VIRTMOUSE 19 -#define CHECKSHA 20 -#define UNJBRAM 21 - -#define TAG_BTNSCALE 98 -#define TAG_RESOLUTION 99 -#define TAG_ALLOCMEM 100 -#define TAG_JARGS 101 - -#define TAG_REND 102 -#define TAG_PICKER_REND 142 -#define TAG_DONE_REND 152 - -#define TAG_JHOME 103 -#define TAG_PICKER_JHOME 143 -#define TAG_DONE_JHOME 153 - -#define TAG_GDIR 104 -#define TAG_PICKER_GDIR 144 -#define TAG_DONE_GDIR 154 - -#define TAG_RESETWARN 106 - -#define TAG_SWITCH_VRELEASE 107 -#define TAG_SWITCH_VSNAPSHOT 108 -#define TAG_SWITCH_VOLDBETA 109 -#define TAG_SWITCH_VOLDALPHA 110 - -#define TAG_DEBUGLOG 111 -#define TAG_SLIDEHOTBAR 112 - -#define TAG_SAFEAREA 113 - -#define TAG_HOMESYM 114 - -#define TAG_RELAUNCH 115 - -#define TAG_ERASEPREF 116 - -#define TAG_ARCCAPES 117 - -#define TAG_VIRTMOUSE 118 - -#define TAG_CHECKSHA 119 - -#define TAG_UNJBRAM 120 - -@interface LauncherPreferencesViewController () { -} - -// - (void)method - -@end - -@implementation LauncherPreferencesViewController - -UITextField *jargsTextField; -UITextField *rendTextField; -UITextField *jhomeTextField; -UITextField *gdirTextField; -UITextField *activeField; -NSArray* rendererList; -NSMutableArray* gdirList; -NSArray* jhomeList; -UIPickerView* rendPickerView; -UIPickerView* gdirPickerView; -UIPickerView* jhomePickerView; -UISwitch *slideHotbarSwitch; -UIBlurEffect *blur; -UIVisualEffectView *blurView; -UIScrollView *scrollView; - -NSDictionary *rendererDict; -NSArray *menuDict; - -int tempIndex; - -- (void)viewDidLoad -{ - [super viewDidLoad]; - setViewBackgroundColor(self.view); - - CGRect screenBounds = [[UIScreen mainScreen] bounds]; - CGFloat screenScale = [[UIScreen mainScreen] scale]; - - int width = (int) roundf(screenBounds.size.width); - int height = (int) roundf(screenBounds.size.height) - self.navigationController.navigationBar.frame.size.height; - CGFloat currY = 8.0; - - rendererDict = @{ - @"gl4es 1.1.4 - exports OpenGL 2.1": @"libgl4es_114.dylib", - @"tinygl4angle (1.17+) - exports OpenGL 3.2 (Core Profile, limited)": @"libtinygl4angle.dylib", - @"Zink (Mesa 21.0) - exports OpenGL 4.1": @"libOSMesaOverride.dylib" - }; - - scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame]; - scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; - [self.view addSubview:scrollView]; - scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - - UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width + 20, 0)]; - [scrollView addSubview:tableView]; - - [self registerForKeyboardNotifications]; - - blur = [UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular]; - blurView = [[UIVisualEffectView alloc] initWithEffect:blur]; - - UILabel *btnsizeTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY, 0.0, 30.0)]; - btnsizeTextView.text = @"Button scale (%)"; - btnsizeTextView.numberOfLines = 0; - btnsizeTextView.textAlignment = NSTextAlignmentCenter; - [btnsizeTextView sizeToFit]; - CGRect tempRect = btnsizeTextView.frame; - tempRect.size.height = 30.0; - btnsizeTextView.frame = tempRect; - [tableView addSubview:btnsizeTextView]; - - DBNumberedSlider *buttonSizeSlider = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(20.0 + btnsizeTextView.frame.size.width, currY, self.view.frame.size.width - btnsizeTextView.frame.size.width - 28.0, btnsizeTextView.frame.size.height)]; - buttonSizeSlider.tag = TAG_BTNSCALE; - [buttonSizeSlider addTarget:self action:@selector(sliderMoved:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]; - [buttonSizeSlider setBackgroundColor:[UIColor clearColor]]; - buttonSizeSlider.minimumValue = 50.0; - buttonSizeSlider.maximumValue = 500.0; - buttonSizeSlider.continuous = YES; - buttonSizeSlider.value = [getPreference(@"button_scale") floatValue]; - [tableView addSubview:buttonSizeSlider]; - - UILabel *resolutionTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=43.0, 0.0, 30.0)]; - resolutionTextView.text = @"Resolution (%)"; - resolutionTextView.numberOfLines = 0; - resolutionTextView.textAlignment = NSTextAlignmentCenter; - [resolutionTextView sizeToFit]; - tempRect = resolutionTextView.frame; - tempRect.size.height = 30.0; - resolutionTextView.frame = tempRect; - [tableView addSubview:resolutionTextView]; - - DBNumberedSlider *resolutionSlider = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(20.0 + btnsizeTextView.frame.size.width, currY, self.view.frame.size.width - btnsizeTextView.frame.size.width - 28.0, resolutionTextView.frame.size.height)]; - resolutionSlider.tag = TAG_RESOLUTION; - [resolutionSlider addTarget:self action:@selector(sliderMoved:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]; - [resolutionSlider setBackgroundColor:[UIColor clearColor]]; - resolutionSlider.minimumValue = 25; - resolutionSlider.maximumValue = 150; - resolutionSlider.continuous = YES; - resolutionSlider.value = [getPreference(@"resolution") intValue]; - [tableView addSubview:resolutionSlider]; - - - if(getenv("POJAV_DETECTEDJB") || [getPreference(@"ram_unjb_enable") boolValue] == YES) { - // You cannot bypass jetsam memory limits unjailbroken - // Use with caution on unjailbroken devices - UILabel *memTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=45.0, 0.0, 30.0)]; - memTextView.text = @"Allocated RAM"; - memTextView.numberOfLines = 0; - memTextView.textAlignment = NSTextAlignmentCenter; - [memTextView sizeToFit]; - tempRect = memTextView.frame; - tempRect.size.height = 30.0; - memTextView.frame = tempRect; - [tableView addSubview:memTextView]; - - DBNumberedSlider *memSlider = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(20.0 + btnsizeTextView.frame.size.width, currY, self.view.frame.size.width - btnsizeTextView.frame.size.width - 28.0, resolutionTextView.frame.size.height)]; - memSlider.tag = TAG_ALLOCMEM; - [memSlider addTarget:self action:@selector(sliderMoved:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]; - [memSlider setBackgroundColor:[UIColor clearColor]]; - memSlider.minimumValue = roundf(([[NSProcessInfo processInfo] physicalMemory] / 1048576) * 0.25); - memSlider.maximumValue = roundf(([[NSProcessInfo processInfo] physicalMemory] / 1048576) * 0.85); - memSlider.fontSize = 10; - memSlider.continuous = YES; - memSlider.value = [getPreference(@"allocated_memory") intValue]; - [tableView addSubview:memSlider]; - } - UILabel *jargsTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=47.0, 0.0, 0.0)]; - jargsTextView.text = @"Java arguments "; - jargsTextView.numberOfLines = 0; - [jargsTextView sizeToFit]; - [tableView addSubview:jargsTextView]; - - jargsTextField = [[UITextField alloc] initWithFrame:CGRectMake(buttonSizeSlider.frame.origin.x + 3, currY, width - jargsTextView.bounds.size.width - 28.0, 30)]; - [jargsTextField addTarget:jargsTextField action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; - [jargsTextField setReturnKeyType:UIReturnKeyDone]; - jargsTextField.tag = TAG_JARGS; - jargsTextField.delegate = self; - jargsTextField.placeholder = @"Specify arguments..."; - jargsTextField.text = (NSString *) getPreference(@"java_args"); - jargsTextField.autocorrectionType = UITextAutocorrectionTypeNo; - jargsTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop; - jargsTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; - [tableView addSubview:jargsTextField]; - - UILabel *rendTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - rendTextView.text = @"Renderer"; - rendTextView.numberOfLines = 0; - [rendTextView sizeToFit]; - [tableView addSubview:rendTextView]; - - 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")]; - tempIndex = [rendererDict.allValues indexOfObject:getPreference(@"renderer")]; - rendTextField.text = rendererDict.allKeys[tempIndex]; - - rendTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop; - rendTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; - [tableView addSubview:rendTextField]; - - //[rendererList addObject:virglrenderer]; - - rendPickerView = [[UIPickerView alloc] init]; - rendPickerView.delegate = self; - rendPickerView.dataSource = self; - rendPickerView.tag = TAG_PICKER_REND; - [rendPickerView reloadAllComponents]; - [rendPickerView selectRow:tempIndex inComponent:0 animated:NO]; - UIToolbar *rendPickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)]; - UIBarButtonItem *rendFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; - UIBarButtonItem *rendDoneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeKeyboard:)]; - rendDoneButton.tag = TAG_DONE_REND; - rendPickerToolbar.items = @[rendFlexibleSpace, rendDoneButton]; - - rendTextField.inputAccessoryView = rendPickerToolbar; - rendTextField.inputView = rendPickerView; - - UILabel *jhomeTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - jhomeTextView.text = @"Java version"; - jhomeTextView.numberOfLines = 0; - [jhomeTextView sizeToFit]; - [tableView addSubview:jhomeTextView]; - - 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..."; - - jhomeTextField.text = [getPreference(@"java_home") lastPathComponent]; - - jhomeTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop; - jhomeTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; - [tableView addSubview:jhomeTextField]; - - 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 ([jhomeTextField.text isEqualToString:jhomeList[i]]) { - [jhomePickerView selectRow:i inComponent:0 animated:NO]; - break; - } - } - UIToolbar *jhomePickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)]; - UIBarButtonItem *jhomeFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; - UIBarButtonItem *jhomeDoneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeKeyboard:)]; - jhomeDoneButton.tag = TAG_DONE_JHOME; - jhomePickerToolbar.items = @[jhomeFlexibleSpace, jhomeDoneButton]; - - jhomeTextField.inputAccessoryView = jhomePickerToolbar; - jhomeTextField.inputView = jhomePickerView; - - UILabel *gdirTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - gdirTextView.text = @"Game directory"; - gdirTextView.numberOfLines = 0; - [gdirTextView sizeToFit]; - [tableView addSubview:gdirTextView]; - - gdirTextField = [[UITextField alloc] initWithFrame:CGRectMake(buttonSizeSlider.frame.origin.x + 3, currY, width - jargsTextView.bounds.size.width - 28.0, 30)]; - [gdirTextField addTarget:jhomeTextField action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; - gdirTextField.tag = TAG_GDIR; - gdirTextField.delegate = self; - gdirTextField.placeholder = @"Custom game directory..."; - gdirTextField.text = (NSString *) getPreference(@"game_directory"); - gdirTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop; - gdirTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; - [tableView addSubview:gdirTextField]; - - gdirPickerView = [[UIPickerView alloc] init]; - gdirPickerView.delegate = self; - gdirPickerView.dataSource = self; - gdirPickerView.tag = TAG_PICKER_GDIR; - [self instanceDirCont]; - UIToolbar *gdirPickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)]; - UIBarButtonItem *gdirFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; - UIBarButtonItem *gdirDoneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeKeyboard:)]; - UIBarButtonItem *gdirCreateButton = [[UIBarButtonItem alloc] initWithTitle:@"Create new" style:UIBarButtonItemStyleDone target:self action:@selector(createDir:)]; - UIBarButtonItem *gdirDeleteButton = [[UIBarButtonItem alloc] initWithTitle:@"Delete" style:UIBarButtonItemStyleDone target:self action:@selector(removeDir:)]; - gdirDoneButton.tag = TAG_DONE_GDIR; - gdirPickerToolbar.items = @[gdirCreateButton, gdirDeleteButton, gdirFlexibleSpace, gdirDoneButton]; - - gdirTextField.inputAccessoryView = gdirPickerToolbar; - gdirTextField.inputView = gdirPickerView; - - UILabel *slideHotbarTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - slideHotbarTextView.text = @"Slideable hotbar"; - slideHotbarTextView.numberOfLines = 0; - [slideHotbarTextView sizeToFit]; - [tableView addSubview:slideHotbarTextView]; - - slideHotbarSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - slideHotbarSwitch.tag = TAG_SLIDEHOTBAR; - [slideHotbarSwitch setOn:[getPreference(@"slideable_hotbar") boolValue] animated:NO]; - [slideHotbarSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:slideHotbarSwitch]; - - UILabel *resetWarnTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - resetWarnTextView.text = @"Reset launcher warnings"; - resetWarnTextView.numberOfLines = 0; - [resetWarnTextView sizeToFit]; - [tableView addSubview:resetWarnTextView]; - - UISwitch *resetWarnSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - resetWarnSwitch.tag = TAG_RESETWARN; - [resetWarnSwitch setOn:NO animated:NO]; - [resetWarnSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:resetWarnSwitch]; - - UILabel *checkSHATextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - checkSHATextView.text = @"Check game files before launching"; - checkSHATextView.numberOfLines = 0; - [checkSHATextView sizeToFit]; - [tableView addSubview:checkSHATextView]; - - UISwitch *checkSHASwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - checkSHASwitch.tag = TAG_CHECKSHA; - [checkSHASwitch setOn:[getPreference(@"check_sha") boolValue] animated:NO]; - [checkSHASwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:checkSHASwitch]; - - UILabel *debugLogTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - debugLogTextView.text = @"Enable debug logging"; - debugLogTextView.numberOfLines = 0; - [debugLogTextView sizeToFit]; - [tableView addSubview:debugLogTextView]; - - UISwitch *debugLogSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - debugLogSwitch.tag = TAG_DEBUGLOG; - [debugLogSwitch setOn:[getPreference(@"debug_logging") boolValue] animated:NO]; - [debugLogSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:debugLogSwitch]; - - if(getenv("POJAV_DETECTEDJB")) { - // Used for legacy iOS users who are used to - // the old home directory for the game - // TODO: Remove for everyone - UILabel *homesymTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - homesymTextView.text = @"Disable home symlink"; - homesymTextView.numberOfLines = 0; - [homesymTextView sizeToFit]; - [tableView addSubview:homesymTextView]; - - UISwitch *homesymSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - homesymSwitch.tag = TAG_HOMESYM; - [homesymSwitch setOn:[getPreference(@"disable_home_symlink") boolValue] animated:NO]; - [homesymSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:homesymSwitch]; - } - - UILabel *relaunchTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - relaunchTextView.text = @"Restart before launching game"; - relaunchTextView.numberOfLines = 0; - [relaunchTextView sizeToFit]; - [tableView addSubview:relaunchTextView]; - - UISwitch *relaunchSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - relaunchSwitch.tag = TAG_RELAUNCH; - [relaunchSwitch setOn:[getPreference(@"restart_before_launch") boolValue] animated:NO]; - [relaunchSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:relaunchSwitch]; - - UILabel *arcCapesTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - arcCapesTextView.text = @"Enable Cosmetica capes"; - arcCapesTextView.numberOfLines = 0; - [arcCapesTextView sizeToFit]; - [tableView addSubview:arcCapesTextView]; - - UISwitch *arcCapesSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - arcCapesSwitch.tag = TAG_ARCCAPES; - [arcCapesSwitch setOn:[getPreference(@"arccapes_enable") boolValue] animated:NO]; - [arcCapesSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:arcCapesSwitch]; - - UILabel *virtMouseTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - virtMouseTextView.text = @"Enable virtual mouse"; - virtMouseTextView.numberOfLines = 0; - [virtMouseTextView sizeToFit]; - [tableView addSubview:virtMouseTextView]; - - UISwitch *virtMouseSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - virtMouseSwitch.tag = TAG_VIRTMOUSE; - [virtMouseSwitch setOn:[getPreference(@"virtmouse_enable") boolValue] animated:NO]; - [virtMouseSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:virtMouseSwitch]; - - UILabel *erasePrefTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - erasePrefTextView.text = @"Reset all settings"; - erasePrefTextView.numberOfLines = 0; - [erasePrefTextView sizeToFit]; - [tableView addSubview:erasePrefTextView]; - - UISwitch *erasePrefSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - erasePrefSwitch.tag = TAG_ERASEPREF; - [erasePrefSwitch setOn:NO animated:NO]; - [erasePrefSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:erasePrefSwitch]; - - if(!getenv("POJAV_DETECTEDJB")) { - UILabel *enableRAMTextView = [[UILabel alloc] initWithFrame:CGRectMake(16.0, currY+=44.0, 0.0, 0.0)]; - enableRAMTextView.text = @"Enable RAM slider"; - enableRAMTextView.numberOfLines = 0; - [enableRAMTextView sizeToFit]; - [tableView addSubview:enableRAMTextView]; - - UISwitch *enableRAMSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currY - 5.0, 50.0, 30)]; - enableRAMSwitch.tag = TAG_UNJBRAM; - [enableRAMSwitch setOn:[getPreference(@"ram_unjb_enable") boolValue] animated:NO]; - [enableRAMSwitch addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; - [tableView addSubview:enableRAMSwitch]; - } - - CGRect frame = tableView.frame; - frame.size.height = currY+=44; - tableView.frame = frame; - scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, tableView.frame.size.height); - - if (@available(iOS 14.0, *)) { - // use UIMenu - UIBarButtonItem *help = [[UIBarButtonItem alloc] initWithImage:[[UIImage systemImageNamed:@"questionmark.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] style:UIBarButtonItemStyleDone target:self action:@selector(helpMenu)]; - UIBarButtonItem *close = [[UIBarButtonItem alloc] initWithImage:[[UIImage systemImageNamed:@"xmark.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] style:UIBarButtonItemStyleDone target:self action:@selector(exitAppAlert)]; - self.navigationItem.rightBarButtonItems = @[help, close]; - UIAction *buttonScale = [UIAction actionWithTitle:@"Button scale" image:[[UIImage systemImageNamed:@"aspectratio"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:BTNSCALE];}]; - UIAction *resolution = [UIAction actionWithTitle:@"Resolution" image:[[UIImage systemImageNamed:@"viewfinder"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:RESOLUTION];}]; - UIAction *allocRAM = [UIAction actionWithTitle:@"Allocated RAM" image:[[UIImage systemImageNamed:@"memorychip"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:ALLOCMEM];}]; - UIAction *jargs = [UIAction actionWithTitle:@"Java arguments" image:[[UIImage systemImageNamed:@"character.cursor.ibeam"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:JARGS];}]; - UIAction *renderer = [UIAction actionWithTitle:@"Renderer" image:[[UIImage systemImageNamed:@"cpu"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:REND];}]; - UIAction *jversion = [UIAction actionWithTitle:@"Java version" image:[[UIImage systemImageNamed:@"cube"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:JHOME];}]; - UIAction *gamedir = [UIAction actionWithTitle:@"Game directory" image:[[UIImage systemImageNamed:@"folder"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:GDIRECTORY];}]; - UIAction *hotbar = [UIAction actionWithTitle:@"Slideable hotbar" image:[[UIImage systemImageNamed:@"slider.horizontal.below.rectangle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:SLIDEHOTBAR];}]; - UIAction *resetwarn = [UIAction actionWithTitle:@"Reset warnings" image:[[UIImage systemImageNamed:@"exclamationmark.triangle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:RESETWARN];}]; - UIAction *shacheck = [UIAction actionWithTitle:@"Check game files before launching" image:[[UIImage systemImageNamed:@"shield.lefthalf.fill"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:CHECKSHA];}]; - UIAction *safearea = [UIAction actionWithTitle:@"Safe area" image:[[UIImage systemImageNamed:@"crop"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:SAFEAREA];}]; - UIAction *debugLog = [UIAction actionWithTitle:@"Debug logging" image:[[UIImage systemImageNamed:@"doc.badge.gearshape"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:DEBUGLOG];}]; - UIAction *restart = [UIAction actionWithTitle:@"Restart before launching game" image:[[UIImage systemImageNamed:@"arrowtriangle.left.circle"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:RELAUNCH];}]; - UIAction *symlink = [UIAction actionWithTitle:@"Home symlink" image:[[UIImage systemImageNamed:@"link"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:HOMESYM];}]; - UIAction *cosmetica = [UIAction actionWithTitle:@"Enable Cosmetica capes" image:[[UIImage systemImageNamed:@"square.and.arrow.down"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:ARCCAPES];}]; - UIAction *virtmouse = [UIAction actionWithTitle:@"Enable virtual mouse" image:[[UIImage systemImageNamed:@"cursorarrow.rays"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:VIRTMOUSE];}]; - UIAction *resetprefs = [UIAction actionWithTitle:@"Reset preferences" image:[[UIImage systemImageNamed:@"trash"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:ERASEPREF];}]; - UIAction *enableRAM = [UIAction actionWithTitle:@"Enable RAM slider" image:[[UIImage systemImageNamed:@"slider.horizontal.3"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] identifier:nil handler:^(__kindof UIAction * _Nonnull action) {[self helpAlertOpt:UNJBRAM];}]; - - if(getenv("POJAV_DETECTEDJB")) { - menuDict = @[buttonScale, resolution, allocRAM, jargs, renderer, jversion, gamedir, hotbar, resetwarn, shacheck, safearea, debugLog, restart, symlink, cosmetica, virtmouse, resetprefs]; - } else if(!getenv("POJAV_DETECTEDJB") && [getPreference(@"disable_home_symlink") boolValue] == YES) { - menuDict = @[buttonScale, resolution, allocRAM, jargs, renderer, jversion, gamedir, hotbar, resetwarn, shacheck, safearea, debugLog, restart, symlink, cosmetica, virtmouse, resetprefs, enableRAM]; - } else { - menuDict = @[buttonScale, resolution, jargs, renderer, jversion, gamedir, hotbar, resetwarn, shacheck, safearea, debugLog, restart, symlink, cosmetica, virtmouse, resetprefs, enableRAM]; - } - - UIMenu *menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:menuDict]; - self.navigationItem.rightBarButtonItem.action = nil; - self.navigationItem.rightBarButtonItem.primaryAction = nil; - self.navigationItem.rightBarButtonItem.menu = menu; - } else { - UIBarButtonItem *help = [[UIBarButtonItem alloc] initWithTitle:@"Help" style:UIBarButtonItemStyleDone target:self action:@selector(helpMenu)]; - UIBarButtonItem *close = [[UIBarButtonItem alloc] initWithTitle:@"Close App" style:UIBarButtonItemStyleDone target:self action:@selector(exitApp)]; - self.navigationItem.rightBarButtonItems = @[help, close]; - } -} - -- (void)registerForKeyboardNotifications -{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWasShown:) - name:UIKeyboardDidShowNotification object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWillBeHidden:) - name:UIKeyboardWillHideNotification object:nil]; - -} - -- (void)keyboardWasShown:(NSNotification*)aNotification -{ - NSDictionary* info = [aNotification userInfo]; - CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size; - - UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0); - scrollView.contentInset = contentInsets; - scrollView.scrollIndicatorInsets = contentInsets; - CGRect aRect = self.view.frame; - aRect.size.height -= kbSize.height; - if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) { - CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height); - [scrollView setContentOffset:scrollPoint animated:YES]; - } -} - -- (void)keyboardWillBeHidden:(NSNotification*)aNotification -{ - UIEdgeInsets contentInsets = UIEdgeInsetsZero; - scrollView.contentInset = contentInsets; - scrollView.scrollIndicatorInsets = contentInsets; -} - -- (void)textFieldDidBeginEditing:(UITextField *)textField -{ - activeField = textField; -} - -- (void)textFieldDidEndEditing:(UITextField *)textField { - activeField = nil; - if (textField.tag == TAG_JARGS) { - 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) { - 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]; - [self presentViewController:javaAlert animated:YES completion:nil]; - [javaAlert addAction:ok]; - setPreference(@"java_warn", @NO); - } - } else if (textField.tag == TAG_GDIR) { - setPreference(@"game_directory", textField.text); - init_setupMultiDir(); - } -} - -- (void)exitAppAlert { - UIAlertController *closeAlert = [UIAlertController alertControllerWithTitle:@"Close PojavLauncher?" message:@"" preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *close = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self exitApp];}]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:nil]; - [closeAlert addAction:cancel]; - [closeAlert addAction:close]; - [self presentViewController:closeAlert animated:YES completion:nil]; - -} - -- (void)exitApp { - [blurView setFrame:[[self view] bounds]]; - [blurView setAlpha:0]; - [self.view addSubview:blurView]; - - [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ - [blurView setAlpha:1]; - } completion:^(BOOL finished) { - exit(0); - }]; -} - --(void)instanceDirCont { - NSFileManager *fm = [NSFileManager defaultManager]; - NSArray* files = [fm contentsOfDirectoryAtPath:[NSString stringWithFormat:@"%s/instances", getenv("POJAV_HOME")] error:nil]; - if (gdirList == nil) { - gdirList = [NSMutableArray arrayWithCapacity:10]; - } else { - [gdirList removeAllObjects]; - } - BOOL updateTempIndex = YES; - tempIndex = 0; - for(NSString *file in files) { - NSString *path = [[NSString stringWithFormat:@"%s/instances", getenv("POJAV_HOME")] stringByAppendingPathComponent:file]; - BOOL isDir = NO; - [fm fileExistsAtPath:path isDirectory:(&isDir)]; - if(isDir) { - [gdirList addObject:file]; - if ([file isEqualToString:gdirTextField.text]) { - updateTempIndex = NO; - } - if (updateTempIndex) { - ++tempIndex; - } - } - } - [gdirPickerView reloadAllComponents]; - [gdirPickerView selectRow:tempIndex inComponent:0 animated:NO]; -} - -- (void)createDir:(UIBarButtonItem *)sender { - int type = MKGDIR; - UIAlertController *manageDirAC = [UIAlertController alertControllerWithTitle:@"Create a new game directory" message:@"" preferredStyle:UIAlertControllerStyleAlert]; - [manageDirAC addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { - textField.placeholder = @"Name of new directory"; - textField.secureTextEntry = NO; - }]; - UIAlertAction *confirm = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), [[manageDirAC textFields][0] text]]; - NSError *error; - BOOL isDir = NO; - if([[NSFileManager defaultManager] createDirectoryAtPath:directory withIntermediateDirectories:NO attributes:nil error:&error]) { - [self instanceDirCont]; - gdirTextField.text = [[manageDirAC textFields][0] text]; - [gdirTextField endEditing:YES]; - } - [self manageDirResult:(UIButton *)sender error:error directory:[[manageDirAC textFields][0] text] type:type]; - }]; - [manageDirAC addAction:confirm]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [manageDirAC addAction:cancel]; - [self presentViewController:manageDirAC animated:YES completion:nil]; -} -- (void)removeDir:(UIBarButtonItem *)sender { - int type = RMGDIR; - if(![gdirTextField.text isEqualToString:@"default"]) { - UIAlertController *manageDirAC = [UIAlertController alertControllerWithTitle:@"Are you sure you want to delete this?" message:[NSString stringWithFormat:@"The instance %@ will be deleted forever and cannot be restored.", gdirTextField.text] preferredStyle:UIAlertControllerStyleActionSheet]; - UIAlertAction *confirm = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), gdirTextField.text]; - NSLog(@"%@", directory); - NSError *error; - BOOL isDir = NO; - if([[NSFileManager defaultManager] removeItemAtPath:directory error:&error]) { - [self instanceDirCont]; - gdirTextField.text = @"default"; - [gdirTextField endEditing:YES]; - } - [self manageDirResult:(UIButton *)sender error:error directory:gdirTextField.text type:type]; - }]; - [manageDirAC addAction:confirm]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [manageDirAC addAction:cancel]; - [self presentViewController:manageDirAC animated:YES completion:nil]; - } else if([gdirTextField.text isEqualToString:@"default"]){ - UIAlertController *gdirAlert = [UIAlertController alertControllerWithTitle:@"You cannot delete the default game directory." message:@"" preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:gdirAlert.popoverPresentationController sender:(UIButton *)sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; - [gdirAlert addAction:ok]; - [self presentViewController:gdirAlert animated:YES completion:nil]; - } -} - -- (void)manageDirResult:(UIButton *)sender error:(NSError *)error directory:(NSString *)dirName type:(int)type { - NSString *title; - NSString *message; - if (error == nil) { - if(type == MKGDIR) { - title = @"Successfully changed directory."; - } else if (type == RMGDIR) { - title = @"Successfully removed directory."; - } - } else { - title = @"An error occurred."; - message = error.localizedDescription; - } - - UIAlertController *gdirAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:gdirAlert.popoverPresentationController sender:sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; - [gdirAlert addAction:ok]; - [self presentViewController:gdirAlert animated:YES completion:nil]; -} - -- (void)helpMenu { - if (@available(iOS 14.0, *)) { - // UIMenu - } else { - UIAlertController *helpAlert = [UIAlertController alertControllerWithTitle:@"Needing help with these preferences?" message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - UIAlertAction *btnscale = [UIAlertAction actionWithTitle:@"Button scale" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:BTNSCALE];}]; - [helpAlert addAction:btnscale]; - UIAlertAction *resolution = [UIAlertAction actionWithTitle:@"Resolution" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:RESOLUTION];}]; - [helpAlert addAction:resolution]; - if(getenv("POJAV_DETECTEDJB") || [getPreference(@"disable_home_symlink") boolValue] == YES) { - UIAlertAction *allocmem = [UIAlertAction actionWithTitle:@"Allocated RAM" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:ALLOCMEM];}]; - [helpAlert addAction:allocmem]; - } - UIAlertAction *jargs = [UIAlertAction actionWithTitle:@"Java arguments" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:JARGS];}]; - [helpAlert addAction:jargs]; - UIAlertAction *renderer = [UIAlertAction actionWithTitle:@"Renderer" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:REND];}]; - [helpAlert addAction:renderer]; - if(getenv("POJAV_DETECTEDJB")) { - UIAlertAction *jhome = [UIAlertAction actionWithTitle:@"Java version" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:JHOME];}]; - [helpAlert addAction:jhome]; - } - UIAlertAction *gdirectory = [UIAlertAction actionWithTitle:@"Game directory" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:GDIRECTORY];}]; - [helpAlert addAction:gdirectory]; - UIAlertAction *slidehotbar = [UIAlertAction actionWithTitle:@"Slideable hotbar" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:SLIDEHOTBAR];}]; - [helpAlert addAction:slidehotbar]; - UIAlertAction *resetwarn = [UIAlertAction actionWithTitle:@"Reset warnings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:RESETWARN];}]; - [helpAlert addAction:resetwarn]; - UIAlertAction *checksha = [UIAlertAction actionWithTitle:@"Check game files before launching" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:VIRTMOUSE];}]; - [helpAlert addAction:checksha]; - UIAlertAction *safearea = [UIAlertAction actionWithTitle:@"Safe area" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:SAFEAREA];}]; - [helpAlert addAction:safearea]; - UIAlertAction *debuglog = [UIAlertAction actionWithTitle:@"Debug logging" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:DEBUGLOG];}]; - [helpAlert addAction:debuglog]; - UIAlertAction *relaunch = [UIAlertAction actionWithTitle:@"Restart before launching game" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:RELAUNCH];}]; - [helpAlert addAction:relaunch]; - if(getenv("POJAV_DETECTEDJB")) { - UIAlertAction *homesym = [UIAlertAction actionWithTitle:@"Home symlink" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:HOMESYM];}]; - [helpAlert addAction:homesym]; - } - UIAlertAction *arccapes = [UIAlertAction actionWithTitle:@"Enable Cosmetica capes" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:ARCCAPES];}]; - [helpAlert addAction:arccapes]; - UIAlertAction *virtmouse = [UIAlertAction actionWithTitle:@"Enable virtual mouse" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:VIRTMOUSE];}]; - [helpAlert addAction:virtmouse]; - UIAlertAction *erasepref = [UIAlertAction actionWithTitle:@"Reset preferences" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:ERASEPREF];}]; - [helpAlert addAction:erasepref]; - if(!getenv("POJAV_DETECTEDJB")) { - UIAlertAction *enableRAM = [UIAlertAction actionWithTitle:@"Enable RAM slider" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self helpAlertOpt:UNJBRAM];}]; - [helpAlert addAction:enableRAM]; - } - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [helpAlert addAction:cancel]; - [self setPopoverProperties:helpAlert.popoverPresentationController sender:(UIButton *)self.navigationItem.rightBarButtonItem]; - [self presentViewController:helpAlert animated:YES completion:nil]; - } -} - -- (void)helpAlertOpt:(int)setting { - NSString *title; - NSString *message; - if(setting == BTNSCALE) { - title = @"Button scale"; - message = @"This option allows you to tweak the button scale of the on-screen controls. The numbered slider extends from 50 to 500."; - } else if(setting == RESOLUTION) { - title = @"Resolution"; - message = @"This option allows you to downscale the resolution of the game. Reducing resolution reduces GPU workload for better performance. The minimum resolution scale is 25%."; - } else if(setting == ALLOCMEM) { - title = @"Allocated RAM"; - if(getenv("POJAV_DETECTEDJB")) { - message = @"This option allows you to change the amount of memory used by the game. The minimum is 25% (default) and the maximum is 85%. To be safe, any value over 40% will require a tool like jetsamctl to prevent memory crashes."; - } else if (!(getenv("POJAV_DETECTEDJB") && ![getPreference(@"ram_unjb_enable") boolValue] == NO)){ - message = @"This option allows you to change the amount of memory used by the game. This option is disabled by default on unjailbroken devices due to potential instability of the launcher."; - } else { - message = @"This option allows you to change the amount of memory used by the game. The minimum is 25% (default) and the maximum is 85%. Use this at your own risk while unjailbroken, as it can cause instability or break launching the game entirely."; - } - } else if(setting == JARGS) { - title = @"Java arguments"; - message = @"This option allows you to edit arguments that can be passed to Minecraft. Not all arguments work with PojavLauncher, so be aware."; - } else if(setting == REND) { - title = @"Renderer"; - message = @"This option allows you to change the renderer in use. Choosing tinygl4angle will allow 1.17+ to be played, but will also not work with older versions."; - } else if(setting == JHOME) { - title = @"Java version"; - if(getenv("POJAV_DETECTEDJB")) { - message = @"This option allows you to change the Java executable directory. Choosing Java 16 or Java 17 may allow you to play 1.17, however older versions and most versions of modded Minecraft, as well as the mod installer, will not work."; - } else { - message = @"This option is disabled when running PojavLauncher unjailbroken."; - } - } else if(setting == GDIRECTORY) { - title = @"Game directory"; - message = @"This option allows you to change where your Minecraft settings and saves are stored in a MultiMC like fashion. Useful for when you want to have multiple mod loaders installed but do not want to delete/move mods around to switch."; - } else if(setting == SLIDEHOTBAR) { - title = @"Slideable hotbar"; - message = @"This option allows you to use a finger to slide between hotbar slots."; - } else if(setting == SAFEAREA) { - title = @"Safe area"; - message = @"This option moves the game surface area into a safe area instead of fullscreen. This option is only available for iPhones with notch."; - } else if(setting == RESETWARN) { - title = @"Reset warnings"; - message = @"This option re-enables all warnings to be shown again."; - } else if(setting == TYPESEL) { - title = @"Type switches"; - message = @"These switches allow to to change where or not releases, snapshots, old betas, and old alphas will show up in the version selection menu."; - } else if(setting == CHECKSHA) { - title = @"Check game files before launching"; - message = @"When this option is enabled, every file is validated with SHA1, therefore local modifications will be overwritten."; - } else if(setting == DEBUGLOG) { - title = @"Enable debug logging"; - message = @"This option logs internal settings and actions to latestlog.txt. This helps the developers find issues easier, but Minecraft may run slower as the logs will be written to more often."; - } else if(setting == HOMESYM) { - title = @"Disable home symlink"; - if(getenv("POJAV_DETECTEDJB")) { - message = @"This option disables the home symlink to /var/mobile/Documents/.pojavlauncher from the new game directory. This is used for legacy add-ons to continue working, and as a transition from the old 1.2 directory structure."; - } else { - message = @"This option is disabled when running PojavLauncher unjailbroken."; - } - } else if(setting == RELAUNCH) { - title = @"Restart before launching game"; - message = @"This option allows the launcher to restart itself before launching the game, therefore all changes are applied such as JVM Arguments and Java version. The restart mechanism doesn't always work, so a toggle is here for now."; - } else if (setting == ERASEPREF) { - title = @"Reset preferences"; - message = @"This option resets the launcher preferences to their initial state."; - } else if (setting == ARCCAPES) { - title = @"Enable Cosmetica capes"; - message = @"This option allows you to switch from the OptiFine cape service to Cosmetica. See more about Cosmetica on our website."; - } else if (setting == VIRTMOUSE) { - title = @"Enable virtual mouse"; - message = @"This option allows you to enable or disable the virtual mouse pointer at launch of the game."; - } else if (setting == UNJBRAM){ - title = @"Enable RAM slider"; - message = @"This option allows you to enable or disable the Allocated RAM slider when unjailbroken. See \"Allocated RAM\" for more information."; - } else { - title = @"Error"; - message = [NSString stringWithFormat:@"The setting %d hasn't been specified with a description.", setting]; - } - UIAlertController *helpAlertOpt = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; - [self setPopoverProperties:helpAlertOpt.popoverPresentationController sender:(UIButton *)self.navigationItem.rightBarButtonItem]; - [helpAlertOpt addAction:ok]; - [self presentViewController:helpAlertOpt animated:YES completion:nil]; -} - -- (void)sliderMoved:(DBNumberedSlider *)sender { - float memVal; - switch (sender.tag) { - case TAG_BTNSCALE: - setPreference(@"button_scale", @((int)sender.value)); - break; - case TAG_RESOLUTION: - setPreference(@"resolution", @((int)sender.value)); - break; - case TAG_ALLOCMEM: - memVal = roundf(([[NSProcessInfo processInfo] physicalMemory] / 1048576) * 0.40); - if(sender.value >= memVal && [getPreference(@"mem_warn") boolValue] == YES) { - NSString *title = @"High memory value selected."; - NSString *message; - if(getenv("POJAV_DETECTEDJB")) { - message = @"Due to limitations in the operating system itself, you will need to use a tool like overb0ard to prevent jetsam crashes."; - } else { - message = @"Due to limitations in the operating system itself, this option may cause instability at this and higher values. Proceed with caution."; - } - UIAlertController *memAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:memAlert.popoverPresentationController sender:(UIButton *)sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:memAlert animated:YES completion:nil]; - [memAlert addAction:ok]; - setPreference(@"mem_warn", @NO); - } - setPreference(@"allocated_memory", @((int)sender.value)); - break; - default: - NSLog(@"what does slider %ld for? implement me!", sender.tag); - break; - } -} - -- (void)switchChanged:(UISwitch *)sender { - switch (sender.tag) { - case TAG_RESETWARN: - setPreference(@"mem_warn", @YES); - setPreference(@"option_warn", @YES); - setPreference(@"local_warn", @YES); - setPreference(@"java_warn", @YES); - setPreference(@"jb_warn", @YES); - setPreference(@"customctrl_warn", @YES); - setPreference(@"int_warn", @YES); - setPreference(@"ram_unjb_warn", @YES); - { - UIAlertController *resetWarn = [UIAlertController alertControllerWithTitle:@"Warnings reset." message:@"Restart to show warnings again." preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:resetWarn.popoverPresentationController sender:(UIButton *)sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:resetWarn animated:YES completion:nil]; - [resetWarn addAction:ok]; - } - break; - case TAG_SLIDEHOTBAR: - setPreference(@"slideable_hotbar", @(sender.isOn)); - break; - case TAG_CHECKSHA: - setPreference(@"check_sha", @(sender.isOn)); - break; - case TAG_DEBUGLOG: - setPreference(@"debug_logging", @(sender.isOn)); - break; - case TAG_HOMESYM: - setPreference(@"disable_home_symlink", @(sender.isOn)); - if([getPreference(@"disable_home_symlink") boolValue] == YES){ - NSString *message = [NSString stringWithFormat:@"This will remove the link at /var/mobile/Documents/.pojavlauncher. The new directory is %s.", getenv("POJAV_HOME")]; - UIAlertController *homesymWarn = [UIAlertController alertControllerWithTitle:@"Are you sure?" message:message preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:homesymWarn.popoverPresentationController sender:(UIButton *)sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[[NSFileManager defaultManager] removeItemAtPath:@"/var/mobile/Documents/.pojavlauncher" error:nil];}]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:homesymWarn animated:YES completion:nil]; - [homesymWarn addAction:cancel]; - [homesymWarn addAction:ok]; - } else { - NSString *message = [NSString stringWithFormat:@"This will create a link to %s at /var/mobile/Documents/.pojavlauncher. This option will be removed in a future release.", getenv("POJAV_HOME")]; - UIAlertController *homesymWarn = [UIAlertController alertControllerWithTitle:@"Are you sure?" message:message preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:homesymWarn.popoverPresentationController sender:(UIButton *)sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {symlink("/usr/share/pojavlauncher", "/var/mobile/Documents/.pojavlauncher");}]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:homesymWarn animated:YES completion:nil]; - [homesymWarn addAction:cancel]; - [homesymWarn addAction:ok]; - } - break; - case TAG_RELAUNCH: - setPreference(@"restart_before_launch", @(sender.isOn)); - break; - case TAG_ERASEPREF: - { - NSString *prefFile = [NSString stringWithFormat:@"%s/launcher_preferences.plist", getenv("POJAV_HOME")]; - UIAlertController *eraseprefWarn = [UIAlertController alertControllerWithTitle:@"Are you sure?" message:@"This will remove all of your custom preferences, including resolution, button size, and Java arguments." preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:eraseprefWarn.popoverPresentationController sender:(UIButton *)sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - [[NSFileManager defaultManager] removeItemAtPath:prefFile error:nil]; - UIAlertController *eraseprefPost = [UIAlertController alertControllerWithTitle:@"Preferences reset" message:@"The next time you open PojavLauncher, all of the default settings will be restored." preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:eraseprefWarn.popoverPresentationController sender:(UIButton *)sender]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:eraseprefPost animated:YES completion:nil]; - [eraseprefPost addAction:cancel]; - }]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:eraseprefWarn animated:YES completion:nil]; - [eraseprefWarn addAction:cancel]; - [eraseprefWarn addAction:ok]; - } - break; - case TAG_ARCCAPES: - setPreference(@"arccapes_enable", @(sender.isOn)); - break; - case TAG_VIRTMOUSE: - setPreference(@"virtmouse_enable", @(sender.isOn)); - break; - case TAG_UNJBRAM: - { - setPreference(@"ram_unjb_enable", @(sender.isOn)); - UIAlertController *enableRAMPost = [UIAlertController alertControllerWithTitle:@"RAM slider option has been changed" message:@"You will need to restart the launcher for changes to take effect." preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:enableRAMPost.popoverPresentationController sender:(UIButton *)sender]; - UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; - [self presentViewController:enableRAMPost animated:YES completion:nil]; - [enableRAMPost addAction:ok]; - } - break; - default: - NSLog(@"what does switch %ld for? implement me!", sender.tag); - break; - } -} - -- (void)setPopoverProperties:(UIPopoverPresentationController *)controller sender:(UIButton *)sender { - if (controller != nil) { - controller.sourceView = sender; - controller.sourceRect = sender.bounds; - } -} - -#pragma mark - UIPopoverPresentationControllerDelegate -- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { - return UIModalPresentationNone; -} - -#pragma mark - UIPickerView -- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { - if(pickerView.tag == TAG_PICKER_REND) { - rendTextField.text = [self pickerView:pickerView titleForRow:row forComponent:component]; - } else if(pickerView.tag == TAG_PICKER_JHOME) { - jhomeTextField.text = [self pickerView:pickerView titleForRow:row forComponent:component]; - } else if(pickerView.tag == TAG_PICKER_GDIR) { - gdirTextField.text = [self pickerView:pickerView titleForRow:row forComponent:component]; - setPreference(@"game_directory", gdirTextField.text); - init_setupMultiDir(); - } -} - -- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView { - return 1; -} - -- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { - if(pickerView.tag == TAG_PICKER_REND) { - return rendererDict.allKeys.count; - } else if(pickerView.tag == TAG_PICKER_JHOME) { - return jhomeList.count; - } else if(pickerView.tag == TAG_PICKER_GDIR) { - return gdirList.count; - } else { - return 0; - } -} - -- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { - NSObject *object; - if(pickerView.tag == TAG_PICKER_REND) { - object = [rendererDict.allKeys objectAtIndex:row]; - } else if(pickerView.tag == TAG_PICKER_JHOME) { - object = [jhomeList objectAtIndex:row]; - } else if(pickerView.tag == TAG_PICKER_GDIR) { - object = [gdirList objectAtIndex:row]; - } - return (NSString*) object; -} - -- (void)closeKeyboard:(UIBarButtonItem *)doneButton { - if (doneButton.tag == TAG_DONE_REND) { - [rendTextField endEditing:YES]; - } else if (doneButton.tag == TAG_DONE_JHOME) { - [jhomeTextField endEditing:YES]; - } else if (doneButton.tag == TAG_DONE_GDIR) { - [gdirTextField endEditing:YES]; - } -} - -@end diff --git a/Natives/LauncherPreferencesViewController.h b/Natives/LauncherPreferencesViewController2.h similarity index 65% rename from Natives/LauncherPreferencesViewController.h rename to Natives/LauncherPreferencesViewController2.h index 08d7e687..f983eda1 100644 --- a/Natives/LauncherPreferencesViewController.h +++ b/Natives/LauncherPreferencesViewController2.h @@ -5,6 +5,6 @@ #define JRE17_NAME_JB @"Java 17" #define JRE8_NAME_SB @"Java 8 (sandbox)" -@interface LauncherPreferencesViewController : UIViewController +@interface LauncherPreferencesViewController2 : UITableViewController @end diff --git a/Natives/LauncherPreferencesViewController2.m b/Natives/LauncherPreferencesViewController2.m new file mode 100644 index 00000000..9f1aeea5 --- /dev/null +++ b/Natives/LauncherPreferencesViewController2.m @@ -0,0 +1,573 @@ +#import +#import + +#import "DBNumberedSlider.h" +#import "LauncherPreferences.h" +#import "LauncherPreferencesViewController2.h" +#import "LauncherPrefGameDirViewController.h" +#import "TOInsetGroupedTableView.h" + +#include "utils.h" + +typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); + +@interface LauncherPreferencesViewController2() {} +@property NSArray* prefSections; +@property NSArray*>* prefContents; +@property BOOL prefDetailVisible; + +@property CreateView typeButton, typeChildPane, typePickField, typeTextField, typeSlider, typeSwitch; +@end + +@implementation LauncherPreferencesViewController2 + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [self initViewCreation]; + + self.prefDetailVisible = self.navigationController == nil; + + self.tableView = [[TOInsetGroupedTableView alloc] init]; + self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; + self.tableView.sectionHeaderHeight = 50; + + if (self.navigationController == nil) { + self.tableView.alpha = 0.9; + } + + BOOL(^whenNotInGame)() = ^BOOL(){ + return self.navigationController != nil; + }; + + self.prefSections = @[@"general", @"video", @"control", @"java"]; + + self.prefContents = @[ + @[ + // General settings + @{@"key": @"game_directory", + @"icon": @"folder", + @"type": self.typeChildPane, + @"enableCondition": whenNotInGame, + @"class": LauncherPrefGameDirViewController.class, + }, + @{@"key": @"home_symlink", + @"hasDetail": @YES, + @"icon": @"link", + @"type": self.typeSwitch, + @"enableCondition": ^BOOL(){ + return getenv("POJAV_DETECTEDJB") != NULL && whenNotInGame(); + } + }, + @{@"key": @"check_sha", + @"hasDetail": @YES, + @"icon": @"lock.shield", + @"type": self.typeSwitch, + @"enableCondition": whenNotInGame + }, + @{@"key": @"cosmetica", + @"hasDetail": @YES, + @"icon": @"eyeglasses", + @"type": self.typeSwitch, + @"enableCondition": whenNotInGame + }, + @{@"key": @"debug_logging", + @"hasDetail": @YES, + @"icon": @"doc.badge.gearshape", + @"type": self.typeSwitch + }, + @{@"key": @"reset_warnings", + @"icon": @"exclamationmark.triangle", + @"type": self.typeButton, + @"enableCondition": whenNotInGame, + @"action": ^void(){ + resetWarnings(); + } + }, + @{@"key": @"reset_settings", + @"icon": @"trash", + @"type": self.typeButton, + @"enableCondition": whenNotInGame, + @"requestReload": @YES, + @"showConfirmPrompt": @YES, + @"destructive": @YES, + @"action": ^void(){ + loadPreferences(YES); + [self.tableView reloadData]; + } + }, + ], @[ + // Video and renderer settings + @{@"key": @"renderer", + @"icon": @"cpu", + @"type": self.typePickField, + @"enableCondition": whenNotInGame, + @"pickKeys": @[ + @"auto", + @ RENDERER_NAME_GL4ES, + @ RENDERER_NAME_MTL_ANGLE, + @ RENDERER_NAME_VK_ZINK + ], + @"pickList": @[ + NSLocalizedString(@"preference.title.renderer.auto", nil), + NSLocalizedString(@"preference.title.renderer.gl4es", nil), + NSLocalizedString(@"preference.title.renderer.tinygl4angle", nil), + NSLocalizedString(@"preference.title.renderer.zink", nil) + ] + }, + @{@"key": @"resolution", + @"hasDetail": @YES, + @"icon": @"viewfinder", + @"type": self.typeSlider, + @"min": @(25), + @"max": @(150) + }, + ], @[ + // Control settings + /* + @"disable_gesture": @{ + @"type": self.typeSwitch + }, + */ + @{@"key": @"press_duration", + @"hasDetail": @YES, + @"icon": @"timer", + @"type": self.typeSlider, + @"min": @(100), + @"max": @(1000) + }, + @{@"key": @"button_scale", + @"hasDetail": @YES, + @"icon": @"aspectratio", + @"type": self.typeSlider, + @"min": @(50), // 80? + @"max": @(500) + }, + @{@"key": @"mouse_scale", + @"hasDetail": @YES, + @"icon": @"arrow.up.left.and.arrow.down.right.circle", + @"type": self.typeSlider, + @"min": @(25), + @"max": @(300) + }, + @{@"key": @"mouse_speed", + @"hasDetail": @YES, + @"icon": @"arrow.left.and.right", + @"type": self.typeSlider, + @"min": @(25), + @"max": @(300) + }, + @{@"key": @"virtmouse_enable", + @"hasDetail": @YES, + @"icon": @"cursorarrow.rays", + @"type": self.typeSwitch + }, + @{@"key": @"slideable_hotbar", + @"hasDetail": @YES, + @"icon": @"slider.horizontal.below.rectangle", + @"type": self.typeSwitch + } + ], @[ + // Java tweaks + @{@"key": @"java_home", // Use Java 17 for Minecraft < 1.17 + @"hasDetail": @YES, + @"icon": @"cube", + @"type": self.typeSwitch, + @"enableCondition": whenNotInGame, + // false: 8, true: 17 + @"customSwitchValue": @[@"java-8-openjdk", @"java-17-openjdk"] + }, + @{@"key": @"java_args", + @"hasDetail": @YES, + @"icon": @"slider.vertical.3", + @"type": self.typeTextField, + @"enableCondition": whenNotInGame + }, + @{@"key": @"auto_ram", + @"hasDetail": @YES, + @"icon": @"slider.horizontal.3", + @"type": self.typeSwitch, + @"enableCondition": ^BOOL(){ + return getenv("POJAV_DETECTEDJB") == NULL && whenNotInGame(); + }, + @"warnCondition": ^BOOL(){ + return getenv("POJAV_DETECTEDJB") == NULL && whenNotInGame(); + }, + @"warnKey": @"auto_ram_warn", + @"requestReload": @YES + }, + @{@"key": @"allocated_memory", + @"hasDetail": @YES, + @"icon": @"memorychip", + @"type": self.typeSlider, + @"min": @(NSProcessInfo.processInfo.physicalMemory / 1048576 * 0.25), + @"max": @(NSProcessInfo.processInfo.physicalMemory / 1048576 * 0.85), + @"enableCondition": ^BOOL(){ + return ![getPreference(@"auto_ram") boolValue] && whenNotInGame(); + }, + @"warnCondition": ^BOOL(DBNumberedSlider *view){ + return view.value >= NSProcessInfo.processInfo.physicalMemory / 1048576 * 0.37; + }, + @"warnKey": @"mem_warn" + } + ] + ]; + + UIBarButtonItem *helpButton; + if (@available(iOS 13.0, *)) { + helpButton = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"questionmark.circle"] style:UIBarButtonItemStyleDone target:self action:@selector(toggleDetailVisibility)]; + } else { + helpButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Help", nil) style:UIBarButtonItemStyleDone target:self action:@selector(toggleDetailVisibility)]; + } + self.navigationItem.rightBarButtonItems = @[helpButton]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + // Scan for child pane cells and reload them + // FIXME: any cheaper operations? + NSMutableArray *indexPaths = [[NSMutableArray alloc] init]; + for (int section = 0; section < self.prefContents.count; section++) { + for (int row = 0; row < self.prefContents[section].count; row++) { + if (self.prefContents[section][row][@"type"] == self.typeChildPane) { + [indexPaths addObject:[NSIndexPath indexPathForRow:row inSection:section]]; + } + } + } + [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone]; +} + +- (void)dismissViewController { + [self.presentingViewController performSelector:@selector(updatePreferenceChanges)]; + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)toggleDetailVisibility { + self.prefDetailVisible = !self.prefDetailVisible; + [self.tableView reloadData]; +} + +#pragma mark UITableView + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return self.prefSections.count; +} + +- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { + if (section != 0 || self.navigationController != nil) { + return nil; + } + + UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [doneButton setTitle:NSLocalizedString(@"Done", nil) forState:UIControlStateNormal]; + [doneButton addTarget:self action:@selector(dismissViewController) forControlEvents:UIControlEventTouchUpInside]; + return doneButton; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + return NSLocalizedString(([NSString stringWithFormat:@"preference.section.%@", self.prefSections[section]]), nil); +} + +- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { + NSString *footer = NSLocalizedStringWithDefaultValue(([NSString stringWithFormat:@"preference.section.footer.%@", self.prefSections[section]]), @"Localizable", NSBundle.mainBundle, @" ", nil); + if ([footer isEqualToString:@" "]) { + return nil; + } + return footer; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.prefContents[section].count; +} + +- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + NSDictionary *item = self.prefContents[indexPath.section][indexPath.row]; + + NSString *cellID; + UITableViewCellStyle cellStyle; + if (item[@"type"] == self.typeChildPane) { + cellID = @"cellValue1"; + cellStyle = UITableViewCellStyleValue1; + } else { + cellID = @"cellSubtitle"; + cellStyle = UITableViewCellStyleSubtitle; + } + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; + if (cell == nil) { + cell = [[UITableViewCell alloc] initWithStyle:cellStyle reuseIdentifier:cellID]; + cell.detailTextLabel.numberOfLines = 0; + cell.detailTextLabel.lineBreakMode = NSLineBreakByWordWrapping; + } + // Reset cell properties, as it could be reused + cell.accessoryType = UITableViewCellAccessoryNone; + cell.accessoryView = nil; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + cell.textLabel.textColor = nil; + cell.detailTextLabel.text = nil; + + NSString *key = item[@"key"]; + CreateView createView = item[@"type"]; + createView(cell, key, item); + if (cell.accessoryView) { + objc_setAssociatedObject(cell.accessoryView, @"key", key, OBJC_ASSOCIATION_ASSIGN); + objc_setAssociatedObject(cell.accessoryView, @"item", item, OBJC_ASSOCIATION_ASSIGN); + } + + // Set general properties + if (@available(iOS 13.0, *)) { + BOOL destructive = [item[@"destructive"] boolValue]; + cell.imageView.tintColor = destructive ? UIColor.systemRedColor : nil; + cell.imageView.image = [UIImage systemImageNamed:item[@"icon"]]; + } + cell.textLabel.text = NSLocalizedString(([NSString stringWithFormat:@"preference.title.%@", key]), nil); + if ([item[@"hasDetail"] boolValue] && self.prefDetailVisible) { + cell.detailTextLabel.text = NSLocalizedString(([NSString stringWithFormat:@"preference.detail.%@", key]), nil); + } else if (createView != self.typeChildPane) { + cell.detailTextLabel.text = nil; + } + + // Check if one has enable condition and call if it does + BOOL(^checkEnable)(void) = item[@"enableCondition"]; + cell.userInteractionEnabled = !checkEnable || checkEnable(); + cell.textLabel.enabled = cell.detailTextLabel.enabled = cell.userInteractionEnabled; + [(id)cell.accessoryView setEnabled:cell.userInteractionEnabled]; + + return cell; +} + +#pragma mark initViewCreation, showAlert, checkWarn + +- (void)initViewCreation { + __weak LauncherPreferencesViewController2 *weakSelf = self; + + self.typeButton = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { + BOOL destructive = [item[@"destructive"] boolValue]; + cell.selectionStyle = UITableViewCellSelectionStyleGray; + cell.textLabel.textColor = destructive ? UIColor.systemRedColor : weakSelf.view.tintColor; + }; + + self.typeChildPane = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + cell.selectionStyle = UITableViewCellSelectionStyleGray; + cell.detailTextLabel.text = getPreference(key); + }; + + self.typeTextField = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { + UITextField *view = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width / 2.1, cell.bounds.size.height)]; + [view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; + view.autocorrectionType = UITextAutocorrectionTypeNo; + view.autocapitalizationType = UITextAutocapitalizationTypeNone; + view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin; + view.delegate = weakSelf; + view.placeholder = NSLocalizedString(([NSString stringWithFormat:@"preference.placeholder.%@", key]), nil); + view.returnKeyType = UIReturnKeyDone; + view.text = getPreference(key); + view.textAlignment = NSTextAlignmentRight; + view.adjustsFontSizeToFitWidth = YES; + cell.accessoryView = view; + }; + + self.typePickField = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { + weakSelf.typeTextField(cell, key, item); + UITextField *view = (UITextField *)cell.accessoryView; + UIPickerView *picker = [[UIPickerView alloc] init]; + objc_setAssociatedObject(picker, @"item", item, OBJC_ASSOCIATION_ASSIGN); + objc_setAssociatedObject(picker, @"view", cell.accessoryView, OBJC_ASSOCIATION_ASSIGN); + picker.delegate = weakSelf; + picker.dataSource = weakSelf; + [picker reloadAllComponents]; + [picker selectRow:[item[@"pickKeys"] indexOfObject:getPreference(key)] inComponent:0 animated:NO]; + UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, weakSelf.view.frame.size.width, 44.0)]; + UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; + UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:view action:@selector(resignFirstResponder)]; + toolbar.items = @[flexibleSpace, doneButton]; + view.inputAccessoryView = toolbar; + view.inputView = picker; + }; + + self.typeSlider = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { + DBNumberedSlider *view = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width / 2.1, cell.bounds.size.height)]; + [view addTarget:weakSelf action:@selector(sliderMoved:) forControlEvents:UIControlEventValueChanged]; + view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin; + view.minimumValue = [item[@"min"] intValue]; + view.maximumValue = [item[@"max"] intValue]; + view.continuous = YES; + view.value = [getPreference(key) intValue]; + cell.accessoryView = view; + }; + + self.typeSwitch = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { + UISwitch *view = [[UISwitch alloc] init]; + NSArray *customSwitchValue = item[@"customSwitchValue"]; + if (customSwitchValue == nil) { + [view setOn:[getPreference(key) boolValue] animated:NO]; + } else { + [view setOn:[getPreference(key) isEqualToString:customSwitchValue[1]] animated:NO]; + } + [view addTarget:weakSelf action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; + cell.accessoryView = view; + }; +} + +- (void)showAlertOnView:(UIView *)view title:(NSString *)title message:(NSString *)message { + UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; + alert.popoverPresentationController.sourceView = view; + alert.popoverPresentationController.sourceRect = view.bounds; + UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]; + [alert addAction:ok]; + [self presentViewController:alert animated:YES completion:nil]; +} + +- (void)checkWarn:(UIView *)view { + NSDictionary *item = objc_getAssociatedObject(view, @"item"); + NSString *key = item[@"key"]; + + BOOL(^isWarnable)(UIView *) = item[@"warnCondition"]; + NSString *warnKey = item[@"warnKey"]; + // Display warning if: warn condition is met and either one of these: + // - does not have warnKey, always warn + // - has warnKey and its value is YES, warn once and set it to NO + if (isWarnable && isWarnable(view) && (!warnKey || [getPreference(warnKey) boolValue])) { + if (warnKey) { + setPreference(warnKey, @NO); + } + + NSString *message = NSLocalizedString(([NSString stringWithFormat:@"preference.warn.%@", key]), nil); + [self showAlertOnView:view title:NSLocalizedString(@"Warning", nil) message:message]; + } +} + +#pragma mark - UIPickerView + +- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { + NSDictionary *item = objc_getAssociatedObject(pickerView, @"item"); + UITextField *view = objc_getAssociatedObject(pickerView, @"view"); + + // If there is key, use it + if (item[@"pickKeys"] != nil) { + view.text = item[@"pickKeys"][row]; + } else { + view.text = item[@"pickList"][row]; + } + + // Save the preference + [self textFieldDidEndEditing:view]; +} + +- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView { + return 1; +} + +- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { + NSDictionary *item = objc_getAssociatedObject(pickerView, @"item"); + return [item[@"pickList"] count]; +} + +- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { + NSDictionary *item = objc_getAssociatedObject(pickerView, @"item"); + return item[@"pickList"][row]; +} + +#pragma mark Control event handlers + +- (void)sliderMoved:(DBNumberedSlider *)sender { + [self checkWarn:sender]; + NSString *key = objc_getAssociatedObject(sender, @"key"); + + sender.value = (int)sender.value; + setPreference(key, @(sender.value)); +} + +- (void)switchChanged:(UISwitch *)sender { + [self checkWarn:sender]; + NSDictionary *item = objc_getAssociatedObject(sender, @"item"); + NSString *key = item[@"key"]; + + // Special switches may define custom value instead of NO/YES + NSArray *customSwitchValue = item[@"customSwitchValue"]; + if (customSwitchValue != nil) { + setPreference(key, customSwitchValue[sender.isOn]); + } else { + setPreference(key, @(sender.isOn)); + } + + // Some settings may affect the availability of other settings + // In this case, a switch may request to reload to apply user interaction change + if ([item[@"requestReload"] boolValue]) { + // TODO: only reload needed rows + [self.tableView reloadData]; + } +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [tableView deselectRowAtIndexPath:indexPath animated:NO]; + + NSDictionary *item = self.prefContents[indexPath.section][indexPath.row]; + + if (item[@"type"] == self.typeButton) { + [self tableView:tableView invokeActionWithPromptAtIndexPath:indexPath]; + } else if (item[@"type"] == self.typeChildPane) { + [self tableView:tableView openChildPaneAtIndexPath:indexPath]; + } +} + +#pragma mark External UITableView functions + +- (void)tableView:(UITableView *)tableView openChildPaneAtIndexPath:(NSIndexPath *)indexPath { + NSDictionary *item = self.prefContents[indexPath.section][indexPath.row]; + UIViewController *vc = [[item[@"class"] alloc] init]; + [self.navigationController pushViewController:vc animated:YES]; +} + +- (void)tableView:(UITableView *)tableView invokeActionWithPromptAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath]; + NSDictionary *item = self.prefContents[indexPath.section][indexPath.row]; + NSString *key = item[@"key"]; + + if ([item[@"showConfirmPrompt"] boolValue]) { + BOOL destructive = [item[@"destructive"] boolValue]; + NSString *title = NSLocalizedString(@"preference.title.confirm", nil); + NSString *message = NSLocalizedString(([NSString stringWithFormat:@"preference.title.confirm.%@", key]), nil); + UIAlertController *confirmAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; + confirmAlert.popoverPresentationController.sourceView = view; + confirmAlert.popoverPresentationController.sourceRect = view.bounds; + UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:destructive?UIAlertActionStyleDestructive:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self tableView:tableView invokeActionAtIndexPath:indexPath]; + }]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]; + [confirmAlert addAction:cancel]; + [confirmAlert addAction:ok]; + [self presentViewController:confirmAlert animated:YES completion:nil]; + } else { + [self tableView:tableView invokeActionAtIndexPath:indexPath]; + } +} + +- (void)tableView:(UITableView *)tableView invokeActionAtIndexPath:(NSIndexPath *)indexPath { + NSDictionary *item = self.prefContents[indexPath.section][indexPath.row]; + NSString *key = item[@"key"]; + + void(^invokeAction)(void) = item[@"action"]; + if (invokeAction) { + invokeAction(); + } + + UIView *view = [self.tableView cellForRowAtIndexPath:indexPath]; + NSString *title = NSLocalizedString(([NSString stringWithFormat:@"preference.title.done.%@", key]), nil); + //NSString *message = NSLocalizedString(([NSString stringWithFormat:@"preference.message.done.%@", key]), nil); + [self showAlertOnView:view title:title message:nil]; +} + +#pragma mark UITextField + +- (void)textFieldDidEndEditing:(UITextField *)sender { + [self checkWarn:sender]; + NSString *key = objc_getAssociatedObject(sender, @"key"); + + setPreference(key, sender.text); +} + +@end diff --git a/Natives/LauncherViewController.m b/Natives/LauncherViewController.m index a7eaae2a..4d5fd2de 100644 --- a/Natives/LauncherViewController.m +++ b/Natives/LauncherViewController.m @@ -2,7 +2,6 @@ #import "CustomControlsViewController.h" #import "JavaGUIViewController.h" #import "LauncherPreferences.h" -#import "LauncherPreferencesViewController.h" #import "LauncherViewController.h" #import "MinecraftResourceUtils.h" #import "ios_uikit_bridge.h" @@ -54,8 +53,6 @@ int versionSelectedAt = 0; self.versionList = [[NSMutableArray alloc] init]; - int selectedVersionType = [getPreference(@"selected_version_type") intValue]; - [self reloadVersionList:selectedVersionType]; versionPickerView = [[UIPickerView alloc] init]; versionPickerView.delegate = self; versionPickerView.dataSource = self; @@ -68,7 +65,7 @@ int versionSelectedAt = 0; NSLocalizedString(@"Old-beta", nil), NSLocalizedString(@"Old-alpha", nil) ]]; - versionTypeControl.selectedSegmentIndex = selectedVersionType; + versionTypeControl.selectedSegmentIndex = [getPreference(@"selected_version_type") intValue]; [versionTypeControl addTarget:self action:@selector(changeVersionType:) forControlEvents:UIControlEventValueChanged]; UIBarButtonItem *versionTypeItem = [[UIBarButtonItem alloc] initWithCustomView:versionTypeControl]; UIBarButtonItem *versionFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; @@ -114,6 +111,11 @@ int versionSelectedAt = 0; [scrollView addSubview:self.progressText]; } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self reloadVersionList:[getPreference(@"selected_version_type") intValue]]; +} + - (BOOL)isVersionInstalled:(NSString *)versionId { NSString *localPath = [NSString stringWithFormat:@"%s/versions/%@", getenv("POJAV_GAME_DIR"), versionId]; diff --git a/Natives/LoginViewController.m b/Natives/LoginViewController.m index c88cf80b..bda5d3f5 100644 --- a/Natives/LoginViewController.m +++ b/Natives/LoginViewController.m @@ -21,6 +21,8 @@ #define TYPE_MICROSOFT 1 #define TYPE_OFFLINE 2 +extern NSMutableDictionary *prefDict; + #pragma mark - LoginViewController @interface LoginViewController () { } @@ -36,6 +38,8 @@ [super viewDidLoad]; setViewBackgroundColor(self.view); + setDefaultValueForPref(prefDict, @"control_safe_area", NSStringFromCGRect(getDefaultSafeArea())); + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(msaLoginCallback:) name:@"MSALoginCallback" object:nil]; CGRect screenBounds = [[UIScreen mainScreen] bounds]; diff --git a/Natives/SurfaceViewController.m b/Natives/SurfaceViewController.m index 71752e1e..1ce10197 100644 --- a/Natives/SurfaceViewController.m +++ b/Natives/SurfaceViewController.m @@ -12,6 +12,7 @@ #import "JavaLauncher.h" #import "LauncherPreferences.h" +#import "LauncherPreferencesViewController2.h" #import "MinecraftResourceUtils.h" #import "SurfaceViewController.h" @@ -193,6 +194,7 @@ BOOL slideableHotbar; @property id mouseConnectCallback, mouseDisconnectCallback; @property id controllerConnectCallback, controllerDisconnectCallback; +@property CGFloat mouseSpeed; @property CGRect clickRange; @property BOOL shouldTriggerClick; @@ -219,10 +221,9 @@ BOOL slideableHotbar; CGFloat screenScale = [[UIScreen mainScreen] scale]; resolutionScale = ((NSNumber *)getPreference(@"resolution")).floatValue / 100.0; - slideableHotbar = [getPreference(@"slideable_hotbar") boolValue]; - savedWidth = roundf(screenBounds.size.width * screenScale); - savedHeight = roundf(screenBounds.size.height * screenScale); + savedWidth = roundf(screenBounds.size.width * screenScale * resolutionScale); + savedHeight = roundf(screenBounds.size.height * screenScale * resolutionScale); self.rootView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width + 30.0, self.view.frame.size.height)]; [self.view addSubview:self.rootView]; @@ -249,7 +250,7 @@ BOOL slideableHotbar; [menuSwipeView addSubview:menuSwipeLineView]; [self.rootView addSubview:menuSwipeView]; - self.menuArray = @[@"game.menu.forceClose" /*, @"game.menu.logOutput" */]; + self.menuArray = @[@"game.menu.force_close", @"Settings" /*, @"game.menu.log_output" */]; self.menuView = [[UITableView alloc] initWithFrame:CGRectMake(self.view.frame.size.width + 30.0, 0, self.view.frame.size.width * 0.3 - 36.0 * 0.7, self.view.frame.size.height)]; @@ -272,9 +273,6 @@ self.view.frame.size.width * 0.3 - 36.0 * 0.7, self.view.frame.size.height)]; [self.rootView addSubview:self.surfaceView]; [self.rootView addSubview:self.ctrlView]; - - CGFloat buttonScale = [getPreference(@"button_scale") floatValue] / 100.0; - UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(surfaceOnClick:)]; tapGesture.allowedTouchTypes = @[@(UITouchTypeDirect)]; @@ -304,7 +302,6 @@ self.view.frame.size.width * 0.3 - 36.0 * 0.7, self.view.frame.size.height)]; longpressGesture.allowedTouchTypes = @[@(UITouchTypeDirect)]; longpressGesture.cancelsTouchesInView = NO; longpressGesture.delegate = self; - longpressGesture.minimumPressDuration = [getPreference(@"time_longPressTrigger") floatValue] / 1000; [self.surfaceView addGestureRecognizer:longpressGesture]; self.scrollPanGesture = [[UIPanGestureRecognizer alloc] @@ -349,35 +346,6 @@ self.view.frame.size.width * 0.3 - 36.0 * 0.7, self.view.frame.size.height)]; self.swipeableButtons = [[NSMutableArray alloc] init]; - self.cc_dictionary = parseJSONFromFile(controlFilePath); - if (self.cc_dictionary[@"error"] != nil) { - showDialog(self, @"Error", [NSString stringWithFormat:@"Could not open %@: %@", controlFilePath, [self.cc_dictionary[@"error"] localizedDescription]]); - } else { - CGFloat currentScale = [self.cc_dictionary[@"scaledAt"] floatValue]; - CGFloat savedScale = [getPreference(@"button_scale") floatValue]; - loadControlObject(self.ctrlView, self.cc_dictionary, ^void(ControlButton* button) { - BOOL isSwipeable = [button.properties[@"isSwipeable"] boolValue]; - - button.canBeHidden = YES; - for (int i = 0; i < 4; i++) { - int keycodeInt = [button.properties[@"keycodes"][i] intValue]; - button.canBeHidden &= keycodeInt != SPECIALBTN_TOGGLECTRL; - } - - [button addTarget:self action:@selector(executebtn_down:) forControlEvents:UIControlEventTouchDown]; - [button addTarget:self action:@selector(executebtn_up:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]; - - if (isSwipeable) { - UIPanGestureRecognizer *panRecognizerButton = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(executebtn_swipe:)]; - panRecognizerButton.delegate = self; - [button addGestureRecognizer:panRecognizerButton]; - [self.swipeableButtons addObject:button]; - } - }); - - self.cc_dictionary[@"scaledAt"] = @(savedScale); - } - [KeyboardInput initKeycodeTable]; if (@available(iOS 14.0, tvOS 14.0, *)) { self.mouseConnectCallback = [[NSNotificationCenter defaultCenter] addObserverForName:GCMouseDidConnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { @@ -432,10 +400,41 @@ self.view.frame.size.width * 0.3 - 36.0 * 0.7, self.view.frame.size.height)]; */ // [self setPreferredFramesPerSecond:1000]; + [self updatePreferenceChanges]; [self executebtn_special_togglebtn:0]; [self launchMinecraft]; } +- (void)updatePreferenceChanges { + self.mouseSpeed = [getPreference(@"mouse_speed") floatValue] / 100.0; + slideableHotbar = [getPreference(@"slideable_hotbar") boolValue]; + + virtualMouseEnabled = [getPreference(@"virtmouse_enable") boolValue]; + self.mousePointerView.hidden = isGrabbing || !virtualMouseEnabled; + + CGRect screenBounds = UIScreen.mainScreen.bounds; + CGFloat screenScale = UIScreen.mainScreen.scale; + + // Update virtual mouse scale + CGFloat mouseScale = [getPreference(@"mouse_scale") floatValue] / 100.0; + virtualMouseFrame = CGRectMake(screenBounds.size.width / 2, screenBounds.size.height / 2, 18.0 * mouseScale, 27 * mouseScale); + self.mousePointerView.frame = virtualMouseFrame; + + // May break anytime lol, current: edge, tap, doubleTap, hover, longPress, scrollPan + UILongPressGestureRecognizer *longpressGesture = self.surfaceView.gestureRecognizers[4]; + longpressGesture.minimumPressDuration = [getPreference(@"press_duration") floatValue] / 1000.0; + + self.ctrlView.frame = CGRectFromString(getPreference(@"control_safe_area")); + [self loadCustomControls]; + + // Update resolution + resolutionScale = [getPreference(@"resolution") floatValue] / 100.0; + self.surfaceView.layer.contentsScale = screenScale * resolutionScale; + savedWidth = roundf(screenBounds.size.width * screenScale * resolutionScale); + savedHeight = roundf(screenBounds.size.height * screenScale * resolutionScale); + CallbackBridge_nativeSendScreenSize(savedWidth, savedHeight); +} + - (void)launchMinecraft { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [MinecraftResourceUtils downloadClientJson:getPreference(@"selected_version") progress:nil callback:nil success:^(NSMutableDictionary *json) { @@ -443,22 +442,59 @@ self.view.frame.size.width * 0.3 - 36.0 * 0.7, self.view.frame.size.height)]; launchJVM( BaseAuthenticator.current.authData[@"username"], json, - savedWidth * resolutionScale, savedHeight * resolutionScale, + savedWidth, savedHeight, [json[@"javaVersion"][@"majorVersion"] intValue] ); }]; }); } +- (void)loadCustomControls { + [self.ctrlView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [self.swipeableButtons removeAllObjects]; + [self.cc_dictionary removeAllObjects]; + + NSString *controlFilePath = [NSString stringWithFormat:@"%s/controlmap/%@", getenv("POJAV_HOME"), (NSString *)getPreference(@"default_ctrl")]; + + self.cc_dictionary = parseJSONFromFile(controlFilePath); + if (self.cc_dictionary[@"error"] != nil) { + showDialog(self, NSLocalizedString(@"Error", nil), [NSString stringWithFormat:@"Could not open %@: %@", controlFilePath, [self.cc_dictionary[@"error"] localizedDescription]]); + return; + } + + CGFloat currentScale = [self.cc_dictionary[@"scaledAt"] floatValue]; + CGFloat savedScale = [getPreference(@"button_scale") floatValue]; + loadControlObject(self.ctrlView, self.cc_dictionary, ^void(ControlButton* button) { + BOOL isSwipeable = [button.properties[@"isSwipeable"] boolValue]; + + button.canBeHidden = YES; + for (int i = 0; i < 4; i++) { + int keycodeInt = [button.properties[@"keycodes"][i] intValue]; + button.canBeHidden &= keycodeInt != SPECIALBTN_TOGGLECTRL; + } + + [button addTarget:self action:@selector(executebtn_down:) forControlEvents:UIControlEventTouchDown]; + [button addTarget:self action:@selector(executebtn_up:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]; + + if (isSwipeable) { + UIPanGestureRecognizer *panRecognizerButton = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(executebtn_swipe:)]; + panRecognizerButton.delegate = self; + [button addGestureRecognizer:panRecognizerButton]; + [self.swipeableButtons addObject:button]; + } + }); + + self.cc_dictionary[@"scaledAt"] = @(savedScale); +} + - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures { return UIRectEdgeBottom | UIRectEdgeRight; } - (BOOL)prefersHomeIndicatorAutoHidden { - return NO; + return YES; } - #pragma mark - Menu functions CGPoint lastCenterPoint; @@ -515,7 +551,7 @@ CGPoint lastCenterPoint; - (void)actionForceClose { UIAlertController* alert = [UIAlertController alertControllerWithTitle:nil - message:NSLocalizedString(@"game.menu.confirm.forceClose", nil) + message:NSLocalizedString(@"game.menu.confirm.force_close", nil) preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleDefault handler:nil]; @@ -531,7 +567,12 @@ CGPoint lastCenterPoint; }]; [alert addAction:okAction]; - [currentVC() presentViewController:alert animated:YES completion:nil]; + [self presentViewController:alert animated:YES completion:nil]; +} + +- (void)actionOpenPreferences { + LauncherPreferencesViewController2 *vc = [[LauncherPreferencesViewController2 alloc] init]; + [self presentViewController:vc animated:YES completion:nil]; } - (void)actionToggleLogOutput { @@ -567,6 +608,9 @@ CGPoint lastCenterPoint; [self actionForceClose]; break; case 1: + [self actionOpenPreferences]; + break; + case 2: [self actionToggleLogOutput]; break; } @@ -587,12 +631,12 @@ CGPoint lastCenterPoint; screenScale *= resolutionScale; if (virtualMouseEnabled) { if (event == ACTION_MOVE) { - virtualMouseFrame.origin.x += location.x - lastVirtualMousePoint.x; - virtualMouseFrame.origin.y += location.y - lastVirtualMousePoint.y; + virtualMouseFrame.origin.x += (location.x - lastVirtualMousePoint.x) * self.mouseSpeed; + virtualMouseFrame.origin.y += (location.y - lastVirtualMousePoint.y) * self.mouseSpeed; } else if (event == ACTION_MOVE_MOTION) { event = ACTION_MOVE; - virtualMouseFrame.origin.x += location.x; - virtualMouseFrame.origin.y += location.y; + virtualMouseFrame.origin.x += location.x * self.mouseSpeed; + virtualMouseFrame.origin.y += location.y * self.mouseSpeed; } virtualMouseFrame.origin.x = clamp(virtualMouseFrame.origin.x, 0, self.view.frame.size.width); virtualMouseFrame.origin.y = clamp(virtualMouseFrame.origin.y, 0, self.view.frame.size.height); diff --git a/Natives/egl_bridge.m b/Natives/egl_bridge.m index c2699e6e..48c7e4bf 100644 --- a/Natives/egl_bridge.m +++ b/Natives/egl_bridge.m @@ -91,15 +91,14 @@ pid_t gettid() { return (pid_t) pthread_mach_thread_np(pthread_self()); } - -JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_utils_JREUtils_setenv(JNIEnv *env, jclass clazz, jstring name, jstring value, jboolean overwrite) { - char const *name_c = (*env)->GetStringUTFChars(env, name, NULL); - char const *value_c = (*env)->GetStringUTFChars(env, value, NULL); - - setenv(name_c, value_c, overwrite); - - (*env)->ReleaseStringUTFChars(env, name, name_c); - (*env)->ReleaseStringUTFChars(env, value, value_c); +void JNI_LWJGL_changeRenderer(const char* value_c) { + JNIEnv *env; + (*runtimeJavaVMPtr)->GetEnv(runtimeJavaVMPtr, (void **)&env, JNI_VERSION_1_4); + jstring key = (*env)->NewStringUTF(env, "org.lwjgl.opengl.libname"); + jstring value = (*env)->NewStringUTF(env, value_c); + jclass clazz = (*env)->FindClass(env, "java/lang/System"); + jmethodID method = (*env)->GetStaticMethodID(env, clazz, "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + (*env)->CallStaticObjectMethod(env, clazz, method, key, value); } void pojavTerminate() { @@ -225,11 +224,17 @@ jboolean pojavInit_OpenGL() { if (config_renderer) return JNI_TRUE; NSString *renderer = @(getenv("POJAV_RENDERER")); + JNI_LWJGL_changeRenderer(renderer.UTF8String); + BOOL isAuto = [renderer isEqualToString:@"auto"]; if ([renderer isEqualToString:@"libOSMesa.8.dylib"]) { config_renderer = RENDERER_VIRGL; setenv("GALLIUM_DRIVER", "virpipe", 1); loadSymbolsVirGL(); - } else if ([renderer hasPrefix:@"libgl4es"] || [renderer hasPrefix:@"libtinygl4angle"]) { + } else if (isAuto || [renderer isEqualToString:@ RENDERER_NAME_GL4ES]) { + config_renderer = RENDERER_MTL_ANGLE; + setenv("POJAV_RENDERER", RENDERER_NAME_GL4ES, 1); + loadSymbols(); + } else if ([renderer isEqualToString:@ RENDERER_NAME_MTL_ANGLE]) { config_renderer = RENDERER_MTL_ANGLE; loadSymbols(); } else if ([renderer hasPrefix:@"libOSMesa"]) { @@ -248,9 +253,17 @@ jboolean pojavInit_OpenGL() { } // Pre-load the renderer library + // FIXME: gl4es has to be pre-loaded if it is going to be used later, + // or glCheckFramebufferStatus will return an error void *renderer_handle = dlopen(getenv("POJAV_RENDERER"), RTLD_GLOBAL); debugLog("%s=%p, error=%s", getenv("POJAV_RENDERER"), renderer_handle, dlerror()); + // Set back to the previous value + setenv("POJAV_RENDERER", renderer.UTF8String, 1); + if (isAuto) { + JNI_LWJGL_changeRenderer(RENDERER_NAME_GL4ES); + } + debugLog("EGLBridge: Initializing"); // printf("EGLBridge: ANativeWindow pointer = %p\n", potatoBridge.androidWindow); //(*env)->ThrowNew(env,(*env)->FindClass(env,"java/lang/Exception"),"Trace exception"); @@ -293,8 +306,8 @@ jboolean pojavInit_OpenGL() { eglBindAPI_p(EGL_OPENGL_ES_API); } - potatoBridge.eglSurface = eglCreateWindowSurface_p(potatoBridge.eglDisplay, config, (__bridge EGLNativeWindowType) ((SurfaceViewController *)currentVC()).surfaceView.layer, NULL); - //NSLog(@"Layer %@", ((SurfaceViewController *)currentVC()).surfaceView.layer); + potatoBridge.eglSurface = eglCreateWindowSurface_p(potatoBridge.eglDisplay, config, (__bridge EGLNativeWindowType) ((SurfaceViewController *)currentWindow().rootViewController).surfaceView.layer, NULL); + //NSLog(@"Layer %@", ((SurfaceViewController *)currentWindow().rootViewController).surfaceView.layer); if (!potatoBridge.eglSurface) { debugLog("EGLBridge: Error eglCreateWindowSurface failed: 0x%x", eglGetError_p()); @@ -351,19 +364,26 @@ jboolean pojavInit_OpenGL() { } void pojavSetWindowHint(int hint, int value) { - if (hint != GLFW_CLIENT_API) return; - switch (value) { - case GLFW_NO_API: - config_renderer = RENDERER_VULKAN; - /* Nothing to do: initialization is handled in Java-side */ - // pojavInit_Vulkan(); - break; - case GLFW_OPENGL_API: - pojavInit_OpenGL(); - break; - default: - NSLog(@"GLFW: Unimplemented API 0x%x", value); - abort(); + if (hint == GLFW_CLIENT_API) { + switch (value) { + case GLFW_NO_API: + config_renderer = RENDERER_VULKAN; + /* Nothing to do: initialization is handled in Java-side */ + // pojavInit_Vulkan(); + break; + case GLFW_OPENGL_API: + pojavInit_OpenGL(); + break; + default: + NSLog(@"GLFW: Unimplemented API 0x%x", value); + abort(); + } + } else if (strcmp(getenv("POJAV_RENDERER"), "auto")==0 && hint == GLFW_CONTEXT_VERSION_MAJOR && value >= 3) { + // Free unused gl4es library + void *renderer_handle = dlopen(RENDERER_NAME_GL4ES, RTLD_GLOBAL); + dlclose(renderer_handle); + + JNI_LWJGL_changeRenderer(RENDERER_NAME_MTL_ANGLE); } } @@ -392,7 +412,7 @@ void pojavSwapBuffers() { case RENDERER_VK_ZINK: { glFinish_p(); dispatch_async(dispatch_get_main_queue(), ^{ - [((SurfaceViewController *)currentVC()).surfaceView displayLayer]; + [((SurfaceViewController *)currentWindow().rootViewController).surfaceView displayLayer]; }); } break; } @@ -472,7 +492,7 @@ void pojavMakeCurrent(void* window) { void* pojavCreateContext(void* contextSrc) { if (config_renderer == RENDERER_VULKAN) { - return (__bridge void *)(((SurfaceViewController *)currentVC()).surfaceView.layer); + return (__bridge void *)(((SurfaceViewController *)currentWindow().rootViewController).surfaceView.layer); } if (config_renderer == RENDERER_MTL_ANGLE) { diff --git a/Natives/en.lproj/Localizable.strings b/Natives/en.lproj/Localizable.strings index 7f7181ed..65ae8940 100644 --- a/Natives/en.lproj/Localizable.strings +++ b/Natives/en.lproj/Localizable.strings @@ -3,10 +3,10 @@ StringLocalization */ -"game.menu.forceClose" = "Force close"; -"game.menu.confirm.forceClose" = "Are you sure you want to force close?"; -"game.menu.logOutput" = "Log output"; -"game.menu.customControls" = "Custom controls"; +"game.menu.force_close" = "Force close"; +"game.menu.confirm.force_close" = "Are you sure you want to force close?"; +"game.menu.log_output" = "Log output"; +"game.menu.custom_controls" = "Custom controls"; "Installed" = "Installed"; "Snapshot" = "Snapshot"; @@ -64,3 +64,74 @@ "login.msa.progress.acquireXboxProfile" = "(3/5) Acquiring the Xbox profile"; "login.msa.progress.acquireMCToken" = "(4/5) Acquiring the Minecraft token"; "login.msa.progress.checkMCProfile" = "(5/5) Checking the Minecraft profile"; + +"Settings" = "Settings"; + +"preference.section.general" = "General Settings"; +"preference.section.footer.general" = "* Compatibility symlink option is planned to be removed in a future release."; +"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.footer.control" = ""; +"preference.section.java" = "Java Tweaks"; +//"preference.section.footer.java" = ""; + +"preference.title.confirm" = "Are you sure?"; + +"preference.title.game_directory" = "Minecraft game directory"; +"preference.title.confirm.delete_game_directory" = "The instance %@ will be deleted forever and cannot be restored."; +"preference.multidir.add_directory" = "add game directory"; + +"preference.title.home_symlink" = "Enable compatibility symlink *"; +"preference.detail.home_symlink" = "Make a symlink at /var/mobile/Documents/.pojavlauncher. Unavailable in sandboxed environment."; +"preference.title.check_sha" = "Check files after downloading"; +"preference.detail.check_sha" = "This option forces launcher to check the file hash if it's available. Prevents broken downloads."; +"preference.title.cosmetica" = "Cosmetica Capes"; +"preference.detail.cosmetica" = "Enable capes from Cosmetica (previously Arc). For more information please visit https://cosmetica.cc. Requires OptiFine"; +"preference.title.debug_logging" = "Debug logging"; +"preference.detail.debug_logging" = "Logs internal settings and actions to latestlog.txt. This helps the developers find issues easier, but Minecraft may run slower as the logs will be written to more often."; + +"preference.title.reset_warnings" = "Reset all warnings"; +"preference.title.done.reset_warnings" = "All warnings have been reset."; + +"preference.title.reset_settings" = "Reset all settings"; +"preference.title.confirm.reset_settings" = "This will remove all of your custom preferences, including resolution, button size, and Java arguments."; +"preference.title.done.reset_settings" = "All of the default settings have been restored."; + +"preference.title.renderer" = "Renderer"; +"preference.title.renderer.auto" = "Auto: gl4es or tinygl4angle"; +"preference.title.renderer.gl4es" = "gl4es 1.1.4 - exports OpenGL 2.1"; +"preference.title.renderer.tinygl4angle" = "tinygl4angle (1.17+) - exports OpenGL 3.2 (Core Profile, limited)"; +"preference.title.renderer.zink" = "Zink (Mesa 21.0) - exports OpenGL 4.1"; +"preference.detail.renderer" = "Change the OpenGL render engine to use"; + +"preference.title.resolution" = "Resolution (%)"; +"preference.detail.resolution" = "Allows you to decrease the game resolution."; +"preference.title.disable_gesture" = "Disable gestures"; +"preference.title.press_duration" = "Long-press duration (ms)"; +"preference.detail.press_duration" = "Change trigger time for long press in destroy block and drop item."; +"preference.title.button_scale" = "On-screen button scale (%)"; +"preference.detail.button_scale" = "Upscale them if buttons are too small, usually needed for iPad users."; +"preference.title.mouse_scale" = "Mouse scaling (%)"; +"preference.detail.mouse_scale" = "Change the size of the virtual mouse"; +"preference.title.mouse_speed" = "Mouse speed (%)"; +"preference.detail.mouse_speed" = "Change the speed of the virtual mouse"; +"preference.title.virtmouse_enable" = "Virtual mouse"; +"preference.detail.virtmouse_enable" = "Enable this option to start with virtual mouse on"; +"preference.title.slideable_hotbar" = "Slideable hotbar"; +"preference.detail.slideable_hotbar" = "Make the hotbar slideable like the mouse wheel"; +"preference.title.java_home" = "Use Java 17 for Minecraft < 1.17"; +"preference.detail.java_home" = "Java 8 is used by default for those versions. Enable this option to force use Java 17, however compatibility with modded is not guaranteed."; +"preference.title.java_args" = "JVM Launch arguments"; +"preference.detail.java_args" = "Be careful, this can make the game crash if modified without any prior knowledge."; +"preference.placeholder.java_args" = "Specify arguments..."; +"preference.title.auto_ram" = "Auto RAM"; +"preference.detail.auto_ram" = "Enables automatic RAM adjuster"; +"preference.warn.auto_ram" = "Disabling this option on an unjailbroken device can create instability if altered. Proceed with caution."; +"preference.title.allocated_memory" = "Memory allocation (MB)"; +"preference.detail.allocated_memory" = "Controls how much memory is given to Minecraft"; +"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."; +// sha check +// home symlink diff --git a/Natives/external/DBNumberedSlider b/Natives/external/DBNumberedSlider index bc6ea603..4eddc68b 160000 --- a/Natives/external/DBNumberedSlider +++ b/Natives/external/DBNumberedSlider @@ -1 +1 @@ -Subproject commit bc6ea60353efd2e741de81f84aa691441f2d0ca9 +Subproject commit 4eddc68b5f92247f8afc0f6114d1b92b338749f4 diff --git a/Natives/external/TOInsetGroupedTableView b/Natives/external/TOInsetGroupedTableView new file mode 160000 index 00000000..a17c55ac --- /dev/null +++ b/Natives/external/TOInsetGroupedTableView @@ -0,0 +1 @@ +Subproject commit a17c55acd5faabd7114fb26e908cf1e07b570b1c diff --git a/Natives/glfw_keycodes.h b/Natives/glfw_keycodes.h index ef1151fc..6e88a875 100644 --- a/Natives/glfw_keycodes.h +++ b/Natives/glfw_keycodes.h @@ -35,6 +35,8 @@ #define GLFW_NO_API 0 #define GLFW_OPENGL_API 0x30001 +#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 + /*! @defgroup hat_state Joystick hat states * @brief Joystick hat states. * diff --git a/Natives/input/ControllerInput.m b/Natives/input/ControllerInput.m index b4728bb4..6bc4ca4e 100644 --- a/Natives/input/ControllerInput.m +++ b/Natives/input/ControllerInput.m @@ -169,7 +169,7 @@ BOOL leftShiftHeld; return; } - UIImageView *pointerView = [(id)currentVC() mousePointerView]; + UIImageView *pointerView = [(id)currentWindow().rootViewController mousePointerView]; virtualMouseFrame.origin.x += xValue; virtualMouseFrame.origin.y += yValue; pointerView.frame = virtualMouseFrame; diff --git a/Natives/input_bridge_v3.m b/Natives/input_bridge_v3.m index d6dc7244..6b8f8264 100644 --- a/Natives/input_bridge_v3.m +++ b/Natives/input_bridge_v3.m @@ -270,16 +270,16 @@ JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetGrabbing(JNIE } dispatch_async(dispatch_get_main_queue(), ^{ - UIView *surfaceView = ((SurfaceViewController *)currentVC()).surfaceView; + UIView *surfaceView = ((SurfaceViewController *)currentWindow().rootViewController).surfaceView; if (isGrabbing == JNI_TRUE) { CGFloat screenScale = [[UIScreen mainScreen] scale] * resolutionScale; callback_SurfaceViewController_onTouch(ACTION_DOWN, lastVirtualMousePoint.x * screenScale, lastVirtualMousePoint.y * screenScale); - ((SurfaceViewController *)currentVC()).mousePointerView.frame = virtualMouseFrame; + ((SurfaceViewController *)currentWindow().rootViewController).mousePointerView.frame = virtualMouseFrame; } - ((SurfaceViewController *)currentVC()).scrollPanGesture.enabled = !isGrabbing; - ((SurfaceViewController *)currentVC()).mousePointerView.hidden = isGrabbing || !virtualMouseEnabled; + ((SurfaceViewController *)currentWindow().rootViewController).scrollPanGesture.enabled = !isGrabbing; + ((SurfaceViewController *)currentWindow().rootViewController).mousePointerView.hidden = isGrabbing || !virtualMouseEnabled; if(@available(iOS 14.0, *)) { - [(SurfaceViewController *)currentVC() setNeedsUpdateOfPrefersPointerLocked]; + [(SurfaceViewController *)currentWindow().rootViewController setNeedsUpdateOfPrefersPointerLocked]; } }); } diff --git a/Natives/ios_uikit_bridge.m b/Natives/ios_uikit_bridge.m index 0975e9ed..68f2dec5 100644 --- a/Natives/ios_uikit_bridge.m +++ b/Natives/ios_uikit_bridge.m @@ -1,6 +1,5 @@ #import "authenticator/BaseAuthenticator.h" #import "AppDelegate.h" -#import "BKSSystemService.h" #import "SceneDelegate.h" #import "LoginViewController.h" #import "LauncherPreferences.h" @@ -104,37 +103,24 @@ jstring UIKit_accessClipboard(JNIEnv* env, jint action, jstring copySrc) { } void UIKit_launchMinecraftSurfaceVC() { - setPreference(@"internal_launch_on_boot", getPreference(@"restart_before_launch")); + // Leave this pref, might be useful later for launching with Quick Actions/Shortcuts/URL Scheme + //setPreference(@"internal_launch_on_boot", getPreference(@"restart_before_launch")); setPreference(@"internal_selected_account", BaseAuthenticator.current.authData[@"username"]); setPreference(@"internal_useStackQueue", @(isUseStackQueueCall ? YES : NO)); dispatch_async(dispatch_get_main_queue(), ^{ - if ([getPreference(@"restart_before_launch") boolValue]) { - NSURL *url = [NSURL URLWithString:@"pojavlauncher://"]; - BKSSystemService *service = [[NSClassFromString(@"BKSSystemService") alloc] init]; - unsigned int port = [service createClientPort]; - [service openURL:url application:@"net.kdt.pojavlauncher" options:nil clientPort:port withResult:nil]; - - // exiting too fast will cause it to fail (race condition?) - // [FBSystemService][0xbb88] Caller "PojavLauncher:pid" has a sandbox that does not allow opening URL's. - // The request was denied by service delegate (SBMainWorkspace) for reason: Security ("Sandbox check failed for process (PojavLauncher:pid) openURL not allowed"). - // therefore, sleep for 1ms before exit - usleep(1000); - exit(0); - } else { - 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.alpha = 0; - } completion:^(BOOL b){ - [window resignKeyWindow]; - window.alpha = 1; - window.rootViewController = [[SurfaceViewController alloc] init]; - [window makeKeyAndVisible]; - }]; + 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.alpha = 0; + } completion:^(BOOL b){ + [window resignKeyWindow]; + window.alpha = 1; + window.rootViewController = [[SurfaceViewController alloc] init]; + [window makeKeyAndVisible]; + }]; }); } diff --git a/Natives/main.m b/Natives/main.m index c23e3515..4be355f0 100644 --- a/Natives/main.m +++ b/Natives/main.m @@ -7,7 +7,6 @@ #import "AppDelegate.h" #import "customcontrols/CustomControlsUtils.h" #import "LauncherPreferences.h" -#import "LauncherPreferencesViewController.h" #include #include @@ -321,7 +320,7 @@ int main(int argc, char * argv[]) { init_hookFunctions(); - loadPreferences(); + loadPreferences(NO); init_setupResolvConf(); init_setupMultiDir(); init_setupLauncherProfiles(); diff --git a/Natives/resources/Assets.car b/Natives/resources/Assets.car index bd4d3515..e6891c76 100644 Binary files a/Natives/resources/Assets.car and b/Natives/resources/Assets.car differ diff --git a/Natives/utils.h b/Natives/utils.h index 4d4ee4ca..2266300b 100644 --- a/Natives/utils.h +++ b/Natives/utils.h @@ -28,6 +28,10 @@ #define GLFW_FOCUSED 0x00020001 #define GLFW_VISIBLE 0x00020004 +#define RENDERER_NAME_GL4ES "libgl4es_114.dylib" +#define RENDERER_NAME_MTL_ANGLE "libtinygl4angle.dylib" +#define RENDERER_NAME_VK_ZINK "libOSMesa.8.dylib" + #define SPECIALBTN_KEYBOARD -1 #define SPECIALBTN_TOGGLECTRL -2 #define SPECIALBTN_MOUSEPRI -3 @@ -59,6 +63,7 @@ BOOL isControlModifiable; void init_hookFunctions(); void init_setupMultiDir(); +UIWindow* currentWindow(); UIViewController* currentVC(); NSMutableDictionary* parseJSONFromFile(NSString *path); diff --git a/Natives/utils.m b/Natives/utils.m index b83df79a..20097f6e 100644 --- a/Natives/utils.m +++ b/Natives/utils.m @@ -43,12 +43,16 @@ BOOL getEntitlementValue(NSString *key); } @end -UIViewController* currentVC() { +UIWindow* currentWindow() { id delegate = UIApplication.sharedApplication.delegate; if (@available(iOS 13.0, *)) { delegate = UIApplication.sharedApplication.connectedScenes.anyObject.delegate; } - return [delegate window].visibleViewController; + return [delegate window]; +} + +UIViewController* currentVC() { + return currentWindow().visibleViewController; } BOOL isJITEnabled() { diff --git a/README.md b/README.md index 09c0987b..97d29076 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ PojavLauncher is amazing, and surprisingly stable, and it wouldn't be this way w - [fishhook](https://github.com/facebook/fishhook) (jailed environment usage only): [BSD-3 License](https://github.com/facebook/fishhook/blob/main/LICENSE). - [Java Native Access](https://github.com/java-native-access/jna): [Apache 2.0 License](https://github.com/java-native-access/jna/blob/master/LICENSE). - [shaderc](https://github.com/google/shaderc) (used by mods that uses Vulkan for rendering): [Apache 2.0 License](https://github.com/google/shaderc/blob/main/LICENSE). +- [TOInsetGroupedTableView](https://github.com/TimOliver/TOInsetGroupedTableView): [MIT License](https://github.com/TimOliver/TOInsetGroupedTableView/blob/master/LICENSE). ## Special thanks to MacStadium! This project is listed under the MacStadium Open Source Program, which allows all of us developers to keep on moving forward even without physical access to a Mac.