Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/PojavLauncher_iOS
Path: blob/main/Natives/LauncherPreferencesViewController.m
589 views
1
#import <Foundation/Foundation.h>
2
3
#import "DBNumberedSlider.h"
4
#import "HostManagerBridge.h"
5
#import "LauncherNavigationController.h"
6
#import "LauncherMenuViewController.h"
7
#import "LauncherPreferences.h"
8
#import "LauncherPreferencesViewController.h"
9
#import "LauncherPrefContCfgViewController.h"
10
#import "LauncherPrefManageJREViewController.h"
11
#import "UIKit+hook.h"
12
13
#import "config.h"
14
#import "ios_uikit_bridge.h"
15
#import "utils.h"
16
17
@interface LauncherPreferencesViewController()
18
@property(nonatomic) NSArray<NSString*> *rendererKeys, *rendererList;
19
@end
20
21
@implementation LauncherPreferencesViewController
22
23
- (id)init {
24
self = [super init];
25
self.title = localize(@"Settings", nil);
26
return self;
27
}
28
29
- (NSString *)imageName {
30
return @"MenuSettings";
31
}
32
33
- (void)viewDidLoad
34
{
35
self.getPreference = ^id(NSString *section, NSString *key){
36
NSString *keyFull = [NSString stringWithFormat:@"%@.%@", section, key];
37
return getPrefObject(keyFull);
38
};
39
self.setPreference = ^(NSString *section, NSString *key, id value){
40
NSString *keyFull = [NSString stringWithFormat:@"%@.%@", section, key];
41
setPrefObject(keyFull, value);
42
};
43
44
self.hasDetail = YES;
45
self.prefDetailVisible = self.navigationController == nil;
46
47
self.prefSections = @[@"general", @"video", @"control", @"java", @"debug"];
48
49
self.rendererKeys = getRendererKeys(NO);
50
self.rendererList = getRendererNames(NO);
51
52
BOOL(^whenNotInGame)() = ^BOOL(){
53
return self.navigationController != nil;
54
};
55
self.prefContents = @[
56
@[
57
// General settings
58
@{@"icon": @"cube"},
59
@{@"key": @"check_sha",
60
@"hasDetail": @YES,
61
@"icon": @"lock.shield",
62
@"type": self.typeSwitch,
63
@"enableCondition": whenNotInGame
64
},
65
@{@"key": @"cosmetica",
66
@"hasDetail": @YES,
67
@"icon": @"eyeglasses",
68
@"type": self.typeSwitch,
69
@"enableCondition": whenNotInGame
70
},
71
@{@"key": @"debug_logging",
72
@"hasDetail": @YES,
73
@"icon": @"doc.badge.gearshape",
74
@"type": self.typeSwitch,
75
@"action": ^(BOOL enabled){
76
debugLogEnabled = enabled;
77
NSLog(@"[Debugging] Debug log enabled: %@", enabled ? @"YES" : @"NO");
78
}
79
},
80
@{@"key": @"appicon",
81
@"hasDetail": @YES,
82
@"icon": @"paintbrush",
83
@"type": self.typePickField,
84
@"enableCondition": ^BOOL(){
85
return UIApplication.sharedApplication.supportsAlternateIcons;
86
},
87
@"action": ^void(NSString *iconName) {
88
if ([iconName isEqualToString:@"AppIcon-Light"]) {
89
iconName = nil;
90
}
91
[UIApplication.sharedApplication setAlternateIconName:iconName completionHandler:^(NSError * _Nullable error) {
92
if (error == nil) return;
93
NSLog(@"Error in appicon: %@", error);
94
showDialog(localize(@"Error", nil), error.localizedDescription);
95
}];
96
},
97
@"pickKeys": @[
98
@"AppIcon-Light",
99
@"AppIcon-Dark",
100
@"AppIcon-Development"
101
],
102
@"pickList": @[
103
localize(@"preference.title.appicon-default", nil),
104
localize(@"preference.title.appicon-dark", nil),
105
localize(@"preference.title.appicon-development", nil)
106
]
107
},
108
@{@"key": @"hidden_sidebar",
109
@"hasDetail": @YES,
110
@"icon": @"sidebar.leading",
111
@"type": self.typeSwitch,
112
@"enableCondition": whenNotInGame
113
},
114
@{@"key": @"reset_warnings",
115
@"icon": @"exclamationmark.triangle",
116
@"type": self.typeButton,
117
@"enableCondition": whenNotInGame,
118
@"action": ^void(){
119
resetWarnings();
120
}
121
},
122
@{@"key": @"reset_settings",
123
@"icon": @"trash",
124
@"type": self.typeButton,
125
@"enableCondition": whenNotInGame,
126
@"requestReload": @YES,
127
@"showConfirmPrompt": @YES,
128
@"destructive": @YES,
129
@"action": ^void(){
130
loadPreferences(YES);
131
[self.tableView reloadData];
132
}
133
},
134
@{@"key": @"erase_demo_data",
135
@"icon": @"trash",
136
@"type": self.typeButton,
137
@"enableCondition": ^BOOL(){
138
NSString *demoPath = [NSString stringWithFormat:@"%s/.demo", getenv("POJAV_HOME")];
139
int count = [NSFileManager.defaultManager contentsOfDirectoryAtPath:demoPath error:nil].count;
140
return whenNotInGame() && count > 0;
141
},
142
@"showConfirmPrompt": @YES,
143
@"destructive": @YES,
144
@"action": ^void(){
145
NSString *demoPath = [NSString stringWithFormat:@"%s/.demo", getenv("POJAV_HOME")];
146
NSError *error;
147
if([NSFileManager.defaultManager removeItemAtPath:demoPath error:&error]) {
148
[NSFileManager.defaultManager createDirectoryAtPath:demoPath
149
withIntermediateDirectories:YES attributes:nil error:nil];
150
[NSFileManager.defaultManager changeCurrentDirectoryPath:demoPath];
151
if (getenv("DEMO_LOCK")) {
152
[(LauncherNavigationController *)self.navigationController fetchLocalVersionList];
153
}
154
} else {
155
NSLog(@"Error in erase_demo_data: %@", error);
156
showDialog(localize(@"Error", nil), error.localizedDescription);
157
}
158
}
159
}
160
], @[
161
// Video and renderer settings
162
@{@"icon": @"video"},
163
@{@"key": @"renderer",
164
@"hasDetail": @YES,
165
@"icon": @"cpu",
166
@"type": self.typePickField,
167
@"enableCondition": whenNotInGame,
168
@"pickKeys": self.rendererKeys,
169
@"pickList": self.rendererList
170
},
171
@{@"key": @"resolution",
172
@"hasDetail": @YES,
173
@"icon": @"viewfinder",
174
@"type": self.typeSlider,
175
@"min": @(25),
176
@"max": @(150)
177
},
178
@{@"key": @"max_framerate",
179
@"hasDetail": @YES,
180
@"icon": @"timelapse",
181
@"type": self.typeSwitch,
182
@"enableCondition": ^BOOL(){
183
return whenNotInGame() && (UIScreen.mainScreen.maximumFramesPerSecond > 60);
184
}
185
},
186
@{@"key": @"performance_hud",
187
@"hasDetail": @YES,
188
@"icon": @"waveform.path.ecg",
189
@"type": self.typeSwitch,
190
@"enableCondition": ^BOOL(){
191
return [CAMetalLayer instancesRespondToSelector:@selector(developerHUDProperties)];
192
}
193
},
194
@{@"key": @"fullscreen_airplay",
195
@"hasDetail": @YES,
196
@"icon": @"airplayvideo",
197
@"type": self.typeSwitch,
198
@"action": ^(BOOL enabled){
199
if (self.navigationController != nil) return;
200
if (UIApplication.sharedApplication.connectedScenes.count < 2) return;
201
if (enabled) {
202
[self.presentingViewController performSelector:@selector(switchToExternalDisplay)];
203
} else {
204
[self.presentingViewController performSelector:@selector(switchToInternalDisplay)];
205
}
206
}
207
},
208
@{@"key": @"silence_other_audio",
209
@"hasDetail": @YES,
210
@"icon": @"speaker.slash",
211
@"type": self.typeSwitch
212
},
213
@{@"key": @"silence_with_switch",
214
@"hasDetail": @YES,
215
@"icon": @"speaker.zzz",
216
@"type": self.typeSwitch
217
},
218
], @[
219
// Control settings
220
@{@"icon": @"gamecontroller"},
221
@{@"key": @"default_gamepad_ctrl",
222
@"icon": @"hammer",
223
@"type": self.typeChildPane,
224
@"enableCondition": whenNotInGame,
225
@"canDismissWithSwipe": @NO,
226
@"class": LauncherPrefContCfgViewController.class
227
},
228
@{@"key": @"hardware_hide",
229
@"icon": @"eye.slash",
230
@"hasDetail": @YES,
231
@"type": self.typeSwitch,
232
},
233
@{@"key": @"recording_hide",
234
@"icon": @"eye.slash",
235
@"hasDetail": @YES,
236
@"type": self.typeSwitch,
237
},
238
@{@"key": @"gesture_mouse",
239
@"icon": @"cursorarrow.click",
240
@"hasDetail": @YES,
241
@"type": self.typeSwitch,
242
},
243
@{@"key": @"gesture_hotbar",
244
@"icon": @"hand.tap",
245
@"hasDetail": @YES,
246
@"type": self.typeSwitch,
247
},
248
@{@"key": @"disable_haptics",
249
@"icon": @"wave.3.left",
250
@"hasDetail": @NO,
251
@"type": self.typeSwitch,
252
},
253
@{@"key": @"slideable_hotbar",
254
@"hasDetail": @YES,
255
@"icon": @"slider.horizontal.below.rectangle",
256
@"type": self.typeSwitch
257
},
258
@{@"key": @"press_duration",
259
@"hasDetail": @YES,
260
@"icon": @"cursorarrow.click.badge.clock",
261
@"type": self.typeSlider,
262
@"min": @(100),
263
@"max": @(1000),
264
},
265
@{@"key": @"button_scale",
266
@"hasDetail": @YES,
267
@"icon": @"aspectratio",
268
@"type": self.typeSlider,
269
@"min": @(50), // 80?
270
@"max": @(500)
271
},
272
@{@"key": @"mouse_scale",
273
@"hasDetail": @YES,
274
@"icon": @"arrow.up.left.and.arrow.down.right.circle",
275
@"type": self.typeSlider,
276
@"min": @(25),
277
@"max": @(300)
278
},
279
@{@"key": @"mouse_speed",
280
@"hasDetail": @YES,
281
@"icon": @"cursorarrow.motionlines",
282
@"type": self.typeSlider,
283
@"min": @(25),
284
@"max": @(300)
285
},
286
@{@"key": @"virtmouse_enable",
287
@"hasDetail": @YES,
288
@"icon": @"cursorarrow.rays",
289
@"type": self.typeSwitch
290
},
291
@{@"key": @"gyroscope_enable",
292
@"hasDetail": @YES,
293
@"icon": @"gyroscope",
294
@"type": self.typeSwitch,
295
@"enableCondition": ^BOOL(){
296
return realUIIdiom != UIUserInterfaceIdiomTV;
297
}
298
},
299
@{@"key": @"gyroscope_invert_x_axis",
300
@"hasDetail": @YES,
301
@"icon": @"arrow.left.and.right",
302
@"type": self.typeSwitch,
303
@"enableCondition": ^BOOL(){
304
return realUIIdiom != UIUserInterfaceIdiomTV;
305
}
306
},
307
@{@"key": @"gyroscope_sensitivity",
308
@"hasDetail": @YES,
309
@"icon": @"move.3d",
310
@"type": self.typeSlider,
311
@"min": @(50),
312
@"max": @(300),
313
@"enableCondition": ^BOOL(){
314
return realUIIdiom != UIUserInterfaceIdiomTV;
315
}
316
}
317
], @[
318
// Java tweaks
319
@{@"icon": @"sparkles"},
320
@{@"key": @"manage_runtime",
321
@"hasDetail": @YES,
322
@"icon": @"cube",
323
@"type": self.typeChildPane,
324
@"canDismissWithSwipe": @YES,
325
@"class": LauncherPrefManageJREViewController.class,
326
@"enableCondition": whenNotInGame
327
},
328
@{@"key": @"java_args",
329
@"hasDetail": @YES,
330
@"icon": @"slider.vertical.3",
331
@"type": self.typeTextField,
332
@"enableCondition": whenNotInGame
333
},
334
@{@"key": @"env_variables",
335
@"hasDetail": @YES,
336
@"icon": @"terminal",
337
@"type": self.typeTextField,
338
@"enableCondition": whenNotInGame
339
},
340
@{@"key": @"auto_ram",
341
@"hasDetail": @YES,
342
@"icon": @"slider.horizontal.3",
343
@"type": self.typeSwitch,
344
@"enableCondition": whenNotInGame,
345
@"warnCondition": ^BOOL(){
346
return !isJailbroken;
347
},
348
@"warnKey": @"auto_ram_warn",
349
@"requestReload": @YES
350
},
351
@{@"key": @"allocated_memory",
352
@"hasDetail": @YES,
353
@"icon": @"memorychip",
354
@"type": self.typeSlider,
355
@"min": @(250),
356
@"max": @((NSProcessInfo.processInfo.physicalMemory / 1048576) * 0.85),
357
@"enableCondition": ^BOOL(){
358
return ![self.getPreference(@"java", @"auto_ram") boolValue] && whenNotInGame();
359
},
360
@"warnCondition": ^BOOL(DBNumberedSlider *view){
361
return view.value >= NSProcessInfo.processInfo.physicalMemory / 1048576 * 0.37;
362
},
363
@"warnKey": @"mem_warn"
364
}
365
], @[
366
// Debug settings - only recommended for developer use
367
@{@"icon": @"ladybug"},
368
@{@"key": @"debug_skip_wait_jit",
369
@"hasDetail": @YES,
370
@"icon": @"forward",
371
@"type": self.typeSwitch,
372
@"enableCondition": whenNotInGame
373
},
374
@{@"key": @"debug_hide_home_indicator",
375
@"hasDetail": @YES,
376
@"icon": @"iphone.and.arrow.forward",
377
@"type": self.typeSwitch,
378
@"enableCondition": ^BOOL(){
379
return
380
self.splitViewController.view.safeAreaInsets.bottom > 0 ||
381
self.view.safeAreaInsets.bottom > 0;
382
}
383
},
384
@{@"key": @"debug_ipad_ui",
385
@"hasDetail": @YES,
386
@"icon": @"ipad",
387
@"type": self.typeSwitch,
388
@"enableCondition": whenNotInGame
389
},
390
@{@"key": @"debug_auto_correction",
391
@"hasDetail": @YES,
392
@"icon": @"textformat.abc.dottedunderline",
393
@"type": self.typeSwitch
394
}
395
]
396
];
397
398
[super viewDidLoad];
399
if (self.navigationController == nil) {
400
self.tableView.alpha = 0.9;
401
}
402
}
403
404
- (void)viewWillDisappear:(BOOL)animated {
405
[super viewWillDisappear:animated];
406
if (self.navigationController == nil) {
407
[self.presentingViewController performSelector:@selector(updatePreferenceChanges)];
408
}
409
}
410
411
#pragma mark UITableView
412
413
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
414
if (section == 0) { // Add to general section
415
return [NSString stringWithFormat:@"PojavLauncher %@-%s (%s/%s)\n%@ on %@ (%s)\nPID: %d",
416
NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"],
417
CONFIG_TYPE, CONFIG_BRANCH, CONFIG_COMMIT,
418
UIDevice.currentDevice.completeOSVersion, [HostManager GetModelName], getenv("POJAV_DETECTEDINST"), getpid()];
419
}
420
421
NSString *footer = NSLocalizedStringWithDefaultValue(([NSString stringWithFormat:@"preference.section.footer.%@", self.prefSections[section]]), @"Localizable", NSBundle.mainBundle, @" ", nil);
422
if ([footer isEqualToString:@" "]) {
423
return nil;
424
}
425
return footer;
426
}
427
428
@end
429
430