diff --git a/.gitignore b/.gitignore index d947fa5d..222fa157 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /JavaApp/local_out /Natives/PojavLauncher /Natives/build -/Natives/resources/*.png .DS_STORE /pojavlauncher.xcodeproj /Natives/resources/Frameworks/libvgpu.dylib + diff --git a/JavaApp/src/main/java/net/kdt/pojavlaunch/uikit/AccountJNI.java b/JavaApp/src/main/java/net/kdt/pojavlaunch/uikit/AccountJNI.java index 7d27bf0d..49b733d1 100644 --- a/JavaApp/src/main/java/net/kdt/pojavlaunch/uikit/AccountJNI.java +++ b/JavaApp/src/main/java/net/kdt/pojavlaunch/uikit/AccountJNI.java @@ -1,5 +1,6 @@ package net.kdt.pojavlaunch.uikit; +import java.net.UnknownHostException; import net.kdt.pojavlaunch.PojavProfile; import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.authenticator.microsoft.MicrosoftAuthTask; @@ -26,12 +27,17 @@ public class AccountJNI { switch (type) { case TYPE_SELECTACC: CURRENT_ACCOUNT = MinecraftAccount.load(data); - if (CURRENT_ACCOUNT.isMicrosoft) { - CURRENT_ACCOUNT = new MicrosoftAuthTask().run("true", CURRENT_ACCOUNT.msaRefreshToken); - } else if (CURRENT_ACCOUNT.accessToken.length() > 5) { - PojavProfile.updateTokens(data); + try { + if (CURRENT_ACCOUNT.isMicrosoft) { + CURRENT_ACCOUNT = new MicrosoftAuthTask().run("true", CURRENT_ACCOUNT.msaRefreshToken); + } else if (CURRENT_ACCOUNT.accessToken.length() > 5) { + PojavProfile.updateTokens(data); + } + CURRENT_ACCOUNT = MinecraftAccount.load(data); + } catch (UnknownHostException e) { + e.printStackTrace(); + // ignore, since it's likely that the user is offline } - CURRENT_ACCOUNT = MinecraftAccount.load(data); break; case TYPE_MICROSOFT: diff --git a/JavaApp/src/main/java/net/kdt/pojavlaunch/uikit/UIKit.java b/JavaApp/src/main/java/net/kdt/pojavlaunch/uikit/UIKit.java index d0185552..965a849f 100644 --- a/JavaApp/src/main/java/net/kdt/pojavlaunch/uikit/UIKit.java +++ b/JavaApp/src/main/java/net/kdt/pojavlaunch/uikit/UIKit.java @@ -112,23 +112,20 @@ public class UIKit { CallbackBridge.mouseX = x; CallbackBridge.mouseY = y; } - CallbackBridge.mouseLastX = x; - CallbackBridge.mouseLastY = y; break; case ACTION_MOVE: if (GLFW.mGLFWIsGrabbing) { CallbackBridge.mouseX += x - CallbackBridge.mouseLastX; CallbackBridge.mouseY += y - CallbackBridge.mouseLastY; - - CallbackBridge.mouseLastX = x; - CallbackBridge.mouseLastY = y; } else { CallbackBridge.mouseX = x; CallbackBridge.mouseY = y; } break; } + CallbackBridge.mouseLastX = x; + CallbackBridge.mouseLastY = y; CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY); } diff --git a/Natives/CMakeLists.txt b/Natives/CMakeLists.txt index 50a61129..a1e1d86b 100644 --- a/Natives/CMakeLists.txt +++ b/Natives/CMakeLists.txt @@ -58,6 +58,8 @@ add_executable(PojavLauncher SurfaceViewController.m UpdateHistoryViewController.m customcontrols/ControlButton.m + customcontrols/ControlDrawer.m + customcontrols/ControlSubButton.m customcontrols/CustomControlsUtils.m egl_bridge.m external/DBNumberedSlider/Classes/DBNumberedSlider.m diff --git a/Natives/CustomControlsViewController.m b/Natives/CustomControlsViewController.m index 61f2cd85..310fb34a 100644 --- a/Natives/CustomControlsViewController.m +++ b/Natives/CustomControlsViewController.m @@ -7,16 +7,6 @@ #include "glfw_keycodes.h" #include "utils.h" -// CGRectOffset(RECT, notchOffset, 0) -#define ADD_BUTTON(NAME, KEY, RECT, VISIBLE) \ - ControlButton *button_##KEY = [ControlButton initWithName:NAME keycode:KEY rect:CGRectMake(RECT.origin.x * buttonScale, RECT.origin.y * buttonScale, RECT.size.width * buttonScale, RECT.size.height * buttonScale) transparency:0.0f]; \ - [button_##KEY addGestureRecognizer:[[UITapGestureRecognizer alloc] \ - initWithTarget:self action:@selector(showControlPopover:)]]; \ - [self.offsetView addSubview:button_##KEY]; - -#define APPLY_SCALE(KEY) \ - KEY = @([(NSNumber *)KEY floatValue] * savedScale / currentScale); - int width; @interface CustomControlsViewController () { @@ -35,6 +25,7 @@ int width; { [super viewDidLoad]; viewController = self; + isControlModifiable = YES; [self.navigationController setNavigationBarHidden:YES animated:YES]; [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; @@ -46,7 +37,7 @@ int width; } else { self.view.backgroundColor = [UIColor whiteColor]; } - + CGRect screenBounds = [[UIScreen mainScreen] bounds]; UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; @@ -59,7 +50,7 @@ int width; int height = (int) roundf(screenBounds.size.height); width = width - insets.left - insets.right; - CGFloat buttonScale = ((NSNumber *) getPreference(@"button_scale")).floatValue / 100.0; + CGFloat buttonScale = [getPreference(@"button_scale") floatValue] / 100.0; UIPanGestureRecognizer *panRecognizer; panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(wasDragged:)]; @@ -73,31 +64,19 @@ int width; NSError *cc_error; NSString *cc_data = [NSString stringWithContentsOfFile:controlFilePath encoding:NSUTF8StringEncoding error:&cc_error]; - if (cc_error != nil) { + if (cc_error) { NSLog(@"Error: could not read %@: %@", controlFilePath, cc_error.localizedDescription); showDialog(self, @"Error", [NSString stringWithFormat:@"Could not read %@: %@", controlFilePath, cc_error.localizedDescription]); } else { NSData* cc_objc_data = [cc_data dataUsingEncoding:NSUTF8StringEncoding]; self.cc_dictionary = [NSJSONSerialization JSONObjectWithData:cc_objc_data options:NSJSONReadingMutableContainers error:&cc_error]; - if (cc_error != nil) { + if (cc_error) { showDialog(self, @"Error parsing JSON", cc_error.localizedDescription); - } else if (convertLayoutIfNecessary(self.cc_dictionary)) { - NSMutableArray *cc_controlDataList = self.cc_dictionary[@"mControlDataList"]; - CGFloat currentScale = ((NSNumber *)self.cc_dictionary[@"scaledAt"]).floatValue; - CGFloat savedScale = ((NSNumber *)getPreference(@"button_scale")).floatValue; - int cc_version = ((NSNumber *)self.cc_dictionary[@"version"]).intValue; - for (int i = 0; i < (int) cc_controlDataList.count; i++) { - NSMutableDictionary *cc_buttonDict = cc_controlDataList[i]; - APPLY_SCALE(cc_buttonDict[@"width"]); - APPLY_SCALE(cc_buttonDict[@"height"]); - APPLY_SCALE(cc_buttonDict[@"strokeWidth"]); - - ControlButton *button = [ControlButton initWithProperties:cc_buttonDict]; + } else { + loadControlObject(self.offsetView, self.cc_dictionary, ^void(ControlButton* button) { [button addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showControlPopover:)]]; - [self.offsetView addSubview:button]; - } - self.cc_dictionary[@"scaledAt"] = @(savedScale); + }); } } } @@ -182,19 +161,9 @@ int width; - (void)viewDidLoad { [super viewDidLoad]; - // dirty hack to avoid displaying contents in the arrow -/* - switch (popoverPresentationController.arrowDirection) { - case UIPopoverArrowDirectionUp: - - case UIPopoverArrowDirectionDown: - case UIPopoverArrowDirectionLeft: - case UIPopoverArrowDirectionRight: - } -*/ self.view.frame = CGRectMake(0, 0, self.preferredContentSize.width, self.preferredContentSize.height); - self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.preferredContentSize.width, self.preferredContentSize.height)]; + self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(5.0, 5.0, self.preferredContentSize.width - 10.0, self.preferredContentSize.height - 10.0)]; [self.view addSubview:self.scrollView]; if (self.shouldDisplayButtonEditor) { @@ -204,9 +173,14 @@ int width; } } +- (void)viewSafeAreaInsetsDidChange { + [super viewSafeAreaInsetsDidChange]; + self.scrollView.contentInset = self.view.safeAreaInsets; +} + - (void)displayButtonEditor { - CGFloat width = self.view.frame.size.width; - CGFloat height = self.view.frame.size.height; + CGFloat width = self.view.frame.size.width - 10.0; + CGFloat height = self.view.frame.size.height - 10.0; UILabel *labelName = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)]; labelName.text = @"Name"; @@ -246,12 +220,22 @@ int width; } - (void)displayControlMenu { - UIButton *buttonExit = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - buttonExit.autoresizingMask = UIViewAutoresizingFlexibleWidth; - [buttonExit setTitle:@"Exit" forState:UIControlStateNormal]; - buttonExit.frame = CGRectMake(0.0, 0.0, self.scrollView.frame.size.width, 50.0); - [buttonExit addTarget:self action:@selector(actionExit) forControlEvents:UIControlEventTouchUpInside]; - [self.scrollView addSubview:buttonExit]; + // TODO: UIMenu for iOS 13+? + CGFloat currY = -50.0; + +//~~macro~~:fr: +#define CC_OPTION(OPT, NAME) \ + UIButton *button##OPT = [UIButton buttonWithType:UIButtonTypeRoundedRect]; \ + button##OPT.autoresizingMask = UIViewAutoresizingFlexibleWidth; \ + [button##OPT setTitle:NAME forState:UIControlStateNormal]; \ + button##OPT.frame = CGRectMake(0.0, currY+=50.0, self.scrollView.frame.size.width, 50.0); \ + [button##OPT addTarget:self action:@selector(action##OPT) forControlEvents:UIControlEventTouchUpInside]; \ + [self.scrollView addSubview:button##OPT]; \ + + CC_OPTION(Exit, @"Exit") + //CC_OPTION(Load, @"Load") + //CC_OPTION(Save, @"Save") + //CC_OPTION(SetDef, @"Set default control file") } - (void)actionExit { @@ -260,4 +244,9 @@ int width; [((UINavigationController *)self.presentingViewController) popViewControllerAnimated:YES]; } +- (void)actionSetDef { + [self dismissViewControllerAnimated:YES completion:nil]; + +} + @end diff --git a/Natives/JavaGUIViewController.m b/Natives/JavaGUIViewController.m index fc4fe605..194d1c56 100644 --- a/Natives/JavaGUIViewController.m +++ b/Natives/JavaGUIViewController.m @@ -73,7 +73,7 @@ const void * _CGDataProviderGetBytePointerCallbackAWT(void *info) { int width = (int) roundf(screenBounds.size.width); int height = (int) roundf(screenBounds.size.height); - float resolution = ((NSNumber *)getPreference(@"resolution")).floatValue / 100.0; + float resolution = [getPreference(@"resolution") floatValue] / 100.0; savedWidth = roundf(width * screenScale * resolution); savedHeight = roundf(height * screenScale * resolution); diff --git a/Natives/LauncherPreferencesViewController.m b/Natives/LauncherPreferencesViewController.m index fdf4dab8..2ab4043b 100644 --- a/Natives/LauncherPreferencesViewController.m +++ b/Natives/LauncherPreferencesViewController.m @@ -89,6 +89,8 @@ NSString *lib_gl4es115 = @"libgl4es_115.dylib"; NSString *lib_tinygl4angle = @"libtinygl4angle.dylib"; NSString *lib_zink = @"libOSMesaOverride.dylib"; +int tempIndex; + - (void)viewDidLoad { [super viewDidLoad]; @@ -212,12 +214,16 @@ NSString *lib_zink = @"libOSMesaOverride.dylib"; rendTextField.placeholder = @"Override renderer..."; if ([getPreference(@"renderer") isEqualToString:lib_gl4es114]) { rendTextField.text = gl4es114; + tempIndex = 0; } else if ([getPreference(@"renderer") isEqualToString:lib_gl4es115]) { rendTextField.text = gl4es115; + tempIndex = 1; } else if ([getPreference(@"renderer") isEqualToString:lib_tinygl4angle]) { rendTextField.text = tinygl4angle; + tempIndex = 2; } else if ([getPreference(@"renderer") isEqualToString:lib_zink]) { rendTextField.text = zink; + tempIndex = 3; } rendTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop; @@ -229,13 +235,13 @@ NSString *lib_zink = @"libOSMesaOverride.dylib"; [rendererList addObject:gl4es115]; [rendererList addObject:tinygl4angle]; [rendererList addObject:zink]; - - [self instanceDirCont]; rendPickerView = [[UIPickerView alloc] init]; rendPickerView.delegate = self; rendPickerView.dataSource = self; rendPickerView.tag = TAG_PICKER_REND; + [rendPickerView reloadAllComponents]; + [rendPickerView selectRow:tempIndex inComponent:0 animated:NO]; UIToolbar *rendPickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)]; UIBarButtonItem *rendFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; UIBarButtonItem *rendDoneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeKeyboard:)]; @@ -292,6 +298,13 @@ NSString *lib_zink = @"libOSMesaOverride.dylib"; jhomePickerView.delegate = self; jhomePickerView.dataSource = self; jhomePickerView.tag = TAG_PICKER_JHOME; + [jhomePickerView reloadAllComponents]; + for (int i = 0; i < jhomeList.count; i++) { + if ([jhomeList[i] isEqualToString:jhomeTextField.text]) { + [jhomePickerView selectRow:i inComponent:0 animated:NO]; + break; + } + } UIToolbar *jhomePickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)]; UIBarButtonItem *jhomeFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; UIBarButtonItem *jhomeDoneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeKeyboard:)]; @@ -329,6 +342,7 @@ NSString *lib_zink = @"libOSMesaOverride.dylib"; gdirPickerView.delegate = self; gdirPickerView.dataSource = self; gdirPickerView.tag = TAG_PICKER_GDIR; + [self instanceDirCont]; UIToolbar *gdirPickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)]; UIBarButtonItem *gdirFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; UIBarButtonItem *gdirDoneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeKeyboard:)]; @@ -611,15 +625,29 @@ NSString *lib_zink = @"libOSMesaOverride.dylib"; -(void)instanceDirCont { NSFileManager *fm = [NSFileManager defaultManager]; NSArray* files = [fm contentsOfDirectoryAtPath:[NSString stringWithFormat:@"%s/instances", getenv("POJAV_HOME")] error:nil]; - gdirList = [NSMutableArray arrayWithCapacity:10]; + if (gdirList == nil) { + gdirList = [NSMutableArray arrayWithCapacity:10]; + } else { + [gdirList removeAllObjects]; + } + BOOL updateTempIndex = YES; + tempIndex = 0; for(NSString *file in files) { NSString *path = [[NSString stringWithFormat:@"%s/instances", getenv("POJAV_HOME")] stringByAppendingPathComponent:file]; BOOL isDir = NO; [fm fileExistsAtPath:path isDirectory:(&isDir)]; if(isDir) { [gdirList addObject:file]; + if ([file isEqualToString:gdirTextField.text]) { + updateTempIndex = NO; + } + if (updateTempIndex) { + ++tempIndex; + } } } + [gdirPickerView reloadAllComponents]; + [gdirPickerView selectRow:tempIndex inComponent:0 animated:NO]; } - (void)createDir:(UIBarButtonItem *)sender { diff --git a/Natives/LauncherViewController.m b/Natives/LauncherViewController.m index 35a8bfc0..a49a60cf 100644 --- a/Natives/LauncherViewController.m +++ b/Natives/LauncherViewController.m @@ -233,7 +233,10 @@ int versionSelectedAt = 0; NSString *javaVer = getPreference(@"java_home"); if(![javaVer containsString:(@"java-8-openjdk")] && ![javaVer containsString:(@"jre8")]) { UIAlertController *offlineAlert = [UIAlertController alertControllerWithTitle:@"Cannot use the mod installer" message:@"In order to use the mod installer, you need to install Java 8 and specify it in the Preferences menu." preferredStyle:UIAlertControllerStyleActionSheet]; - [self setPopoverProperties:offlineAlert.popoverPresentationController sender:(UIButton *)sender]; + if (offlineAlert.popoverPresentationController != nil) { + offlineAlert.popoverPresentationController.sourceView = self.view; + offlineAlert.popoverPresentationController.sourceRect = CGRectMake(self.view.frame.size.width-10.0, 0, 10, 10); + } UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; [self presentViewController:offlineAlert animated:YES completion:nil]; [offlineAlert addAction:ok]; @@ -275,13 +278,6 @@ int versionSelectedAt = 0; callback_LauncherViewController_installMinecraft([result UTF8String]); } -- (void)setPopoverProperties:(UIPopoverPresentationController *)controller sender:(UIButton *)sender { - if (controller != nil) { - controller.sourceView = sender; - controller.sourceRect = sender.bounds; - } -} - #pragma mark - UIPopoverPresentationControllerDelegate - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection { return UIModalPresentationNone; diff --git a/Natives/LoginViewController.m b/Natives/LoginViewController.m index d43cd58f..5fd21c61 100644 --- a/Natives/LoginViewController.m +++ b/Natives/LoginViewController.m @@ -450,34 +450,23 @@ void loginAccountInput(UINavigationController *controller, int type, const char* - (void)viewDidLoad { [super viewDidLoad]; - [self setTitle:@"Select account"]; - if (self.accountList == nil) { self.accountList = [NSMutableArray array]; } else { [self.accountList removeAllObjects]; } - + // List accounts - // TODO: convert to objc - DIR *d; - struct dirent *dir; - char accPath[2048]; - sprintf(accPath, "%s/accounts", getenv("POJAV_HOME")); - d = opendir(accPath); - if (d) { - int i = 0; - while ((dir = readdir(d)) != NULL) { - // Skip "." and ".." - if (i < 2) { - i++; - continue; - } else if ([@(dir->d_name) hasSuffix:@".json"]) { - NSString *trimmedName= [@(dir->d_name) substringToIndex:((int)[@(dir->d_name) length] - 5)]; - [self.accountList addObject:trimmedName]; - } + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *dir = [NSString stringWithFormat:@"%s/accounts", getenv("POJAV_HOME")]; + NSArray *files = [fm contentsOfDirectoryAtPath:dir error:nil]; + for(NSString *file in files) { + NSString *path = [dir stringByAppendingPathComponent:file]; + BOOL isDir = NO; + [fm fileExistsAtPath:path isDirectory:(&isDir)]; + if(!isDir && [file hasSuffix:@".json"]) { + [self.accountList addObject:[file stringByDeletingPathExtension]]; } - closedir(d); } CGRect screenBounds = [[UIScreen mainScreen] bounds]; diff --git a/Natives/SurfaceViewController.h b/Natives/SurfaceViewController.h index 60e80b1c..10d09917 100644 --- a/Natives/SurfaceViewController.h +++ b/Natives/SurfaceViewController.h @@ -2,6 +2,9 @@ #import #import "MGLKit.h" +CGRect virtualMouseFrame; +CGPoint lastVirtualMousePoint; + @interface GameSurfaceView : UIView { CALayer* layer; @@ -15,5 +18,6 @@ @interface SurfaceViewController : UIViewController @property(nonatomic, strong) GameSurfaceView* surfaceView; +@property UIImageView* mousePointerView; @end diff --git a/Natives/SurfaceViewController.m b/Natives/SurfaceViewController.m index e6bb537a..14516864 100644 --- a/Natives/SurfaceViewController.m +++ b/Natives/SurfaceViewController.m @@ -4,6 +4,8 @@ #import "ios_uikit_bridge.h" #import "customcontrols/ControlButton.h" +#import "customcontrols/ControlDrawer.h" +#import "customcontrols/ControlSubButton.h" #import "customcontrols/CustomControlsUtils.h" #include "glfw_keycodes.h" @@ -14,13 +16,9 @@ #include "GLES2/gl2.h" #include "GLES2/gl2ext.h" - -// Debugging purposes +// Debugging only // #define DEBUG_VISIBLE_TEXT_FIELD -#define APPLY_SCALE(KEY) \ - KEY = @([(NSNumber *)KEY floatValue] * savedScale / currentScale); - #define INPUT_SPACE_CHAR @" " #define INPUT_SPACE_LENGTH 20 @@ -98,6 +96,8 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { { [super viewDidLoad]; viewController = self; + isControlModifiable = NO; + [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; [self setNeedsUpdateOfHomeIndicatorAutoHidden]; @@ -129,7 +129,7 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { notchOffset = insets.left; width = width - notchOffset * 2; - CGFloat buttonScale = ((NSNumber *) getPreference(@"button_scale")).floatValue / 100.0; + CGFloat buttonScale = [getPreference(@"button_scale") floatValue] / 100.0; UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(surfaceOnClick:)]; @@ -166,6 +166,13 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { [self.surfaceView addGestureRecognizer:mouseWheelGesture]; } + // Virtual mouse + virtualMouseFrame = CGRectMake(screenBounds.size.width / 2, screenBounds.size.height / 2, 18, 27); + self.mousePointerView = [[UIImageView alloc] initWithFrame:virtualMouseFrame]; + self.mousePointerView.hidden = YES; + self.mousePointerView.image = [UIImage imageNamed:@"mouse_pointer.png"]; + [self.view addSubview:self.mousePointerView]; + #ifndef DEBUG_VISIBLE_TEXT_FIELD inputView = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; inputView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.0f]; @@ -200,25 +207,15 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { self.cc_dictionary = [NSJSONSerialization JSONObjectWithData:cc_objc_data options:NSJSONReadingMutableContainers error:&cc_error]; if (cc_error != nil) { showDialog(self, @"Error parsing JSON", cc_error.localizedDescription); - } else if (convertLayoutIfNecessary(self.cc_dictionary)) { - NSMutableArray *cc_controlDataList = self.cc_dictionary[@"mControlDataList"]; + } else { CGFloat currentScale = [self.cc_dictionary[@"scaledAt"] floatValue]; CGFloat savedScale = [getPreference(@"button_scale") floatValue]; - int cc_version = [self.cc_dictionary[@"version"] intValue]; - for (NSMutableDictionary *cc_buttonDict in cc_controlDataList) { - BOOL isSwipeable = [cc_buttonDict[@"isSwipeable"] boolValue]; - APPLY_SCALE(cc_buttonDict[@"width"]); - APPLY_SCALE(cc_buttonDict[@"height"]); - APPLY_SCALE(cc_buttonDict[@"strokeWidth"]); - - ControlButton *button = [ControlButton initWithProperties:cc_buttonDict]; - [button addTarget:self action:@selector(executebtn_down:) forControlEvents:UIControlEventTouchDown]; - [button addTarget:self action:@selector(executebtn_up:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]; - [self.view addSubview:button]; + loadControlObject(self.view, self.cc_dictionary, ^void(ControlButton* button) { + BOOL isSwipeable = [button.properties[@"isSwipeable"] boolValue]; BOOL isToggleCtrlBtn = NO; for (int i = 0; i < 4; i++) { - int keycodeInt = ((NSNumber *)cc_buttonDict[@"keycodes"][i]).intValue; + int keycodeInt = [button.properties[@"keycodes"][i] intValue]; if (keycodeInt == SPECIALBTN_KEYBOARD) { inputView.frame = button.frame; } @@ -226,6 +223,10 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { isToggleCtrlBtn = YES; } } + + [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; @@ -235,7 +236,8 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { if (!isToggleCtrlBtn) { [self.togglableVisibleButtons addObject:button]; } - } + }); + self.cc_dictionary[@"scaledAt"] = @(savedScale); } } @@ -268,6 +270,27 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { CGFloat screenScale = [[UIScreen mainScreen] scale]; if (!isGrabbing) { screenScale *= resolutionScale; + if (virtualMouseEnabled) { + if (event == ACTION_MOVE) { + virtualMouseFrame.origin.x += location.x - lastVirtualMousePoint.x; + virtualMouseFrame.origin.y += location.y - lastVirtualMousePoint.y; + if (virtualMouseFrame.origin.x < 0) { + virtualMouseFrame.origin.x = 0; + } else if (virtualMouseFrame.origin.x > self.view.frame.size.width) { + virtualMouseFrame.origin.x = self.view.frame.size.width; + } + if (virtualMouseFrame.origin.y < 0) { + virtualMouseFrame.origin.y = 0; + } else if (virtualMouseFrame.origin.y > self.view.frame.size.height) { + virtualMouseFrame.origin.y = self.view.frame.size.height; + } + } + lastVirtualMousePoint = location; + self.mousePointerView.frame = virtualMouseFrame; + callback_SurfaceViewController_onTouch(event, virtualMouseFrame.origin.x * screenScale, virtualMouseFrame.origin.y * screenScale); + return; + } + lastVirtualMousePoint = location; } callback_SurfaceViewController_onTouch(event, location.x * screenScale, location.y * screenScale); } @@ -620,21 +643,32 @@ int currentVisibility = 1; [self executebtn_special_togglebtn:held]; break; - case SPECIALBTN_VIRTUALMOUSE: - case SPECIALBTN_SCROLLUP: case SPECIALBTN_SCROLLDOWN: - NSLog(@"Warning: button %@ sent unimplemented special keycode: %d", button.titleLabel.text, keycode); + if (!held) { + Java_org_lwjgl_glfw_CallbackBridge_nativeSendScroll(NULL, NULL, 0.0, 1.0); + } + break; + + case SPECIALBTN_SCROLLUP: + if (!held) { + Java_org_lwjgl_glfw_CallbackBridge_nativeSendScroll(NULL, NULL, 0.0, -1.0); + } + + case SPECIALBTN_VIRTUALMOUSE: + if (!isGrabbing && !held) { + virtualMouseEnabled = !virtualMouseEnabled; + self.mousePointerView.hidden = !virtualMouseEnabled; + } break; default: NSLog(@"Warning: button %@ sent unknown special keycode: %d", button.titleLabel.text, keycode); break; } - } else { + } else if (keycode > 0) { // there's no key id 0, but we accidentally used -1 as a special key id, so we had to do that // if (keycode == 0) { keycode = -1; } // at the moment, send unknown keycode does nothing, may even cause performance issue, so ignore it - if (keycode == 0) continue; Java_org_lwjgl_glfw_CallbackBridge_nativeSendKey(NULL, NULL, keycode, 0, held, 0); } } @@ -679,7 +713,14 @@ int currentVisibility = 1; if (held == 0) { currentVisibility = !currentVisibility; for (ControlButton *button in self.togglableVisibleButtons) { - button.hidden = currentVisibility; + if (!currentVisibility && ![button isKindOfClass:[ControlSubButton class]]) { + button.hidden = currentVisibility; + if ([button isKindOfClass:[ControlDrawer class]]) { + [(ControlDrawer *)button restoreButtonVisibility]; + } + } else if (currentVisibility) { + button.hidden = currentVisibility; + } } #ifndef DEBUG_VISIBLE_TEXT_FIELD diff --git a/Natives/customcontrols/ControlButton.h b/Natives/customcontrols/ControlButton.h index 088c1140..f5a3366c 100644 --- a/Natives/customcontrols/ControlButton.h +++ b/Natives/customcontrols/ControlButton.h @@ -13,7 +13,13 @@ @property(nonatomic, strong) NSMutableDictionary* properties; -+ (id)initWithProperties:(NSMutableDictionary *)propArray; -+ (id)initWithName:(NSString *)name keycode:(int)keycode rect:(CGRect)rect transparency:(float)transparency; ++ (id)buttonWithProperties:(NSMutableDictionary *)propArray willUpdate:(BOOL)update; ++ (id)buttonWithProperties:(NSMutableDictionary *)propArray; + +- (BOOL)canSnap:(ControlButton *)button; +- (NSString *)generateDynamicX:(CGFloat)x; +- (NSString *)generateDynamicY:(CGFloat)y; +- (void)preProcessProperties; +- (void)update; @end diff --git a/Natives/customcontrols/ControlButton.m b/Natives/customcontrols/ControlButton.m index f4379d19..822d8c94 100644 --- a/Natives/customcontrols/ControlButton.m +++ b/Natives/customcontrols/ControlButton.m @@ -1,12 +1,12 @@ #import "ControlButton.h" #import "CustomControlsUtils.h" #import "../LauncherPreferences.h" +#import "../utils.h" #define INSERT_VALUE(KEY, VALUE) \ string = [string stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"${%@}", @(KEY)] withString:VALUE]; @implementation ControlButton -@synthesize properties; /** * TODO implement: @@ -15,32 +15,53 @@ * - (maybe more) */ -+ (id)initWithProperties:(NSMutableDictionary *)propArray { - ControlButton *instance = [ControlButton buttonWithType:UIButtonTypeRoundedRect]; - [instance setTitle:propArray[@"name"] forState:UIControlStateNormal]; ++ (id)buttonWithProperties:(NSMutableDictionary *)propArray willUpdate:(BOOL)update { + //NSLog(@"DBG button prop = %@", propArray); + ControlButton *instance = [self buttonWithType:UIButtonTypeRoundedRect]; instance.autoresizingMask = UIViewAutoresizingFlexibleWidth; instance.tintColor = [UIColor whiteColor]; + instance.titleLabel.adjustsFontSizeToFitWidth = YES; + instance.titleLabel.lineBreakMode = NSLineBreakByCharWrapping; + instance.titleLabel.numberOfLines = 0; + instance.titleLabel.textAlignment = NSTextAlignmentCenter; instance.properties = propArray; - [instance update]; - + if (update) { + [instance update]; + } + return instance; } -+ (id)initWithName:(NSString *)name keycode:(int)keycode rect:(CGRect)rect transparency:(float)transparency { ++ (id)buttonWithProperties:(NSMutableDictionary *)propArray { + return [self buttonWithProperties:propArray willUpdate:YES]; +} + +/* +- (id)initWithName:(NSString *)name keycode:(int)keycode rect:(CGRect)rect transparency:(float)transparency { CGRect screenBounds = [[UIScreen mainScreen] bounds]; UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; - NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; - properties[@"name"] = name; - properties[@"dynamicX"] = @((screenBounds.size.width - insets.left - insets.right) / rect.origin.x); - properties[@"dynamicY"] = @(screenBounds.size.height / rect.origin.y); - properties[@"width"] = @(rect.size.width); - properties[@"height"] = @(rect.size.height); - properties[@"keycode"] = @(keycode); - properties[@"opacity"] = @((100.0 - transparency) / 100.0); - properties[@"cornerRadius"] = @(0); + NSMutableDictionary *localProp = [[NSMutableDictionary alloc] init]; + localProp[@"name"] = name; + localProp[@"dynamicX"] = @((screenBounds.size.width - insets.left - insets.right) / rect.origin.x); + localProp[@"dynamicY"] = @(screenBounds.size.height / rect.origin.y); + localProp[@"width"] = @(rect.size.width); + localProp[@"height"] = @(rect.size.height); + localProp[@"keycode"] = @(keycode); + localProp[@"opacity"] = @((100.0 - transparency) / 100.0); + localProp[@"cornerRadius"] = @(0); - return [ControlButton initWithProperties:properties]; + return [self initWithProperties:localProp]; +} +*/ + +- (void)preProcessProperties { + CGFloat currentScale = [current_control_object[@"scaledAt"] floatValue]; + CGFloat savedScale = [getPreference(@"button_scale") floatValue]; + if (currentScale != savedScale) { + self.properties[@"width"] = @([self.properties[@"width"] floatValue] * savedScale / currentScale); + self.properties[@"height"] = @([self.properties[@"height"] floatValue] * savedScale / currentScale); + } } - (NSString *)processFunctions:(NSString *)string { @@ -64,8 +85,8 @@ UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; // width: offset the notch parts - CGFloat screenWidth = (screenBounds.size.width - insets.left - insets.right) * screenScale; - CGFloat screenHeight = screenBounds.size.height * screenScale; + CGFloat screenWidth = dpToPx(screenBounds.size.width - insets.left - insets.right); + CGFloat screenHeight = dpToPx(screenBounds.size.height); CGFloat width = [self.properties[@"width"] floatValue]; CGFloat height = [self.properties[@"height"] floatValue]; @@ -73,8 +94,8 @@ // Insert value to ${variable} INSERT_VALUE("top", @"0"); INSERT_VALUE("left", @"0"); - INSERT_VALUE("right", ([NSString stringWithFormat:@"%f", screenWidth - width * screenScale])); - INSERT_VALUE("bottom", ([NSString stringWithFormat:@"%f", screenHeight - height * screenScale])); + INSERT_VALUE("right", ([NSString stringWithFormat:@"%f", screenWidth - dpToPx(width)])); + INSERT_VALUE("bottom", ([NSString stringWithFormat:@"%f", screenHeight - dpToPx(height)])); INSERT_VALUE("width", ([NSString stringWithFormat:@"%f", width * screenScale])); INSERT_VALUE("height", ([NSString stringWithFormat:@"%f", height * screenScale])); INSERT_VALUE("screen_width", ([NSString stringWithFormat:@"%f", screenWidth])); @@ -90,14 +111,37 @@ return [[expression expressionValueWithObject:nil context:nil] floatValue] / screenScale; } -- (void)setDefaultValueForKey:(NSString *)key value:(id)value { - if (self.properties[key] == nil || self.properties[key] == [NSNull null]) { - self.properties[key] = value; +// NOTE: Unlike Android's impl, this method uses dp instead of px (no call to dpToPx) +- (NSString *)generateDynamicX:(CGFloat)x { + CGRect screenBounds = [[UIScreen mainScreen] bounds]; + UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; + // width: offset the notch parts + CGFloat physicalWidth = screenBounds.size.width - insets.left - insets.right; + + if (x + ([self.properties[@"width"] floatValue] / 2.0) > physicalWidth / 2.0) { + return [NSString stringWithFormat:@"%f * ${screen_width} - ${width}", (x + [self.properties[@"width"] floatValue]) / physicalWidth]; + } else{ + return [NSString stringWithFormat:@"%f * ${screen_width}", x / physicalWidth]; + } +} + +// NOTE: Unlike Android's impl, this method uses dp instead of px (no call to dpToPx) +- (NSString *)generateDynamicY:(CGFloat)y { + CGRect screenBounds = [[UIScreen mainScreen] bounds]; + UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; + + CGFloat physicalHeight = screenBounds.size.height; + + if (y + ([self.properties[@"height"] floatValue] / 2.0) > physicalHeight / 2.0) { + return [NSString stringWithFormat:@"%f * ${screen_height} - ${height}", (y + [self.properties[@"height"] floatValue]) / physicalHeight]; + } else { + return [NSString stringWithFormat:@"%f * ${screen_height}", y / physicalHeight]; } } - (void)update { // net/kdt/pojavlaunch/customcontrols/ControlData.update() + [self preProcessProperties]; UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; @@ -117,17 +161,23 @@ // Update other properties self.frame = CGRectMake(propX + insets.left, propY, propW, propH); - self.alpha = [[properties valueForKey:@"opacity"] floatValue]; + self.alpha = [self.properties[@"opacity"] floatValue]; self.backgroundColor = convertARGB2UIColor(propBackgroundColor); self.layer.borderColor = [convertARGB2UIColor(propStrokeColor) CGColor]; - if (propCornerRadius > 0) { - self.layer.cornerRadius = MIN(self.frame.size.width, self.frame.size.height) / 200.0 * propCornerRadius; - } - if (propStrokeWidth > 0) { - self.layer.borderWidth = MAX(self.frame.size.width, self.frame.size.height) / 200.0 * propStrokeWidth; - } + self.layer.cornerRadius = MIN(self.frame.size.width, self.frame.size.height) / 200.0 * propCornerRadius; + self.layer.borderWidth = MAX(self.frame.size.width, self.frame.size.height) / 200.0 * propStrokeWidth; self.clipsToBounds = YES; + + [self setTitle:self.properties[@"name"] forState:UIControlStateNormal]; } +// NOTE: Unlike Android's impl, this method uses dp instead of px (no call to dpToPx), "view.center" instead of "view.pos + view.size/2" +- (BOOL)canSnap:(ControlButton *)button { + return button != self && + MathUtils_dist(button.center.x, button.center.y, self.center.x, self.center.y) + <= MAX(button.frame.size.width/2.0 + self.frame.size.width/2.0, + button.frame.size.height/2.0 + self.frame.size.height/2.0) + 8.0; + } + @end diff --git a/Natives/customcontrols/ControlDrawer.h b/Natives/customcontrols/ControlDrawer.h new file mode 100644 index 00000000..542d1589 --- /dev/null +++ b/Natives/customcontrols/ControlDrawer.h @@ -0,0 +1,19 @@ +#import +#import "ControlButton.h" +#import "ControlSubButton.h" + +@class ControlSubButton; + +@interface ControlDrawer : ControlButton { +} + +@property(nonatomic, strong) NSMutableDictionary* drawerData; +@property(nonatomic, strong) NSMutableArray* buttons; +@property(nonatomic, assign) BOOL areButtonsVisible; + ++ (id)buttonWithData:(NSMutableDictionary *)drawerData; +- (void)addButtonProp:(NSMutableDictionary *)properties; +- (void)addButton:(ControlSubButton *)button; +- (void)restoreButtonVisibility; + +@end diff --git a/Natives/customcontrols/ControlDrawer.m b/Natives/customcontrols/ControlDrawer.m new file mode 100644 index 00000000..7676cc05 --- /dev/null +++ b/Natives/customcontrols/ControlDrawer.m @@ -0,0 +1,112 @@ +#import "ControlDrawer.h" +#import "CustomControlsUtils.h" +#import "../LauncherPreferences.h" +#import "../utils.h" + +#define DOWN 0 +#define LEFT 1 +#define UP 2 +#define RIGHT 3 + +@implementation ControlDrawer + ++ (id)buttonWithData:(NSMutableDictionary *)drawerData { + ControlDrawer *instance = [self buttonWithProperties:drawerData[@"properties"] willUpdate:NO]; + instance.buttons = [[NSMutableArray alloc] init]; + instance.drawerData = drawerData; + [instance update]; + + return instance; +} + +- (void)addButtonProp:(NSMutableDictionary *)properties { + [self addButton:[ControlSubButton buttonWithProperties:self.properties]]; +} + +- (void)addButton:(ControlSubButton *)button { + [self.buttons addObject:button]; + button.parentDrawer = self; + button.hidden = !isControlModifiable; + [self syncButtons]; +} + +- (void)restoreButtonVisibility { + for (ControlButton *button in self.buttons) { + button.hidden = !self.areButtonsVisible; + } +} + +- (void)switchButtonVisibility { + self.areButtonsVisible = !self.areButtonsVisible; + [self restoreButtonVisibility]; +} + +// NOTE: Unlike Android's impl, this method uses dp instead of px (no call to dpToPx) +- (void)alignButtons { + NSString *orientation = (NSString *)self.drawerData[@"orientation"]; + if ([orientation isEqualToString:@"FREE"]) { + return; + } + + for (int i = 0; i < self.buttons.count; i++) { + ControlButton *button = self.buttons[i]; + if ([orientation isEqualToString:@"RIGHT"]) { + button.properties[@"dynamicX"] = [self generateDynamicX:self.frame.origin.x + ([self.properties[@"width"] floatValue] + 2.0) * (i+1)]; + button.properties[@"dynamicY"] = [self generateDynamicY:self.frame.origin.y]; + } else if ([orientation isEqualToString:@"LEFT"]) { + button.properties[@"dynamicX"] = [self generateDynamicX:self.frame.origin.x - ([self.properties[@"width"] floatValue] + 2.0) * (i+1)]; + button.properties[@"dynamicY"] = [self generateDynamicY:self.frame.origin.y]; + } else if ([orientation isEqualToString:@"UP"]) { + button.properties[@"dynamicY"] = [self generateDynamicY:self.frame.origin.y - ([self.properties[@"height"] floatValue] + 2.0) * (i+1)]; + button.properties[@"dynamicX"] = [self generateDynamicX:self.frame.origin.x]; + } else if ([orientation isEqualToString:@"DOWN"]) { + button.properties[@"dynamicY"] = [self generateDynamicY:self.frame.origin.y + ([self.properties[@"height"] floatValue] + 2.0) * (i+1)]; + button.properties[@"dynamicX"] = [self generateDynamicX:self.frame.origin.x]; + } else { + NSLog(@"DEBUG: %s: Unsupported button orientation %@", __FILE__, orientation); + } + [button update]; + } +} + +- (void)resizeButtons { + for (ControlButton *button in self.buttons) { + button.properties[@"width"] = self.properties[@"width"]; + button.properties[@"height"] = self.properties[@"height"]; + [button update]; + } +} + +- (void)syncButtons { + [self alignButtons]; + [self resizeButtons]; +} + +- (BOOL)containsChild:(ControlButton *)button { + return [self.buttons containsObject:button]; +} + +- (void)preProcessProperties { + [super preProcessProperties]; + self.properties[@"isHideable"] = @(YES); +} + +/* +- (void)setHidden:(BOOL)hidden { + super +} +*/ + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesEnded:touches withEvent:event]; + if (!isControlModifiable) { + [self switchButtonVisibility]; + } +} + +- (BOOL)canSnap:(ControlButton *)button { + return [super canSnap:button] && [self containsChild:button]; +} + +@end diff --git a/Natives/customcontrols/ControlSubButton.h b/Natives/customcontrols/ControlSubButton.h new file mode 100644 index 00000000..2550bf87 --- /dev/null +++ b/Natives/customcontrols/ControlSubButton.h @@ -0,0 +1,11 @@ +#import +#import "ControlDrawer.h" + +@class ControlDrawer; + +@interface ControlSubButton : ControlButton { +} + +@property(nonatomic, weak) ControlDrawer* parentDrawer; + +@end diff --git a/Natives/customcontrols/ControlSubButton.m b/Natives/customcontrols/ControlSubButton.m new file mode 100644 index 00000000..04cfd2cc --- /dev/null +++ b/Natives/customcontrols/ControlSubButton.m @@ -0,0 +1,25 @@ +#import "ControlSubButton.h" +#import "CustomControlsUtils.h" +#import "../LauncherPreferences.h" + +#define INSERT_VALUE(KEY, VALUE) \ + string = [string stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"${%@}", @(KEY)] withString:VALUE]; + +@implementation ControlSubButton + +// filterProperties +- (void)preProcessProperties { + if (self.parentDrawer) { + self.properties[@"width"] = self.parentDrawer.properties[@"width"]; + self.properties[@"height"] = self.parentDrawer.properties[@"height"]; + self.properties[@"isDynamicBtn"] = @(NO); + } +} + +- (void)setParentDrawer:(ControlDrawer *)drawer { + _parentDrawer = drawer; + [self preProcessProperties]; + //NSLog(@"DEBUG: SubButton properties %@", self.properties); +} + +@end diff --git a/Natives/customcontrols/CustomControlsUtils.h b/Natives/customcontrols/CustomControlsUtils.h index 4c05f5fa..5911de4b 100644 --- a/Natives/customcontrols/CustomControlsUtils.h +++ b/Natives/customcontrols/CustomControlsUtils.h @@ -1,6 +1,10 @@ #import +#import "ControlButton.h" + +NSMutableDictionary* current_control_object; UIColor* convertARGB2UIColor(int argb); int convertUIColor2ARGB(UIColor* color); BOOL convertLayoutIfNecessary(NSMutableDictionary* dict); void generateAndSaveDefaultControl(); +void loadControlObject(UIView* targetView, NSMutableDictionary* controlDictionary, void(^walkToButton)(ControlButton* button)); diff --git a/Natives/customcontrols/CustomControlsUtils.m b/Natives/customcontrols/CustomControlsUtils.m index 99c2d451..8d9e76c8 100644 --- a/Natives/customcontrols/CustomControlsUtils.m +++ b/Natives/customcontrols/CustomControlsUtils.m @@ -1,4 +1,7 @@ +#import "ControlDrawer.h" +#import "ControlSubButton.h" #import "CustomControlsUtils.h" +#import "../LauncherPreferences.h" #import "../ios_uikit_bridge.h" #include "../glfw_keycodes.h" #include "../utils.h" @@ -69,7 +72,7 @@ void convertV2Layout(NSMutableDictionary* dict) { } } - dict[@"version"] = @(3); + dict[@"version"] = @(4); } void convertV1Layout(NSMutableDictionary* dict) { @@ -86,8 +89,8 @@ void convertV1Layout(NSMutableDictionary* dict) { [btnDict removeObjectForKey:@"transparency"]; // pixel of width, height -> dp - btnDict[@"width"] = @(((NSNumber *)btnDict[@"width"]).floatValue / scale * 50.0); - btnDict[@"height"] = @(((NSNumber *)btnDict[@"height"]).floatValue / scale * 50.0); + btnDict[@"width"] = @([btnDict[@"width"] floatValue] / scale * 50.0); + btnDict[@"height"] = @([btnDict[@"height"] floatValue] / scale * 50.0); // isRound -> cornerRadius 35% if ([btnDict[@"isRound"] boolValue] == YES) { @@ -119,6 +122,8 @@ void convertV1Layout(NSMutableDictionary* dict) { // set final keycode array btnDict[@"keycodes"] = keycodes; + + btnDict[@"mDrawerDataList"] = [[NSMutableArray alloc] init]; } dict[@"scaledAt"] = @(100); @@ -138,6 +143,7 @@ BOOL convertLayoutIfNecessary(NSMutableDictionary* dict) { convertV2Layout(dict); break; case 3: + case 4: break; default: showDialog(viewController, @"Error parsing JSON", [NSString stringWithFormat:@"Incompatible control version code %d. This control version was not implemented in this launcher build.", version]); @@ -147,9 +153,9 @@ BOOL convertLayoutIfNecessary(NSMutableDictionary* dict) { } void generateAndSaveDefaultControl() { - // Generate a V2.3 control + // Generate a v2.4 control NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; - dict[@"version"] = @(3); + dict[@"version"] = @(4); dict[@"scaledAt"] = @(100); dict[@"mControlDataList"] = [[NSMutableArray alloc] init]; [dict[@"mControlDataList"] addObject:createButton(@"Keyboard", @@ -176,14 +182,12 @@ void generateAndSaveDefaultControl() { @"${screen_height} - ${margin} * 3 - ${height} * 3", BTN_SQUARE )]; -/* // TODO: virtual mouse [dict[@"mControlDataList"] addObject:createButton(@"Mouse", (int[]){SPECIALBTN_VIRTUALMOUSE,0,0,0}, - @"${right}", + @"${right} - ${margin}", @"${margin}", BTN_RECT )]; -*/ [dict[@"mControlDataList"] addObject:createButton(@"Debug", (int[]){GLFW_KEY_F3,0,0,0}, @"${margin}", @@ -290,3 +294,36 @@ void generateAndSaveDefaultControl() { )]; */ } + +void loadControlObject(UIView* targetView, NSMutableDictionary* controlDictionary, void(^walkToButton)(ControlButton* button)) { + current_control_object = controlDictionary; + if (convertLayoutIfNecessary(controlDictionary)) { + NSMutableArray *controlDataList = controlDictionary[@"mControlDataList"]; + setPreference(@"internal_current_button_scale", controlDictionary[@"scaledAt"]); + for (NSMutableDictionary *buttonDict in controlDataList) { + //APPLY_SCALE(buttonDict[@"strokeWidth"]); + ControlButton *button = [ControlButton buttonWithProperties:buttonDict]; + walkToButton(button); + [targetView addSubview:button]; + //NSLog(@"DBG Added button=%@", button); + } + + NSMutableArray *drawerDataList = controlDictionary[@"mDrawerDataList"]; + for (NSMutableDictionary *drawerData in drawerDataList) { + ControlDrawer *drawer = [ControlDrawer buttonWithData:drawerData]; + if (isControlModifiable) drawer.areButtonsVisible = YES; + walkToButton(drawer); + [targetView addSubview:drawer]; + //NSLog(@"DBG Added drawer=%@", drawer); + + for (NSMutableDictionary *subButton in drawerData[@"buttonProperties"]) { + ControlSubButton *subView = [ControlSubButton buttonWithProperties:subButton]; + [drawer addButton:subView]; + walkToButton(subView); + [targetView addSubview:subView]; + } + } + + controlDictionary[@"scaledAt"] = @([getPreference(@"button_scale") floatValue]); + } +} diff --git a/Natives/input_bridge_v3.m b/Natives/input_bridge_v3.m index 8213a0c8..14fe5b4b 100644 --- a/Natives/input_bridge_v3.m +++ b/Natives/input_bridge_v3.m @@ -12,6 +12,7 @@ #import #import "AppDelegate.h" +#import "SurfaceViewController.h" #include #include @@ -261,7 +262,20 @@ JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetGrabbing(JNIE grabCursorX = xset; // savedWidth / 2; grabCursorY = yset; // savedHeight / 2; isPrepareGrabPos = true; + + CGRect screenBounds = [[UIScreen mainScreen] bounds]; + virtualMouseFrame.origin.x = screenBounds.size.width / 2; + virtualMouseFrame.origin.y = screenBounds.size.height / 2; } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (isGrabbing == JNI_TRUE) { + CGFloat screenScale = [[UIScreen mainScreen] scale] * resolutionScale; + callback_SurfaceViewController_onTouch(ACTION_DOWN, lastVirtualMousePoint.x * screenScale, lastVirtualMousePoint.y * screenScale); + ((SurfaceViewController *)viewController).mousePointerView.frame = virtualMouseFrame; + } + ((SurfaceViewController *)viewController).mousePointerView.hidden = isGrabbing || !virtualMouseEnabled; + }); } JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeIsGrabbing(JNIEnv* env, jclass clazz) { diff --git a/Natives/resources/Frameworks/MetalANGLE.framework/MetalANGLE b/Natives/resources/Frameworks/MetalANGLE.framework/MetalANGLE index b582d913..fb90b373 100755 Binary files a/Natives/resources/Frameworks/MetalANGLE.framework/MetalANGLE and b/Natives/resources/Frameworks/MetalANGLE.framework/MetalANGLE differ diff --git a/Natives/resources/mouse_pointer.png b/Natives/resources/mouse_pointer.png new file mode 100644 index 00000000..df9f2946 Binary files /dev/null and b/Natives/resources/mouse_pointer.png differ diff --git a/Natives/utils.h b/Natives/utils.h index 6a63e448..31125583 100644 --- a/Natives/utils.h +++ b/Natives/utils.h @@ -33,6 +33,7 @@ #define SPECIALBTN_SCROLLDOWN -8 static float resolutionScale = 1.0; +BOOL isControlModifiable; UIViewController *viewController; @@ -53,6 +54,11 @@ jboolean isGrabbing; int savedWidth, savedHeight; +BOOL virtualMouseEnabled; + +CGFloat MathUtils_dist(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2); +CGFloat dpToPx(CGFloat dp); +CGFloat pxToDp(CGFloat px); void _CGDataProviderReleaseBytePointerCallback(void *info,const void *pointer); jboolean attachThread(bool isAndroid, JNIEnv** secondJNIEnvPtr); diff --git a/Natives/utils.m b/Natives/utils.m index 34bb08ea..24e45c0b 100644 --- a/Natives/utils.m +++ b/Natives/utils.m @@ -8,6 +8,11 @@ #include "utils.h" +CGFloat MathUtils_dist(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2) { + const CGFloat x = (x2 - x1); + const CGFloat y = (y2 - y1); + return (CGFloat) hypot(x, y); + } void _CGDataProviderReleaseBytePointerCallback(void *info,const void *pointer) { } @@ -49,6 +54,16 @@ void free_char_array(JNIEnv *env, jobjectArray jstringArray, char **charArray) { free(charArray); } +CGFloat dpToPx(CGFloat dp) { + CGFloat screenScale = [[UIScreen mainScreen] scale]; + return dp * screenScale; +} + +CGFloat pxToDp(CGFloat px) { + CGFloat screenScale = [[UIScreen mainScreen] scale]; + return px / screenScale; +} + jstring convertStringJVM(JNIEnv* srcEnv, JNIEnv* dstEnv, jstring srcStr) { if (srcStr == NULL) { return NULL; @@ -60,21 +75,6 @@ jstring convertStringJVM(JNIEnv* srcEnv, JNIEnv* dstEnv, jstring srcStr) { return dstStr; } -JNIEXPORT jint JNICALL Java_android_os_OpenJDKNativeRegister_nativeRegisterNatives(JNIEnv *env, jclass clazz, jstring registerSymbol) { - const char *register_symbol_c = (*env)->GetStringUTFChars(env, registerSymbol, 0); - void *symbol = dlsym(RTLD_DEFAULT, register_symbol_c); - if (symbol == NULL) { - printf("dlsym %s failed: %s\n", register_symbol_c, dlerror()); - return -1; - } - - int (*registerNativesForClass)(JNIEnv*) = symbol; - int result = registerNativesForClass(env); - (*env)->ReleaseStringUTFChars(env, registerSymbol, register_symbol_c); - - return (jint) result; -} - JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_utils_JREUtils_setLdLibraryPath(JNIEnv *env, jclass clazz, jstring ldLibraryPath) { // jclass exception_cls = (*env)->FindClass(env, "java/lang/UnsatisfiedLinkError");