Welcome to the new Settings UI

What's new?
- Settings are now categorized
- Re-designed Game Directory manager
- Moved help detail to subtitles, toggleable
- Some settings are now made accessible ingame
- Added Auto Renderer

Removed settings:
- Restart before launching: not needed anymore, also it would break JIT/debugger.
This commit is contained in:
Duy Tran Khanh authored and GitHub committed 2022-09-09 19:08:27 +07:00
commit e372df28dc
30 files changed
+1137 -1200

No files matched your search

+4 -1
View File
@@ -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
@@ -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]);
@@ -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");
-9
View File
@@ -1,9 +0,0 @@
#import <Foundation/Foundation.h>
@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
+4 -1
View File
@@ -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
+12 -20
View File
@@ -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];
+4 -4
View File
@@ -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)];
}
@@ -0,0 +1,6 @@
#import <UIKit/UIKit.h>
@interface LauncherPrefGameDirViewController : UITableViewController
@end
+238
View File
@@ -0,0 +1,238 @@
#import "LauncherPreferences.h"
#import "LauncherPrefGameDirViewController.h"
#import "TOInsetGroupedTableView.h"
#import "ios_uikit_bridge.h"
@interface LauncherPrefGameDirViewController ()<UITextFieldDelegate>
@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<UIMenuElement *> * _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
+2 -1
View File
@@ -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);
+31 -18
View File
@@ -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;
File diff suppressed because it is too large. Load diff
@@ -5,6 +5,6 @@
#define JRE17_NAME_JB @"Java 17"
#define JRE8_NAME_SB @"Java 8 (sandbox)"
@interface LauncherPreferencesViewController : UIViewController <UITextFieldDelegate>
@interface LauncherPreferencesViewController2 : UITableViewController<UITextFieldDelegate>
@end
@@ -0,0 +1,573 @@
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#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()<UIPickerViewDataSource, UIPickerViewDelegate> {}
@property NSArray<NSString*>* prefSections;
@property NSArray<NSArray<NSDictionary*>*>* 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
+6 -4
View File
@@ -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];
+4
View File
@@ -21,6 +21,8 @@
#define TYPE_MICROSOFT 1
#define TYPE_OFFLINE 2
extern NSMutableDictionary *prefDict;
#pragma mark - LoginViewController
@interface LoginViewController () <ASWebAuthenticationPresentationContextProviding>{
}
@@ -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];
+90 -46
View File
@@ -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);
+47 -27
View File
@@ -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) {
+75 -4
View File
@@ -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
+2
View File
@@ -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.
*
+1 -1
View File
@@ -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;
+5 -5
View File
@@ -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];
}
});
}
+14 -28
View File
@@ -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];
}];
});
}
+1 -2
View File
@@ -7,7 +7,6 @@
#import "AppDelegate.h"
#import "customcontrols/CustomControlsUtils.h"
#import "LauncherPreferences.h"
#import "LauncherPreferencesViewController.h"
#include <libgen.h>
#include <pthread.h>
@@ -321,7 +320,7 @@ int main(int argc, char * argv[]) {
init_hookFunctions();
loadPreferences();
loadPreferences(NO);
init_setupResolvConf();
init_setupMultiDir();
init_setupLauncherProfiles();
Binary file not shown.
+5
View File
@@ -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);
+6 -2
View File
@@ -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() {
+1
View File
@@ -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.