444 lines
17 KiB
Objective-C
444 lines
17 KiB
Objective-C
#import <Foundation/Foundation.h>
|
|
|
|
#import "DBNumberedSlider.h"
|
|
#import "HostManagerBridge.h"
|
|
#import "LauncherNavigationController.h"
|
|
#import "LauncherMenuViewController.h"
|
|
#import "LauncherPreferences.h"
|
|
#import "LauncherPreferencesViewController.h"
|
|
#import "LauncherPrefContCfgViewController.h"
|
|
#import "LauncherPrefManageJREViewController.h"
|
|
#import "UIKit+hook.h"
|
|
|
|
#import "config.h"
|
|
#import "ios_uikit_bridge.h"
|
|
#import "utils.h"
|
|
|
|
@interface LauncherPreferencesViewController()
|
|
@property(nonatomic) NSArray<NSString*> *rendererKeys, *rendererList;
|
|
@end
|
|
|
|
@implementation LauncherPreferencesViewController
|
|
|
|
- (id)init {
|
|
self = [super init];
|
|
self.title = localize(@"Settings", nil);
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)imageName {
|
|
return @"MenuSettings";
|
|
}
|
|
|
|
- (void)viewDidLoad
|
|
{
|
|
self.getPreference = ^id(NSString *section, NSString *key){
|
|
NSString *keyFull = [NSString stringWithFormat:@"%@.%@", section, key];
|
|
return getPrefObject(keyFull);
|
|
};
|
|
self.setPreference = ^(NSString *section, NSString *key, id value){
|
|
NSString *keyFull = [NSString stringWithFormat:@"%@.%@", section, key];
|
|
setPrefObject(keyFull, value);
|
|
};
|
|
|
|
self.hasDetail = YES;
|
|
self.prefDetailVisible = self.navigationController == nil;
|
|
|
|
self.prefSections = @[@"general", @"video", @"control", @"java", @"debug"];
|
|
|
|
self.rendererKeys = getRendererKeys(NO);
|
|
self.rendererList = getRendererNames(NO);
|
|
|
|
BOOL(^whenNotInGame)() = ^BOOL(){
|
|
return self.navigationController != nil;
|
|
};
|
|
self.prefContents = @[
|
|
@[
|
|
// General settings
|
|
@{@"icon": @"cube"},
|
|
@{@"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,
|
|
@"action": ^(BOOL enabled){
|
|
debugLogEnabled = enabled;
|
|
NSLog(@"[Debugging] Debug log enabled: %@", enabled ? @"YES" : @"NO");
|
|
}
|
|
},
|
|
@{@"key": @"appicon",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"paintbrush",
|
|
@"type": self.typePickField,
|
|
@"enableCondition": ^BOOL(){
|
|
return UIApplication.sharedApplication.supportsAlternateIcons;
|
|
},
|
|
@"action": ^void(NSString *iconName) {
|
|
if ([iconName isEqualToString:@"AppIcon-Light"]) {
|
|
iconName = nil;
|
|
}
|
|
[UIApplication.sharedApplication setAlternateIconName:iconName completionHandler:^(NSError * _Nullable error) {
|
|
if (error == nil) return;
|
|
NSLog(@"Error in appicon: %@", error);
|
|
showDialog(localize(@"Error", nil), error.localizedDescription);
|
|
}];
|
|
},
|
|
@"pickKeys": @[
|
|
@"AppIcon-Light",
|
|
@"AppIcon-Dark",
|
|
@"AppIcon-Development"
|
|
],
|
|
@"pickList": @[
|
|
localize(@"preference.title.appicon-default", nil),
|
|
localize(@"preference.title.appicon-dark", nil),
|
|
localize(@"preference.title.appicon-development", nil)
|
|
]
|
|
},
|
|
@{@"key": @"hidden_sidebar",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"sidebar.leading",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": whenNotInGame
|
|
},
|
|
@{@"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];
|
|
}
|
|
},
|
|
@{@"key": @"erase_demo_data",
|
|
@"icon": @"trash",
|
|
@"type": self.typeButton,
|
|
@"enableCondition": ^BOOL(){
|
|
NSString *demoPath = [NSString stringWithFormat:@"%s/.demo", getenv("POJAV_HOME")];
|
|
int count = [NSFileManager.defaultManager contentsOfDirectoryAtPath:demoPath error:nil].count;
|
|
return whenNotInGame() && count > 0;
|
|
},
|
|
@"showConfirmPrompt": @YES,
|
|
@"destructive": @YES,
|
|
@"action": ^void(){
|
|
NSString *demoPath = [NSString stringWithFormat:@"%s/.demo", getenv("POJAV_HOME")];
|
|
NSError *error;
|
|
if([NSFileManager.defaultManager removeItemAtPath:demoPath error:&error]) {
|
|
[NSFileManager.defaultManager createDirectoryAtPath:demoPath
|
|
withIntermediateDirectories:YES attributes:nil error:nil];
|
|
[NSFileManager.defaultManager changeCurrentDirectoryPath:demoPath];
|
|
if (getenv("DEMO_LOCK")) {
|
|
[(LauncherNavigationController *)self.navigationController fetchLocalVersionList];
|
|
}
|
|
} else {
|
|
NSLog(@"Error in erase_demo_data: %@", error);
|
|
showDialog(localize(@"Error", nil), error.localizedDescription);
|
|
}
|
|
}
|
|
}
|
|
], @[
|
|
// Video and renderer settings
|
|
@{@"icon": @"video"},
|
|
@{@"key": @"renderer",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"cpu",
|
|
@"type": self.typePickField,
|
|
@"enableCondition": whenNotInGame,
|
|
@"pickKeys": self.rendererKeys,
|
|
@"pickList": self.rendererList
|
|
},
|
|
@{@"key": @"resolution",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"viewfinder",
|
|
@"type": self.typeSlider,
|
|
@"min": @(25),
|
|
@"max": @(150)
|
|
},
|
|
@{@"key": @"max_framerate",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"timelapse",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": ^BOOL(){
|
|
return whenNotInGame() && (UIScreen.mainScreen.maximumFramesPerSecond > 60);
|
|
}
|
|
},
|
|
@{@"key": @"performance_hud",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"waveform.path.ecg",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": ^BOOL(){
|
|
return [CAMetalLayer instancesRespondToSelector:@selector(developerHUDProperties)];
|
|
}
|
|
},
|
|
@{@"key": @"fullscreen_airplay",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"airplayvideo",
|
|
@"type": self.typeSwitch,
|
|
@"action": ^(BOOL enabled){
|
|
if (self.navigationController != nil) return;
|
|
if (UIApplication.sharedApplication.connectedScenes.count < 2) return;
|
|
if (enabled) {
|
|
[self.presentingViewController performSelector:@selector(switchToExternalDisplay)];
|
|
} else {
|
|
[self.presentingViewController performSelector:@selector(switchToInternalDisplay)];
|
|
}
|
|
}
|
|
},
|
|
@{@"key": @"silence_other_audio",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"speaker.slash",
|
|
@"type": self.typeSwitch
|
|
},
|
|
@{@"key": @"silence_with_switch",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"speaker.zzz",
|
|
@"type": self.typeSwitch
|
|
},
|
|
@{@"key": @"allow_microphone",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"mic",
|
|
@"type": self.typeSwitch
|
|
},
|
|
], @[
|
|
// Control settings
|
|
@{@"icon": @"gamecontroller"},
|
|
@{@"key": @"default_gamepad_ctrl",
|
|
@"icon": @"hammer",
|
|
@"type": self.typeChildPane,
|
|
@"enableCondition": whenNotInGame,
|
|
@"canDismissWithSwipe": @NO,
|
|
@"class": LauncherPrefContCfgViewController.class
|
|
},
|
|
@{@"key": @"hardware_hide",
|
|
@"icon": @"eye.slash",
|
|
@"hasDetail": @YES,
|
|
@"type": self.typeSwitch,
|
|
},
|
|
@{@"key": @"recording_hide",
|
|
@"icon": @"eye.slash",
|
|
@"hasDetail": @YES,
|
|
@"type": self.typeSwitch,
|
|
},
|
|
@{@"key": @"gesture_mouse",
|
|
@"icon": @"cursorarrow.click",
|
|
@"hasDetail": @YES,
|
|
@"type": self.typeSwitch,
|
|
},
|
|
@{@"key": @"gesture_hotbar",
|
|
@"icon": @"hand.tap",
|
|
@"hasDetail": @YES,
|
|
@"type": self.typeSwitch,
|
|
},
|
|
@{@"key": @"disable_haptics",
|
|
@"icon": @"wave.3.left",
|
|
@"hasDetail": @NO,
|
|
@"type": self.typeSwitch,
|
|
},
|
|
@{@"key": @"slideable_hotbar",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"slider.horizontal.below.rectangle",
|
|
@"type": self.typeSwitch
|
|
},
|
|
@{@"key": @"press_duration",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"cursorarrow.click.badge.clock",
|
|
@"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": @"cursorarrow.motionlines",
|
|
@"type": self.typeSlider,
|
|
@"min": @(25),
|
|
@"max": @(300)
|
|
},
|
|
@{@"key": @"virtmouse_enable",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"cursorarrow.rays",
|
|
@"type": self.typeSwitch
|
|
},
|
|
@{@"key": @"gyroscope_enable",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"gyroscope",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": ^BOOL(){
|
|
return realUIIdiom != UIUserInterfaceIdiomTV;
|
|
}
|
|
},
|
|
@{@"key": @"gyroscope_invert_x_axis",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"arrow.left.and.right",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": ^BOOL(){
|
|
return realUIIdiom != UIUserInterfaceIdiomTV;
|
|
}
|
|
},
|
|
@{@"key": @"gyroscope_sensitivity",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"move.3d",
|
|
@"type": self.typeSlider,
|
|
@"min": @(50),
|
|
@"max": @(300),
|
|
@"enableCondition": ^BOOL(){
|
|
return realUIIdiom != UIUserInterfaceIdiomTV;
|
|
}
|
|
}
|
|
], @[
|
|
// Java tweaks
|
|
@{@"icon": @"sparkles"},
|
|
@{@"key": @"manage_runtime",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"cube",
|
|
@"type": self.typeChildPane,
|
|
@"canDismissWithSwipe": @YES,
|
|
@"class": LauncherPrefManageJREViewController.class,
|
|
@"enableCondition": whenNotInGame
|
|
},
|
|
@{@"key": @"java_args",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"slider.vertical.3",
|
|
@"type": self.typeTextField,
|
|
@"enableCondition": whenNotInGame
|
|
},
|
|
@{@"key": @"env_variables",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"terminal",
|
|
@"type": self.typeTextField,
|
|
@"enableCondition": whenNotInGame
|
|
},
|
|
@{@"key": @"auto_ram",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"slider.horizontal.3",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": whenNotInGame,
|
|
@"warnCondition": ^BOOL(){
|
|
return !isJailbroken;
|
|
},
|
|
@"warnKey": @"auto_ram_warn",
|
|
@"requestReload": @YES
|
|
},
|
|
@{@"key": @"allocated_memory",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"memorychip",
|
|
@"type": self.typeSlider,
|
|
@"min": @(250),
|
|
@"max": @((NSProcessInfo.processInfo.physicalMemory / 1048576) * 0.85),
|
|
@"enableCondition": ^BOOL(){
|
|
return ![self.getPreference(@"java", @"auto_ram") boolValue] && whenNotInGame();
|
|
},
|
|
@"warnCondition": ^BOOL(DBNumberedSlider *view){
|
|
return view.value >= NSProcessInfo.processInfo.physicalMemory / 1048576 * 0.37;
|
|
},
|
|
@"warnKey": @"mem_warn"
|
|
}
|
|
], @[
|
|
// Debug settings - only recommended for developer use
|
|
@{@"icon": @"ladybug"},
|
|
@{@"key": @"debug_skip_wait_jit",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"forward",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": whenNotInGame
|
|
},
|
|
@{@"key": @"debug_hide_home_indicator",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"iphone.and.arrow.forward",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": ^BOOL(){
|
|
return
|
|
self.splitViewController.view.safeAreaInsets.bottom > 0 ||
|
|
self.view.safeAreaInsets.bottom > 0;
|
|
}
|
|
},
|
|
@{@"key": @"debug_ipad_ui",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"ipad",
|
|
@"type": self.typeSwitch,
|
|
@"enableCondition": whenNotInGame
|
|
},
|
|
@{@"key": @"debug_auto_correction",
|
|
@"hasDetail": @YES,
|
|
@"icon": @"textformat.abc.dottedunderline",
|
|
@"type": self.typeSwitch
|
|
}
|
|
]
|
|
];
|
|
|
|
[super viewDidLoad];
|
|
if (self.navigationController == nil) {
|
|
self.tableView.alpha = 0.9;
|
|
}
|
|
if (NSProcessInfo.processInfo.isMacCatalystApp) {
|
|
UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeClose];
|
|
closeButton.frame = CGRectOffset(closeButton.frame, 10, 10);
|
|
[closeButton addTarget:self action:@selector(actionClose) forControlEvents:UIControlEventTouchUpInside];
|
|
[self.view addSubview:closeButton];
|
|
}
|
|
}
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated {
|
|
[super viewWillDisappear:animated];
|
|
if (self.navigationController == nil) {
|
|
[self.presentingViewController performSelector:@selector(updatePreferenceChanges)];
|
|
}
|
|
}
|
|
|
|
- (void)actionClose {
|
|
[self dismissViewControllerAnimated:YES completion:nil];
|
|
}
|
|
|
|
#pragma mark UITableView
|
|
|
|
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
|
|
if (section == 0) { // Add to general section
|
|
return [NSString stringWithFormat:@"PojavLauncher %@-%s (%s/%s)\n%@ on %@ (%s)\nPID: %d",
|
|
NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"],
|
|
CONFIG_TYPE, CONFIG_BRANCH, CONFIG_COMMIT,
|
|
UIDevice.currentDevice.completeOSVersion, [HostManager GetModelName], getenv("POJAV_DETECTEDINST"), getpid()];
|
|
}
|
|
|
|
NSString *footer = NSLocalizedStringWithDefaultValue(([NSString stringWithFormat:@"preference.section.footer.%@", self.prefSections[section]]), @"Localizable", NSBundle.mainBundle, @" ", nil);
|
|
if ([footer isEqualToString:@" "]) {
|
|
return nil;
|
|
}
|
|
return footer;
|
|
}
|
|
|
|
@end
|