Files
PojavLauncher_iOS/Natives/LauncherPreferencesViewController.m

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