diff --git a/CMakeLists.txt b/CMakeLists.txt index 768c2b84b9..e98a4041dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1150,8 +1150,8 @@ elseif(IOS) ios/iCade/iCadeReaderView.h ios/iCade/iCadeReaderView.m ios/iCade/iCadeState.h - UI/DarwinMemoryStickManager.mm - UI/DarwinMemoryStickManager.h + UI/DarwinFileSystemServices.mm + UI/DarwinFileSystemServices.h Common/Battery/AppleBatteryClient.m ) @@ -1172,7 +1172,7 @@ elseif(IOS) set_source_files_properties(ios/main.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(ios/CameraHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(ios/LocationHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) - set_source_files_properties(UI/DarwinMemoryStickManager.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) + set_source_files_properties(UI/DarwinFileSystemServices.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc) set(TargetBin PPSSPP) elseif(USING_QT_UI) @@ -1231,8 +1231,8 @@ elseif(TARGET SDL2::SDL2) endif() set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2) if(APPLE) - set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm SDL/SDLCocoaMetalLayer.h SDL/SDLCocoaMetalLayer.mm UI/DarwinMemoryStickManager.mm Common/Battery/AppleBatteryClient.m) - set_source_files_properties(UI/DarwinMemoryStickManager.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) + set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm SDL/SDLCocoaMetalLayer.h SDL/SDLCocoaMetalLayer.mm UI/DarwinFileSystemServices.mm UI/DarwinFileSystemServices.h Common/Battery/AppleBatteryClient.m) + set_source_files_properties(UI/DarwinFileSystemServices.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc) set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY}) elseif(USING_EGL) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 211314d016..dea43f64f0 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -58,6 +58,9 @@ SDLJoystick *joystick = NULL; #include "SDLGLGraphicsContext.h" #include "SDLVulkanGraphicsContext.h" +#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) +#include "UI/DarwinFileSystemServices.h" +#endif GlobalUIState lastUIState = UISTATE_MENU; GlobalUIState GetUIState(); @@ -185,7 +188,18 @@ void System_SendMessage(const char *command, const char *parameter) { } else if (!strcmp(command, "audio_resetDevice")) { StopSDLAudioDevice(); InitSDLAudioDevice(); - } + } +#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) + else if (!strcmp(command, "browse_folder")) { + DarwinDirectoryPanelCallback callback = [] (Path thePathChosen) { + NativeMessageReceived("browse_folder", thePathChosen.c_str()); + }; + + DarwinFileSystemServices services; + services.presentDirectoryPanel(callback, /* allowFiles = */ true, /* allowDirectorites = */ true); + } +#endif + } void System_AskForPermission(SystemPermission permission) {} diff --git a/UI/DarwinMemoryStickManager.h b/UI/DarwinFileSystemServices.h similarity index 55% rename from UI/DarwinMemoryStickManager.h rename to UI/DarwinFileSystemServices.h index 4d2f5cbd8f..f49d053d42 100644 --- a/UI/DarwinMemoryStickManager.h +++ b/UI/DarwinFileSystemServices.h @@ -1,12 +1,11 @@ // -// DarwinMemoryStickManager.h +// DarwinFileSystemServices.h // PPSSPP // // Created by Serena on 20/01/2023. // -#ifndef DarwinMemoryStickManager_h -#define DarwinMemoryStickManager_h +#pragma once #include "ppsspp_config.h" #include "Common/File/Path.h" @@ -15,15 +14,14 @@ typedef std::function DarwinDirectoryPanelCallback; -/// A Class to manage the memory stick on Darwin (macOS, iOS) platforms, -/// consisting of meth(od)s to present the directory panel -/// to choose the user preferred memory stick directory, -/// to determine the appropriate memory stick directory, -/// and to *set* the preferred memory stick directory. -class DarwinMemoryStickManager { +/// A Class providing help functions to work with the FileSystem +/// on Darwin platforms. +class DarwinFileSystemServices { public: /// Present a pannel to choose the directory as the memory stick manager. - void presentDirectoryPanel(DarwinDirectoryPanelCallback); + void presentDirectoryPanel(DarwinDirectoryPanelCallback, + bool allowFiles = false, + bool allowDirectories = true); static Path appropriateMemoryStickDirectoryToUse(); static void setUserPreferredMemoryStickDirectory(Path); @@ -34,5 +32,3 @@ private: void *__pickerDelegate = NULL; #endif // PPSSPP_PLATFORM(IOS) }; - -#endif /* DarwinMemoryStickManager_h */ diff --git a/UI/DarwinMemoryStickManager.mm b/UI/DarwinFileSystemServices.mm similarity index 51% rename from UI/DarwinMemoryStickManager.mm rename to UI/DarwinFileSystemServices.mm index fea8aa9052..50d9f94ec6 100644 --- a/UI/DarwinMemoryStickManager.mm +++ b/UI/DarwinFileSystemServices.mm @@ -1,5 +1,5 @@ // -// DarwinMemoryStickManager.mm +// DarwinFileSystemServices.mm // PPSSPP // // Created by Serena on 20/01/2023. @@ -7,12 +7,12 @@ #include "ppsspp_config.h" #include "Core/Config.h" -#include "DarwinMemoryStickManager.h" +#include "DarwinFileSystemServices.h" #include #include #if !__has_feature(objc_arc) -#error Must be built with ARC, please revise the flags for DarwinMemoryStickManager.mm to include -fobjc-arc. +#error Must be built with ARC, please revise the flags for DarwinFileSystemServices.mm to include -fobjc-arc. #endif #if __has_include() @@ -32,10 +32,8 @@ } - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { - // Notice: we could've hardcoded behaviour here to call - // `DarwinMemoryStickManager::setUserPreferredMemoryStickDirectory` - // But for the sake of flexibility in the future, i'll just call a provided callback. - if (urls.count >= 1) self.callback(Path(urls[0].path.UTF8String)); + if (urls.count >= 1) + self.callback(Path(urls[0].path.UTF8String)); } @end @@ -44,36 +42,51 @@ #include #endif // __has_include() -void DarwinMemoryStickManager::presentDirectoryPanel(DarwinDirectoryPanelCallback callback) { +void DarwinFileSystemServices::presentDirectoryPanel(DarwinDirectoryPanelCallback callback, + bool allowFiles, + bool allowDirectories) { dispatch_async(dispatch_get_main_queue(), ^{ #if PPSSPP_PLATFORM(MAC) NSOpenPanel *panel = [[NSOpenPanel alloc] init]; panel.allowsMultipleSelection = NO; - panel.canChooseFiles = NO; - panel.canChooseDirectories = YES; - panel.allowedFileTypes = @[(__bridge NSString *)kUTTypeFolder]; + panel.canChooseFiles = allowFiles; + panel.canChooseDirectories = allowDirectories; +// if (!allowFiles && allowDirectories) +// panel.allowedFileTypes = @[(__bridge NSString *)kUTTypeFolder]; NSModalResponse modalResponse = [panel runModal]; - if (modalResponse == NSModalResponseOK && panel.URLs && panel.URLs.firstObject) + if (modalResponse == NSModalResponseOK && panel.URLs.firstObject) callback(Path(panel.URLs.firstObject.path.UTF8String)); #elif PPSSPP_PLATFORM(IOS) - if (UIWindow *window = [UIApplication.sharedApplication keyWindow]) { - if (UIViewController *viewController = window.rootViewController) { - NSString *folderUTType = (__bridge NSString *)kUTTypeFolder; - UIDocumentPickerViewController *pickerVC = [[UIDocumentPickerViewController alloc] - initWithDocumentTypes:@[folderUTType] inMode:UIDocumentPickerModeOpen]; - // What if you wanted to go to heaven, but then God showed you the next few lines? - // serious note: have to do this, because __pickerDelegate has to stay retained as a class property - __pickerDelegate = (void *)CFBridgingRetain([[DocumentPickerDelegate alloc] initWithCallback:callback]); - pickerVC.delegate = (__bridge DocumentPickerDelegate *)__pickerDelegate; - [viewController presentViewController:pickerVC animated:true completion:nil]; - } - } + UIViewController *rootViewController = UIApplication.sharedApplication + .keyWindow + .rootViewController; + + // get current window view controller + if (!rootViewController) + return; + + NSMutableArray *types = [NSMutableArray array]; + UIDocumentPickerMode pickerMode = UIDocumentPickerModeOpen; + + if (allowDirectories) + [types addObject: (__bridge NSString *)kUTTypeFolder]; + if (allowFiles) { + [types addObject: (__bridge NSString *)kUTTypeItem]; + pickerMode = UIDocumentPickerModeImport; + } + + UIDocumentPickerViewController *pickerVC = [[UIDocumentPickerViewController alloc] initWithDocumentTypes: types inMode: pickerMode]; + // What if you wanted to go to heaven, but then God showed you the next few lines? + // serious note: have to do this, because __pickerDelegate has to stay retained as a class property + __pickerDelegate = (void *)CFBridgingRetain([[DocumentPickerDelegate alloc] initWithCallback:callback]); + pickerVC.delegate = (__bridge DocumentPickerDelegate *)__pickerDelegate; + [rootViewController presentViewController:pickerVC animated:true completion:nil]; #endif }); } -Path DarwinMemoryStickManager::appropriateMemoryStickDirectoryToUse() { +Path DarwinFileSystemServices::appropriateMemoryStickDirectoryToUse() { NSString *userPreferred = [[NSUserDefaults standardUserDefaults] stringForKey:@(PreferredMemoryStickUserDefaultsKey)]; if (userPreferred) return Path(userPreferred.UTF8String); @@ -81,7 +94,7 @@ Path DarwinMemoryStickManager::appropriateMemoryStickDirectoryToUse() { return __defaultMemoryStickPath(); } -Path DarwinMemoryStickManager::__defaultMemoryStickPath() { +Path DarwinFileSystemServices::__defaultMemoryStickPath() { #if PPSSPP_PLATFORM(IOS) NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; @@ -91,7 +104,7 @@ Path DarwinMemoryStickManager::__defaultMemoryStickPath() { #endif } -void DarwinMemoryStickManager::setUserPreferredMemoryStickDirectory(Path path) { +void DarwinFileSystemServices::setUserPreferredMemoryStickDirectory(Path path) { [[NSUserDefaults standardUserDefaults] setObject:@(path.c_str()) forKey:@(PreferredMemoryStickUserDefaultsKey)]; g_Config.memStickDirectory = path; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 9c6a74bcd9..3645c5c089 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -76,7 +76,7 @@ #include "GPU/Common/FramebufferManagerCommon.h" #if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) -#include "UI/DarwinMemoryStickManager.h" +#include "UI/DarwinFileSystemServices.h" #endif #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) @@ -1228,9 +1228,9 @@ UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { #if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) - DarwinMemoryStickManager memoryStickManager; + DarwinFileSystemServices memoryStickManager; DarwinDirectoryPanelCallback callback = [] (Path thePathChosen) { - DarwinMemoryStickManager::setUserPreferredMemoryStickDirectory(thePathChosen); + DarwinFileSystemServices::setUserPreferredMemoryStickDirectory(thePathChosen); }; memoryStickManager.presentDirectoryPanel(callback); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 3a9bfa515d..ed600b42d3 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -62,6 +62,10 @@ #include "GPU/GPUInterface.h" #include "Common/Data/Text/I18n.h" +#if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(MAC) +#include "UI/DarwinFileSystemServices.h" // For the browser +#endif + #include "Core/HLE/sceUmd.h" bool MainScreen::showHomebrewTab = false; @@ -691,9 +695,15 @@ void GameBrowser::Refresh() { if (System_GetPropertyBool(SYSPROP_HAS_ADDITIONAL_STORAGE)) { topBar->Add(new Choice(ImageID("I_SDCARD"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::StorageClick); } +#if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(MAC) + // on Darwin, we don't show the 'Browse' text alongside the image + // we show just the image, because we don't need to emphasize the button on Darwin + topBar->Add(new Choice(ImageID("I_FOLDER_OPEN"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::BrowseClick); +#else if (System_GetPropertyBool(SYSPROP_HAS_FOLDER_BROWSER)) { topBar->Add(new Choice(mm->T("Browse"), ImageID("I_FOLDER_OPEN"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::BrowseClick); } +#endif } else { topBar->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f))); } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index c65074adf5..1867f3a4b7 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -142,7 +142,7 @@ #endif #if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(MAC) -#include "UI/DarwinMemoryStickManager.h" +#include "UI/DarwinFileSystemServices.h" #endif #include @@ -544,11 +544,11 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #elif PPSSPP_PLATFORM(IOS) g_Config.defaultCurrentDirectory = g_Config.internalDataDirectory; - g_Config.memStickDirectory = DarwinMemoryStickManager::appropriateMemoryStickDirectoryToUse(); + g_Config.memStickDirectory = DarwinFileSystemServices::appropriateMemoryStickDirectoryToUse(); g_Config.flash0Directory = Path(std::string(external_dir)) / "flash0"; #elif PPSSPP_PLATFORM(MAC) g_Config.defaultCurrentDirectory = Path(getenv("HOME")); - g_Config.memStickDirectory = DarwinMemoryStickManager::appropriateMemoryStickDirectoryToUse(); + g_Config.memStickDirectory = DarwinFileSystemServices::appropriateMemoryStickDirectoryToUse(); g_Config.flash0Directory = Path(std::string(external_dir)) / "flash0"; #elif PPSSPP_PLATFORM(SWITCH) g_Config.memStickDirectory = g_Config.internalDataDirectory / "config/ppsspp"; @@ -1260,6 +1260,15 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { else if (msg == "app_resumed" || msg == "got_focus") { // Assume that the user may have modified things. MemoryStick_NotifyWrite(); + } else if (msg == "browse_folder") { + Path thePath = Path(value); + File::FileInfo info; + if (!File::GetFileInfo(thePath, &info)) + return; + if (info.isDirectory) + NativeMessageReceived("browse_folderSelect", thePath.c_str()); + else + NativeMessageReceived("browse_fileSelect", thePath.c_str()); } } diff --git a/ios/main.mm b/ios/main.mm index b10db1a79c..49b35c50b0 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -20,6 +20,7 @@ #include "Common/System/System.h" #include "Common/StringUtils.h" #include "Common/Profiler/Profiler.h" +#include "UI/DarwinFileSystemServices.h" static int (*csops)(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); static boolean_t (*exc_server)(mach_msg_header_t *, mach_msg_header_t *); @@ -201,6 +202,13 @@ void System_SendMessage(const char *command, const char *parameter) { g_safeInsetTop = top; g_safeInsetBottom = bottom; } + } else if (!strcmp(command, "browse_folder")) { + DarwinDirectoryPanelCallback callback = [] (Path thePathChosen) { + NativeMessageReceived("browse_folder", thePathChosen.c_str()); + }; + + DarwinFileSystemServices services; + services.presentDirectoryPanel(callback, /* allowFiles = */ true, /* allowDirectorites = */ true); } }