diff --git a/JavaApp/src/main/java/net/kdt/pojavlaunch/PojavLauncher.java b/JavaApp/src/main/java/net/kdt/pojavlaunch/PojavLauncher.java index 5423f076..00b20f6a 100644 --- a/JavaApp/src/main/java/net/kdt/pojavlaunch/PojavLauncher.java +++ b/JavaApp/src/main/java/net/kdt/pojavlaunch/PojavLauncher.java @@ -49,7 +49,7 @@ public class PojavLauncher { Class.forName("com.github.caciocavallosilano.cacio.ctc.CTCPreloadClassLoader"); } catch (ClassNotFoundException e) {} - if (args[0].equals(".LaunchJAR")) { + if (args[0].equals("--launchJar")) { UIKit.callback_JavaGUIViewController_launchJarFile(args[1]); } else { launchMinecraft(args); diff --git a/JavaApp/src/main/java/net/kdt/pojavlaunch/Tools.java b/JavaApp/src/main/java/net/kdt/pojavlaunch/Tools.java index 1a134a0d..8138dd48 100644 --- a/JavaApp/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/JavaApp/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -51,8 +51,6 @@ public final class Tools { public static final String ASSETS_PATH = DIR_GAME_NEW + "/assets"; public static final String OBSOLETE_RESOURCES_PATH=DIR_GAME_NEW + "/resources"; - - public static final String NATIVE_LIB_DIR = "@executable_path/Frameworks"; public static void launchMinecraft(MinecraftAccount profile, final JMinecraftVersionList.Version versionInfo) throws Throwable { String[] launchArgs = getMinecraftArgs(profile, versionInfo); @@ -103,7 +101,7 @@ public final class Tools { varArgMap.put("user_type", "msa"); varArgMap.put("version_name", versionName); varArgMap.put("version_type", versionInfo.type); - varArgMap.put("natives_directory", Tools.NATIVE_LIB_DIR); + varArgMap.put("natives_directory", System.getProperty("java.library.path")); List minecraftArgs = new ArrayList(); if (versionInfo.arguments != null) { diff --git a/Natives/CMakeLists.txt b/Natives/CMakeLists.txt index 8cb8fd79..f979b18f 100644 --- a/Natives/CMakeLists.txt +++ b/Natives/CMakeLists.txt @@ -143,6 +143,7 @@ add_executable(PojavLauncher LauncherSplitViewController.m MinecraftResourceUtils.m PickTextField.m + PLLogOutputView.m PLPickerView.m PLPreferences.m PLPrefTableViewController.m diff --git a/Natives/JavaGUIViewController.m b/Natives/JavaGUIViewController.m index 4a04dfbd..b0723b67 100644 --- a/Natives/JavaGUIViewController.m +++ b/Natives/JavaGUIViewController.m @@ -3,12 +3,15 @@ #import "JavaGUIViewController.h" #import "JavaLauncher.h" #import "LauncherPreferences.h" +#import "PLLogOutputView.h" #import "TrackedTextField.h" #import "UnzipKit.h" #import "ios_uikit_bridge.h" #include "glfw_keycodes.h" #include "utils.h" +#define SPECIALBTN_LOGOUTPUT -100 + static BOOL shouldHitEnterAfterWindowShown; static int* rgbArray; static SurfaceView* surfaceView; @@ -106,6 +109,7 @@ void AWTInputBridge_sendKey(int keycode) { @property(nonatomic) TrackedTextField* inputTextField; @property(nonatomic) UIImageView* mousePointerView; @property(nonatomic) ControlLayout* ctrlView; +@property(nonatomic) PLLogOutputView* logOutputView; @end @@ -200,6 +204,10 @@ void AWTInputBridge_sendKey(int keycode) { [self.view addSubview:self.ctrlView]; [self loadCustomControls]; + self.logOutputView = [[PLLogOutputView alloc] initWithFrame:self.view.frame]; + self.logOutputView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + [self.view addSubview:self.logOutputView]; + rgbArray = calloc(4, (size_t) (windowWidth * windowHeight)); setenv("POJAV_SKIP_JNI_GLFW", "1", 1); @@ -228,6 +236,11 @@ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ BTN_RECT )]; #endif + [dict[@"mControlDataList"] addObject:createButton(@"Log output", + (int[]){SPECIALBTN_LOGOUTPUT,0,0,0}, + @"${right} - ${margin}", @"${margin}", + BTN_RECT + )]; [dict[@"mControlDataList"] addObject:createButton(@"PRI", (int[]){SPECIALBTN_MOUSEPRI,0,0,0}, @"${margin}", @"${bottom} - ${margin}", @@ -338,12 +351,17 @@ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ break; case SPECIALBTN_VIRTUALMOUSE: - if (held) return; + if (held) break; virtualMouseEnabled = !virtualMouseEnabled; self.mousePointerView.hidden = !virtualMouseEnabled; setPrefBool(@"control.virtmouse_enable", virtualMouseEnabled); break; + case SPECIALBTN_LOGOUTPUT: + if (held) break; + [self.logOutputView actionToggleLogOutput]; + break; + default: NSLog(@"Warning: button %@ sent unknown special keycode: %d", sender.titleLabel.text, keycode); break; diff --git a/Natives/JavaLauncher.m b/Natives/JavaLauncher.m index 55be4be1..20071afd 100644 --- a/Natives/JavaLauncher.m +++ b/Natives/JavaLauncher.m @@ -124,7 +124,7 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi [PLProfiles resolveKeyForCurrentProfile:@"gameDir"]] .stringByStandardizingPath; } else { - defaultJRETag = @"install_jar"; + defaultJRETag = @"execute_jar"; gameDir = @(getenv("POJAV_GAME_DIR")); } NSLog(@"[JavaLauncher] Looking for Java %d or later", minVersion); @@ -132,9 +132,9 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi if (javaHome == nil) { UIKit_returnToSplitView(); - BOOL isInstallJar = [defaultJRETag isEqualToString:@"install_jar"]; + BOOL isExecuteJar = [defaultJRETag isEqualToString:@"execute_jar"]; showDialog(localize(@"Error", nil), [NSString stringWithFormat:localize(@"java.error.missing_runtime", nil), - isInstallJar ? [launchTarget lastPathComponent] : PLProfiles.current.selectedProfile[@"lastVersionId"], minVersion]); + isExecuteJar ? [launchTarget lastPathComponent] : PLProfiles.current.selectedProfile[@"lastVersionId"], minVersion]); return 1; } else if ([javaHome hasPrefix:@(getenv("POJAV_HOME"))]) { if (NSBundle.mainBundle.infoDictionary[@"LCDataUUID"]) { @@ -294,7 +294,7 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi margv[++margc] = "net.kdt.pojavlaunch.PojavLauncher"; if (username == nil) { - margv[++margc] = ".LaunchJAR"; + margv[++margc] = "--launchJar"; } else { margv[++margc] = username.UTF8String; } diff --git a/Natives/LauncherMenuViewController.m b/Natives/LauncherMenuViewController.m index 14ff1535..0915c276 100644 --- a/Natives/LauncherMenuViewController.m +++ b/Natives/LauncherMenuViewController.m @@ -73,7 +73,7 @@ } [self.options addObject: (id)[LauncherMenuCustomItem - title:localize(@"launcher.menu.install_jar", nil) + title:localize(@"launcher.menu.execute_jar", nil) imageName:@"MenuInstallJar" action:^{ [contentNavigationController performSelector:@selector(enterModInstaller)]; }]]; diff --git a/Natives/LauncherPrefManageJREViewController.m b/Natives/LauncherPrefManageJREViewController.m index 90b0bb56..409006f6 100644 --- a/Natives/LauncherPrefManageJREViewController.m +++ b/Natives/LauncherPrefManageJREViewController.m @@ -62,11 +62,11 @@ static WFWorkflowProgressView* currentProgressView; self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; self.javaRuntimes = @{ - @(DEFAULT_JRE): @[@"preference.manage_runtime.default.1165", @"preference.manage_runtime.default.117", @"launcher.menu.install_jar"] + @(DEFAULT_JRE): @[@"preference.manage_runtime.default.1165", @"preference.manage_runtime.default.117", @"launcher.menu.execute_jar"] }.mutableCopy; self.sortedJavaVersions = @[@(DEFAULT_JRE)].mutableCopy; - self.selectedRTTags = @[@"1_16_5_older", @"1_17_newer", @"install_jar"]; + self.selectedRTTags = @[@"1_16_5_older", @"1_17_newer", @"execute_jar"]; self.selectedRuntimes = getPrefObject(@"java.java_homes"); NSString *internalPath = [NSString stringWithFormat:@"%@/java_runtimes", NSBundle.mainBundle.bundlePath]; diff --git a/Natives/LauncherPreferences.m b/Natives/LauncherPreferences.m index 4d8793ed..935f60a7 100644 --- a/Natives/LauncherPreferences.m +++ b/Natives/LauncherPreferences.m @@ -86,7 +86,7 @@ NSString* getSelectedJavaHome(NSString* defaultJRETag, int minVersion) { NSDictionary *pref = getPrefObject(@"java.java_homes"); NSDictionary *selected = pref[@"0"]; NSString *selectedVer = selected[defaultJRETag]; - if ([defaultJRETag isEqualToString:@"install_jar"] && minVersion > selectedVer.intValue) { + if ([defaultJRETag isEqualToString:@"execute_jar"] && minVersion > selectedVer.intValue) { NSArray *sortedVersions = [pref.allKeys valueForKeyPath:@"self.integerValue"]; sortedVersions = [sortedVersions sortedArrayUsingSelector:@selector(compare:)]; for (NSNumber *version in sortedVersions) { diff --git a/Natives/PLLogOutputView.h b/Natives/PLLogOutputView.h new file mode 100644 index 00000000..cc459b26 --- /dev/null +++ b/Natives/PLLogOutputView.h @@ -0,0 +1,8 @@ +#import + +@interface PLLogOutputView : UIView +- (void)actionStartStopLogOutput; +- (void)actionToggleLogOutput; ++ (void)appendToLog:(NSString *)line; ++ (void)handleExitCode:(int)code; +@end diff --git a/Natives/PLLogOutputView.m b/Natives/PLLogOutputView.m new file mode 100644 index 00000000..ae3175c1 --- /dev/null +++ b/Natives/PLLogOutputView.m @@ -0,0 +1,209 @@ +#import "PLLogOutputView.h" +#import "SurfaceViewController.h" +#import "utils.h" + +@interface PLLogOutputView() +@property(nonatomic) UITableView* logTableView; +@property(nonatomic) UINavigationBar* navigationBar; +@end + +@implementation PLLogOutputView +static BOOL fatalErrorOccurred; +static NSMutableArray* logLines; +static PLLogOutputView* current; + +- (instancetype)initWithFrame:(CGRect)frame { + frame.origin.y = frame.size.height; + self = [super initWithFrame:frame]; + frame.origin.y = 0; + + logLines = [NSMutableArray new]; + self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5]; + self.hidden = YES; + + UINavigationItem *navigationItem = [[UINavigationItem alloc] init]; + navigationItem.rightBarButtonItems = @[ + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop + target:self action:@selector(actionToggleLogOutput)], + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash + target:self action:@selector(actionClearLogOutput)] + ]; + self.navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, 44)]; + self.navigationBar.items = @[navigationItem]; + self.navigationBar.topItem.title = localize(@"game.menu.log_output", nil); + [self.navigationBar sizeToFit]; + self.navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; + + self.logTableView = [[UITableView alloc] initWithFrame:frame]; + //self.logTableView.allowsSelection = NO; + self.logTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + self.logTableView.backgroundColor = UIColor.clearColor; + self.logTableView.contentInset = UIEdgeInsetsMake(self.navigationBar.frame.size.height, 0, 0, 0); + self.logTableView.dataSource = self; + self.logTableView.delegate = self; + self.logTableView.layoutMargins = UIEdgeInsetsZero; + self.logTableView.rowHeight = 20; + self.logTableView.separatorInset = UIEdgeInsetsZero; + self.logTableView.separatorStyle = UITableViewCellSeparatorStyleNone; + [self addSubview:self.logTableView]; + [self addSubview:self.navigationBar]; + + canAppendToLog = YES; + [self actionStartStopLogOutput]; + + current = self; + return self; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return logLines.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; + + if (cell == nil) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"]; + cell.backgroundColor = UIColor.clearColor; + //cell.selectionStyle = UITableViewCellSelectionStyleNone; + cell.textLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:16]; + cell.textLabel.textColor = UIColor.whiteColor; + } + cell.textLabel.text = logLines[indexPath.row]; + + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [tableView deselectRowAtIndexPath:indexPath animated:NO]; + + UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; + NSString *line = cell.textLabel.text; + if (line.length == 0 || [line isEqualToString:@"\n"]) { + return; + } + + UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:line preferredStyle:UIAlertControllerStyleActionSheet]; + alert.popoverPresentationController.sourceView = cell; + alert.popoverPresentationController.sourceRect = cell.bounds; + UIAlertAction *share = [UIAlertAction actionWithTitle:localize(localize(@"Share", nil), nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[line] applicationActivities:nil]; + activityVC.popoverPresentationController.sourceView = _navigationBar; + activityVC.popoverPresentationController.sourceRect = _navigationBar.bounds; + [currentVC() presentViewController:activityVC animated:YES completion:nil]; + }]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]; + [alert addAction:share]; + [alert addAction:cancel]; + [currentVC() presentViewController:alert animated:YES completion:nil]; +} + +- (void)actionClearLogOutput { + [logLines removeAllObjects]; + [self.logTableView reloadData]; +} + +- (void)actionShareLatestlog { + NSString *latestlogPath = [NSString stringWithFormat:@"file://%s/latestlog.txt", getenv("POJAV_HOME")]; + UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[@"latestlog.txt", + [NSURL URLWithString:latestlogPath]] applicationActivities:nil]; + activityVC.popoverPresentationController.sourceView = self.navigationBar; + activityVC.popoverPresentationController.sourceRect = self.navigationBar.bounds; + [currentVC() presentViewController:activityVC animated:YES completion:nil]; +} + +- (void)actionStartStopLogOutput { + canAppendToLog = !canAppendToLog; + UINavigationItem* item = self.navigationBar.items[0]; + item.leftBarButtonItem = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem: + canAppendToLog ? UIBarButtonSystemItemPause : UIBarButtonSystemItemPlay + target:self action:@selector(actionStartStopLogOutput)]; +} + +- (void)actionToggleLogOutput { + if (fatalErrorOccurred) { + [self performSelector:@selector(actionForceClose)]; + return; + } + + UIViewAnimationOptions opt = self.hidden ? UIViewAnimationOptionCurveEaseOut : UIViewAnimationOptionCurveEaseIn; + [UIView transitionWithView:self duration:0.4 options:UIViewAnimationOptionCurveEaseOut animations:^(void){ + CGRect frame = self.frame; + frame.origin.y = self.hidden ? 0 : frame.size.height; + self.hidden = NO; + self.frame = frame; + } completion: ^(BOOL finished) { + self.hidden = self.frame.origin.y != 0; + }]; +} + ++ (void)_appendToLog:(NSString *)line { + if (line.length == 0) { + return; + } + + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:logLines.count inSection:0]; + [logLines addObject:line]; + UIView.animationsEnabled = NO; + [current.logTableView beginUpdates]; + [current.logTableView + insertRowsAtIndexPaths:@[indexPath] + withRowAnimation:UITableViewRowAnimationNone]; + [current.logTableView endUpdates]; + UIView.animationsEnabled = YES; + + [current.logTableView + scrollToRowAtIndexPath:indexPath + atScrollPosition:UITableViewScrollPositionBottom animated:NO]; +} + ++ (void)appendToLog:(NSString *)string { + dispatch_async(dispatch_get_main_queue(), ^(void){ + NSArray *lines = [string componentsSeparatedByCharactersInSet: + NSCharacterSet.newlineCharacterSet]; + for (NSString *line in lines) { + [self _appendToLog:line]; + } + }); +} + ++ (void)handleExitCode:(int)code { + dispatch_async(dispatch_get_main_queue(), ^(void){ + if (current.hidden) { + [current actionToggleLogOutput]; + } + // Cleanup navigation bar + UINavigationBar *navigationBar = current.navigationBar; + navigationBar.topItem.title = [NSString stringWithFormat: + localize(@"game.title.exit_code", nil), code]; + navigationBar.items[0].leftBarButtonItem = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemAction + target:currentVC() action:@selector(actionShareLatestlog)]; + UIBarButtonItem *exitItem = navigationBar.items[0].rightBarButtonItems[0]; + navigationBar.items[0].rightBarButtonItems = nil; + navigationBar.items[0].rightBarButtonItem = exitItem; + + if (canAppendToLog) { + canAppendToLog = NO; + fatalErrorOccurred = YES; + return; + } + [current actionClearLogOutput]; + [self _appendToLog:@"... (latestlog.txt)"]; + NSString *latestlogPath = [NSString stringWithFormat:@"%s/latestlog.txt", getenv("POJAV_HOME")]; + NSString *linesStr = [NSString stringWithContentsOfFile:latestlogPath + encoding:NSUTF8StringEncoding error:nil]; + NSArray *lines = [linesStr componentsSeparatedByCharactersInSet: + NSCharacterSet.newlineCharacterSet]; + + // Print last 100 lines from latestlog.txt + for (int i = MAX(lines.count-100, 0); i < lines.count; i++) { + [self _appendToLog:lines[i]]; + } + + fatalErrorOccurred = YES; + }); +} + +@end diff --git a/Natives/PLPreferences.m b/Natives/PLPreferences.m index bf6c3b6f..bd30cf47 100644 --- a/Natives/PLPreferences.m +++ b/Natives/PLPreferences.m @@ -49,7 +49,7 @@ @"0": @{ @"1_16_5_older": @"8", @"1_17_newer": @"17", - @"install_jar": @"8" + @"execute_jar": @"8" }.mutableCopy, @"8": @"internal", @"17": @"internal" diff --git a/Natives/SurfaceViewController+LogView.m b/Natives/SurfaceViewController+LogView.m index dbba3738..feb729c9 100644 --- a/Natives/SurfaceViewController+LogView.m +++ b/Natives/SurfaceViewController+LogView.m @@ -1,225 +1,11 @@ #import "SurfaceViewController.h" #import "utils.h" -static BOOL fatalErrorOccurred; - -extern UIWindow* currentWindow(); - -@interface LogDelegate : NSObject -@end - -@interface LogDelegate() -@end - -static NSMutableArray* logLines; - -@implementation LogDelegate - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return logLines.count; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; - - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"]; - cell.backgroundColor = UIColor.clearColor; - //cell.selectionStyle = UITableViewCellSelectionStyleNone; - cell.textLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:16]; - cell.textLabel.textColor = UIColor.whiteColor; - } - cell.textLabel.text = logLines[indexPath.row]; - - return cell; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [tableView deselectRowAtIndexPath:indexPath animated:NO]; - - UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - NSString *line = cell.textLabel.text; - if (line.length == 0 || [line isEqualToString:@"\n"]) { - return; - } - - SurfaceViewController *vc = (id)currentWindow().rootViewController; - - UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:line preferredStyle:UIAlertControllerStyleActionSheet]; - alert.popoverPresentationController.sourceView = cell; - alert.popoverPresentationController.sourceRect = cell.bounds; - UIAlertAction *share = [UIAlertAction actionWithTitle:localize(localize(@"Share", nil), nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - UIView *navigationBar = vc.logOutputView.subviews[1]; - UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[line] applicationActivities:nil]; - activityVC.popoverPresentationController.sourceView = navigationBar; - activityVC.popoverPresentationController.sourceRect = navigationBar.bounds; - [vc presentViewController:activityVC animated:YES completion:nil]; - }]; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]; - [alert addAction:share]; - [alert addAction:cancel]; - [vc presentViewController:alert animated:YES completion:nil]; -} - -@end - @implementation SurfaceViewController(LogView) -static LogDelegate* logDelegate; -static int logCharPerLine; - - (void)initCategory_LogView { - logLines = NSMutableArray.new; - logCharPerLine = self.view.frame.size.width / 10; - - self.logOutputView = [[UIView alloc] initWithFrame:CGRectOffset(self.view.frame, 0, self.view.frame.size.height)]; - self.logOutputView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5]; - self.logOutputView.hidden = YES; - - UINavigationItem *navigationItem = [[UINavigationItem alloc] init]; - navigationItem.rightBarButtonItems = @[ - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop - target:self action:@selector(actionToggleLogOutput)], - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash - target:self action:@selector(actionClearLogOutput)] - ]; - UINavigationBar* navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)]; - navigationBar.items = @[navigationItem]; - navigationBar.topItem.title = localize(@"game.menu.log_output", nil); - [navigationBar sizeToFit]; - navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; - - self.logTableView = [[UITableView alloc] initWithFrame:self.view.frame]; - logDelegate = [[LogDelegate alloc] init]; - //self.logTableView.allowsSelection = NO; - self.logTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - self.logTableView.backgroundColor = UIColor.clearColor; - self.logTableView.contentInset = UIEdgeInsetsMake(navigationBar.frame.size.height, 0, 0, 0); - self.logTableView.dataSource = logDelegate; - self.logTableView.delegate = logDelegate; - self.logTableView.layoutMargins = UIEdgeInsetsZero; - self.logTableView.rowHeight = 20; - self.logTableView.separatorInset = UIEdgeInsetsZero; - self.logTableView.separatorStyle = UITableViewCellSeparatorStyleNone; - [self.logOutputView addSubview:self.logTableView]; - [self.logOutputView addSubview:navigationBar]; + self.logOutputView = [[PLLogOutputView alloc] initWithFrame:self.view.frame]; [self.rootView addSubview:self.logOutputView]; - - canAppendToLog = YES; - [self actionStartStopLogOutput]; -} - -- (void)actionClearLogOutput { - [logLines removeAllObjects]; - [self.logTableView reloadData]; -} - -- (void)actionShareLatestlog { - UINavigationBar *navigationBar = self.logOutputView.subviews[1]; - NSString *latestlogPath = [NSString stringWithFormat:@"file://%s/latestlog.txt", getenv("POJAV_HOME")]; - UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[@"latestlog.txt", - [NSURL URLWithString:latestlogPath]] applicationActivities:nil]; - activityVC.popoverPresentationController.sourceView = navigationBar; - activityVC.popoverPresentationController.sourceRect = navigationBar.bounds; - [self presentViewController:activityVC animated:YES completion:nil]; -} - -- (void)actionStartStopLogOutput { - canAppendToLog = !canAppendToLog; - UINavigationItem* item = ((UINavigationBar *)self.logOutputView.subviews[1]).items[0]; - item.leftBarButtonItem = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem: - canAppendToLog ? UIBarButtonSystemItemPause : UIBarButtonSystemItemPlay - target:self action:@selector(actionStartStopLogOutput)]; -} - -- (void)actionToggleLogOutput { - if (fatalErrorOccurred) { - [self performSelector:@selector(actionForceClose)]; - return; - } - - UIViewAnimationOptions opt = self.logOutputView.hidden ? UIViewAnimationOptionCurveEaseOut : UIViewAnimationOptionCurveEaseIn; - [UIView transitionWithView:self.logOutputView duration:0.4 options:UIViewAnimationOptionCurveEaseOut animations:^(void){ - CGRect frame = self.logOutputView.frame; - frame.origin.y = self.logOutputView.hidden ? 0 : frame.size.height; - self.logOutputView.hidden = NO; - self.logOutputView.frame = frame; - } completion: ^(BOOL finished) { - self.logOutputView.hidden = self.logOutputView.frame.origin.y != 0; - }]; -} - -+ (void)_appendToLog:(NSString *)line { - if (line.length == 0) { - return; - } - - SurfaceViewController *instance = (id)currentWindow().rootViewController; - - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:logLines.count inSection:0]; - [logLines addObject:line]; - UIView.animationsEnabled = NO; - [instance.logTableView beginUpdates]; - [instance.logTableView - insertRowsAtIndexPaths:@[indexPath] - withRowAnimation:UITableViewRowAnimationNone]; - [instance.logTableView endUpdates]; - UIView.animationsEnabled = YES; - - [instance.logTableView - scrollToRowAtIndexPath:indexPath - atScrollPosition:UITableViewScrollPositionBottom animated:NO]; -} - -+ (void)appendToLog:(NSString *)string { - dispatch_async(dispatch_get_main_queue(), ^(void){ - NSArray *lines = [string componentsSeparatedByCharactersInSet: - NSCharacterSet.newlineCharacterSet]; - for (NSString *line in lines) { - [self _appendToLog:line]; - } - }); -} - -+ (void)handleExitCode:(int)code { - dispatch_async(dispatch_get_main_queue(), ^(void){ - SurfaceViewController *instance = (id)currentWindow().rootViewController; - - if (instance.logOutputView.hidden) { - [instance actionToggleLogOutput]; - } - // Cleanup navigation bar - UINavigationBar *navigationBar = instance.logOutputView.subviews[1]; - navigationBar.topItem.title = [NSString stringWithFormat: - localize(@"game.title.exit_code", nil), code]; - navigationBar.items[0].leftBarButtonItem = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem:UIBarButtonSystemItemAction - target:instance action:@selector(actionShareLatestlog)]; - UIBarButtonItem *exitItem = navigationBar.items[0].rightBarButtonItems[0]; - navigationBar.items[0].rightBarButtonItems = nil; - navigationBar.items[0].rightBarButtonItem = exitItem; - - if (canAppendToLog) { - canAppendToLog = NO; - fatalErrorOccurred = YES; - return; - } - [instance actionClearLogOutput]; - [self _appendToLog:@"... (latestlog.txt)"]; - NSString *latestlogPath = [NSString stringWithFormat:@"%s/latestlog.txt", getenv("POJAV_HOME")]; - NSString *linesStr = [NSString stringWithContentsOfFile:latestlogPath - encoding:NSUTF8StringEncoding error:nil]; - NSArray *lines = [linesStr componentsSeparatedByCharactersInSet: - NSCharacterSet.newlineCharacterSet]; - - // Print last 100 lines from latestlog.txt - for (int i = MAX(lines.count-100, 0); i < lines.count; i++) { - [self _appendToLog:lines[i]]; - } - - fatalErrorOccurred = YES; - }); } - (void)viewWillTransitionToSize_LogView:(CGRect)frame { diff --git a/Natives/SurfaceViewController+Navigation.m b/Natives/SurfaceViewController+Navigation.m index 91f9df9e..8d9a30e8 100644 --- a/Natives/SurfaceViewController+Navigation.m +++ b/Natives/SurfaceViewController+Navigation.m @@ -200,7 +200,7 @@ static CGPoint lastCenterPoint; [self actionForceClose]; break; case 1: - [self performSelector:@selector(actionToggleLogOutput)]; + [self.logOutputView actionToggleLogOutput]; break; case 2: [self actionOpenCustomControls]; diff --git a/Natives/SurfaceViewController.h b/Natives/SurfaceViewController.h index 60ad03d5..3a143b1e 100644 --- a/Natives/SurfaceViewController.h +++ b/Natives/SurfaceViewController.h @@ -2,6 +2,7 @@ #import "customcontrols/ControlLayout.h" #import "GameSurfaceView.h" +#import "PLLogOutputView.h" BOOL canAppendToLog; dispatch_group_t fatalExitGroup; @@ -26,8 +27,7 @@ CGPoint lastVirtualMousePoint; + (BOOL)isRunning; // LogView category -@property(nonatomic) UITableView* logTableView; -@property(nonatomic) UIView* logOutputView; +@property(nonatomic) PLLogOutputView* logOutputView; // Navigation category @property(nonatomic) NSArray *menuArray; @@ -45,9 +45,6 @@ CGPoint lastVirtualMousePoint; @interface SurfaceViewController(LogView) -+ (void)appendToLog:(NSString *)line; -+ (void)handleExitCode:(int)code; - - (void)viewWillTransitionToSize_LogView:(CGRect)frame; @end diff --git a/Natives/ios_uikit_bridge.m b/Natives/ios_uikit_bridge.m index a686eea1..532a0de4 100644 --- a/Natives/ios_uikit_bridge.m +++ b/Natives/ios_uikit_bridge.m @@ -4,6 +4,7 @@ #import "LauncherNavigationController.h" #import "LauncherPreferences.h" #import "LauncherSplitViewController.h" +#import "PLLogOutputView.h" #import "SurfaceViewController.h" #include @@ -46,7 +47,7 @@ JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_uikit_UIKit_showError(JNIEnv* en if (SurfaceViewController.isRunning) { NSLog(@"%@\n%@", title_o, message_o); - [SurfaceViewController handleExitCode:1]; + [PLLogOutputView handleExitCode:1]; return; } diff --git a/Natives/main.m b/Natives/main.m index 63ac2293..7326e553 100644 --- a/Natives/main.m +++ b/Natives/main.m @@ -8,6 +8,7 @@ #import "HostManagerBridge.h" #import "JavaLauncher.h" #import "LauncherPreferences.h" +#import "PLLogOutputView.h" #import "PLProfiles.h" #import "SurfaceViewController.h" #import "UIKit+hook.h" @@ -208,7 +209,7 @@ void init_redirectStdio() { } } if (canAppendToLog) { - [SurfaceViewController appendToLog:@(buf)]; + [PLLogOutputView appendToLog:@(buf)]; } [file writeData:[NSData dataWithBytes:buf length:rsize]]; [file synchronizeFile]; diff --git a/Natives/main_hook.m b/Natives/main_hook.m index 9232ba18..68c90990 100644 --- a/Natives/main_hook.m +++ b/Natives/main_hook.m @@ -1,4 +1,5 @@ #import +#import "PLLogOutputView.h" #import "SurfaceViewController.h" #import "ios_uikit_bridge.h" #import "utils.h" @@ -14,7 +15,7 @@ void handle_fatal_exit(int code) { return; } - [SurfaceViewController handleExitCode:code]; + [PLLogOutputView handleExitCode:code]; if (fatalExitGroup != nil) { // Likely other threads are crashing, put them to sleep diff --git a/Natives/resources/en.lproj/Localizable.strings b/Natives/resources/en.lproj/Localizable.strings index a16524b6..2bb798fe 100644 --- a/Natives/resources/en.lproj/Localizable.strings +++ b/Natives/resources/en.lproj/Localizable.strings @@ -74,7 +74,7 @@ "launcher.mcl.error_download" = "Failed to download %@: %@"; "launcher.menu.custom_controls" = "Custom controls"; -"launcher.menu.install_jar" = "Install .jar"; +"launcher.menu.execute_jar" = "Execute .jar"; "launcher.wait_jit.title" = "Waiting for JIT"; "launcher.wait_jit.message" = "PojavLauncher relies on Just-in-time compilation for Java to work at maximum performance."; @@ -226,7 +226,7 @@ "preference.manage_runtime.header.default" = "Preferred Java version"; "preference.manage_runtime.default.1165" = "1.16.5 and older"; "preference.manage_runtime.default.117" = "1.17 and newer"; -"preference.manage_runtime.footer.default" = "Minecraft 1.16.5 and older may run with a newer Java version, however compatibility with mods is not guaranteed. External runtimes can be placed in the “java_runtimes” folder. Changing Java version for “Install .jar” only takes effect if the installer does not expect a newer Java version."; +"preference.manage_runtime.footer.default" = "Minecraft 1.16.5 and older may run with a newer Java version, however compatibility with mods is not guaranteed. External runtimes can be placed in the “java_runtimes” folder. Changing Java version for “Execute .jar” only takes effect if the installer does not expect a newer Java version."; "preference.manage_runtime.footer.java8" = "This is the default version for Minecraft 1.16.5 and older."; "preference.manage_runtime.footer.java17" = "This is the default version for Minecraft 1.17 and newer."; "preference.manage_runtime.header.invalid" = "Invalid runtimes";