mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Implement image file picker on iOS, various buildfixes
This commit is contained in:
1 parent
de7175cf04
commit
6035ef0652
11 files changed
+162
-45
No files matched your search
@@ -7,6 +7,7 @@
|
||||
#include <string_view>
|
||||
|
||||
#include "Common/System/System.h"
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
class Path;
|
||||
|
||||
@@ -91,9 +92,11 @@ inline void System_InputBoxGetString(RequesterToken token, std::string_view titl
|
||||
}
|
||||
|
||||
// This one will pop up a special image browser if available. You can also pick
|
||||
// images with the file browser below.
|
||||
inline void System_BrowseForImage(RequesterToken token, std::string_view title, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) {
|
||||
g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_IMAGE, token, callback, failedCallback, title, "", 0);
|
||||
// images with the file browser below. If you provide savePath, iOS will be able to
|
||||
// convert from HEIC as needed and then save to that path. If this happens, the intParam will
|
||||
// be set to 1. Other backends will probably ignore it and set the intParam to 0.
|
||||
inline void System_BrowseForImage(RequesterToken token, std::string_view title, Path savePath, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) {
|
||||
g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_IMAGE, token, callback, failedCallback, title, savePath.ToString(), 0);
|
||||
}
|
||||
|
||||
enum class BrowseFileType {
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#include "UI/DevScreens.h"
|
||||
#include "UI/DriverManagerScreen.h"
|
||||
#include "UI/DisplayLayoutScreen.h"
|
||||
#include "UI/GameSettingsScreen.h"
|
||||
#include "UI/OnScreenDisplay.h"
|
||||
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
|
||||
@@ -122,6 +124,7 @@ void DeveloperToolsScreen::CreateGeneralTab(UI::LinearLayout *list) {
|
||||
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
||||
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
||||
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
|
||||
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
|
||||
|
||||
list->Add(new ItemHeader(sy->T("CPU Core")));
|
||||
|
||||
|
||||
+44
-39
@@ -101,7 +101,7 @@ extern AndroidAudioState *g_audioState;
|
||||
#endif
|
||||
|
||||
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
|
||||
static void SetMemStickDirDarwin(int requesterToken) {
|
||||
void SetMemStickDirDarwin(int requesterToken) {
|
||||
auto initialPath = g_Config.memStickDirectory;
|
||||
INFO_LOG(Log::System, "Current path: %s", initialPath.c_str());
|
||||
System_BrowseForFolder(requesterToken, "", initialPath, [](const std::string &value, int) {
|
||||
@@ -1129,7 +1129,7 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) {
|
||||
const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg";
|
||||
if (File::Exists(bgPng) || File::Exists(bgJpg)) {
|
||||
backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Clear UI background")));
|
||||
} else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER)) {
|
||||
} else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER) || System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) {
|
||||
backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Set UI background...")));
|
||||
} else {
|
||||
backgroundChoice_ = nullptr;
|
||||
@@ -1516,49 +1516,54 @@ UI::EventReturn GameSettingsScreen::OnChangeBackground(UI::EventParams &e) {
|
||||
const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg";
|
||||
|
||||
if (File::Exists(bgPng) || File::Exists(bgJpg)) {
|
||||
// The button is in clear mode.
|
||||
File::Delete(bgPng);
|
||||
File::Delete(bgJpg);
|
||||
UIBackgroundShutdown();
|
||||
RecreateViews();
|
||||
} else {
|
||||
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
||||
System_BrowseForImage(GetRequesterToken(), sy->T("Set UI background..."), [=](const std::string &value, int) {
|
||||
if (!value.empty()) {
|
||||
Path path(value);
|
||||
|
||||
// Check the file format. Don't rely on the file extension here due to scoped storage URLs.
|
||||
FILE *f = File::OpenCFile(path, "rb");
|
||||
uint8_t buffer[8];
|
||||
ImageFileType type = ImageFileType::UNKNOWN;
|
||||
if (f != nullptr && 8 == fread(buffer, 1, ARRAY_SIZE(buffer), f)) {
|
||||
type = DetectImageFileType(buffer, ARRAY_SIZE(buffer));
|
||||
}
|
||||
|
||||
std::string filename;
|
||||
switch (type) {
|
||||
case ImageFileType::JPEG:
|
||||
filename = "background.jpg";
|
||||
break;
|
||||
case ImageFileType::PNG:
|
||||
filename = "background.png";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!filename.empty()) {
|
||||
Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / filename;
|
||||
File::Copy(Path(value), dest);
|
||||
} else {
|
||||
g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Only JPG and PNG images are supported"), path.GetFilename(), 5.0);
|
||||
}
|
||||
}
|
||||
// It will init again automatically. We can't init outside a frame on Vulkan.
|
||||
UIBackgroundShutdown();
|
||||
RecreateViews();
|
||||
});
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
||||
System_BrowseForImage(GetRequesterToken(), sy->T("Set UI background..."), bgJpg, [=](const std::string &value, int converted) {
|
||||
if (converted == 1) {
|
||||
// The platform code converted and saved the file to the desired path already.
|
||||
INFO_LOG(Log::UI, "Converted file.");
|
||||
} else if (!value.empty()) {
|
||||
Path path(value);
|
||||
|
||||
// Check the file format. Don't rely on the file extension here due to scoped storage URLs.
|
||||
FILE *f = File::OpenCFile(path, "rb");
|
||||
uint8_t buffer[8];
|
||||
ImageFileType type = ImageFileType::UNKNOWN;
|
||||
if (f != nullptr && 8 == fread(buffer, 1, ARRAY_SIZE(buffer), f)) {
|
||||
type = DetectImageFileType(buffer, ARRAY_SIZE(buffer));
|
||||
}
|
||||
|
||||
std::string filename;
|
||||
switch (type) {
|
||||
case ImageFileType::JPEG:
|
||||
filename = "background.jpg";
|
||||
break;
|
||||
case ImageFileType::PNG:
|
||||
filename = "background.png";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!filename.empty()) {
|
||||
Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / filename;
|
||||
File::Copy(Path(value), dest);
|
||||
} else {
|
||||
g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Only JPG and PNG images are supported"), path.GetFilename(), 5.0);
|
||||
}
|
||||
}
|
||||
// It will init again automatically. We can't init outside a frame on Vulkan.
|
||||
UIBackgroundShutdown();
|
||||
RecreateViews();
|
||||
});
|
||||
|
||||
// Change to a browse or clear button.
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
@@ -210,3 +210,4 @@ private:
|
||||
};
|
||||
|
||||
void TriggerRestart(const char *why, bool editThenRestore, const Path &gamePath);
|
||||
void SetMemStickDirDarwin(int requesterToken);
|
||||
@@ -12,6 +12,8 @@
|
||||
<string>Your microphone may be used to emulate Go!Cam/Talkman, a microphone accessory</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Your local network needs to be accessed by PPSSPP for network multiplayer support.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Access to your photos is needed to allow setting a background image.</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>PPSSPP</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
|
||||
@interface PPSSPPViewControllerGL : GLKViewController <
|
||||
iCadeEventDelegate, LocationHandlerDelegate, CameraFrameDelegate,
|
||||
UIGestureRecognizerDelegate, UIKeyInput, PPSSPPViewController>
|
||||
UIGestureRecognizerDelegate, UIKeyInput, PPSSPPViewController,
|
||||
UIImagePickerControllerDelegate, UINavigationControllerDelegate>
|
||||
@end
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Common/System/System.h"
|
||||
#include "Common/System/OSD.h"
|
||||
#include "Common/System/NativeApp.h"
|
||||
#include "Common/System/Request.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/Thread/ThreadUtil.h"
|
||||
#include "Common/Log.h"
|
||||
@@ -102,6 +103,9 @@ id<PPSSPPViewController> sharedViewController;
|
||||
IOSGLESContext *graphicsContext;
|
||||
LocationHelper *locationHelper;
|
||||
CameraHelper *cameraHelper;
|
||||
|
||||
int imageRequestId;
|
||||
NSString *imageFilename;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) EAGLContext* context;
|
||||
@@ -586,6 +590,47 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) {
|
||||
});
|
||||
}
|
||||
|
||||
- (void)pickPhoto:(NSString *)saveFilename requestId:(int)requestId {
|
||||
imageRequestId = requestId;
|
||||
imageFilename = saveFilename;
|
||||
NSLog(@"Picking photo to save to %@ (id: %d)", saveFilename, requestId);
|
||||
|
||||
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
|
||||
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
|
||||
picker.delegate = self;
|
||||
[self presentViewController:picker animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)imagePickerController:(UIImagePickerController *)picker
|
||||
didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {
|
||||
|
||||
UIImage *image = info[UIImagePickerControllerOriginalImage];
|
||||
|
||||
// Convert to JPEG with 90% quality
|
||||
NSData *jpegData = UIImageJPEGRepresentation(image, 0.9);
|
||||
if (jpegData) {
|
||||
// Do something with the JPEG data (e.g., save to file)
|
||||
[jpegData writeToFile:imageFilename atomically:YES];
|
||||
NSLog(@"Saved JPEG image to %@", imageFilename);
|
||||
g_requestManager.PostSystemSuccess(imageRequestId, "", 1);
|
||||
} else {
|
||||
g_requestManager.PostSystemFailure(imageRequestId);
|
||||
}
|
||||
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
[self hideKeyboard];
|
||||
}
|
||||
|
||||
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
|
||||
NSLog(@"User cancelled image picker");
|
||||
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
|
||||
// You can also call your custom callback or use the requestId here
|
||||
g_requestManager.PostSystemFailure(imageRequestId);
|
||||
[self hideKeyboard];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void bindDefaultFBO()
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
- (void)willResignActive;
|
||||
|
||||
- (void)uiStateChanged;
|
||||
- (void)pickPhoto:(NSString *)saveFilename requestId:(int)requestId;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
|
||||
@interface PPSSPPViewControllerMetal : UIViewController<
|
||||
iCadeEventDelegate, LocationHandlerDelegate, CameraFrameDelegate,
|
||||
UIGestureRecognizerDelegate, UIKeyInput, PPSSPPViewController>
|
||||
UIGestureRecognizerDelegate, UIKeyInput, PPSSPPViewController,
|
||||
UIImagePickerControllerDelegate, UINavigationControllerDelegate>
|
||||
@end
|
||||
|
||||
/** The Metal-compatibile view. */
|
||||
@interface PPSSPPMetalView : UIView
|
||||
@end
|
||||
@end
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "Common/System/System.h"
|
||||
#include "Common/System/OSD.h"
|
||||
#include "Common/System/NativeApp.h"
|
||||
#include "Common/System/Request.h"
|
||||
#include "Common/GraphicsContext.h"
|
||||
#include "Common/Thread/ThreadUtil.h"
|
||||
|
||||
@@ -212,6 +213,9 @@ static std::thread g_renderLoopThread;
|
||||
IOSVulkanContext *graphicsContext;
|
||||
LocationHelper *locationHelper;
|
||||
CameraHelper *cameraHelper;
|
||||
|
||||
int imageRequestId;
|
||||
NSString *imageFilename;
|
||||
}
|
||||
|
||||
@property (nonatomic) GCController *gameController __attribute__((weak_import));
|
||||
@@ -726,6 +730,47 @@ extern float g_safeInsetBottom;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)pickPhoto:(NSString *)saveFilename requestId:(int)requestId {
|
||||
imageRequestId = requestId;
|
||||
imageFilename = saveFilename;
|
||||
NSLog(@"Picking photo to save to %@ (id: %d)", saveFilename, requestId);
|
||||
|
||||
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
|
||||
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
|
||||
picker.delegate = self;
|
||||
[self presentViewController:picker animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)imagePickerController:(UIImagePickerController *)picker
|
||||
didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {
|
||||
|
||||
UIImage *image = info[UIImagePickerControllerOriginalImage];
|
||||
|
||||
// Convert to JPEG with 90% quality
|
||||
NSData *jpegData = UIImageJPEGRepresentation(image, 0.9);
|
||||
if (jpegData) {
|
||||
// Do something with the JPEG data (e.g., save to file)
|
||||
[jpegData writeToFile:imageFilename atomically:YES];
|
||||
NSLog(@"Saved JPEG image to %@", imageFilename);
|
||||
g_requestManager.PostSystemSuccess(imageRequestId, "", 1);
|
||||
} else {
|
||||
g_requestManager.PostSystemFailure(imageRequestId);
|
||||
}
|
||||
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
[self hideKeyboard];
|
||||
}
|
||||
|
||||
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
|
||||
NSLog(@"User cancelled image picker");
|
||||
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
|
||||
// You can also call your custom callback or use the requestId here
|
||||
g_requestManager.PostSystemFailure(imageRequestId);
|
||||
[self hideKeyboard];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation PPSSPPMetalView
|
||||
|
||||
+10
@@ -358,6 +358,8 @@ bool System_GetPropertyBool(SystemProperty prop) {
|
||||
return true;
|
||||
case SYSPROP_HAS_FOLDER_BROWSER:
|
||||
return true;
|
||||
case SYSPROP_HAS_IMAGE_BROWSER:
|
||||
return true;
|
||||
case SYSPROP_HAS_OPEN_DIRECTORY:
|
||||
return false;
|
||||
case SYSPROP_HAS_BACK_BUTTON:
|
||||
@@ -462,6 +464,14 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string
|
||||
DarwinFileSystemServices::presentDirectoryPanel(callback, /* allowFiles = */ false, /* allowDirectories = */ true);
|
||||
return true;
|
||||
}
|
||||
case SystemRequestType::BROWSE_FOR_IMAGE:
|
||||
{
|
||||
NSString *filename = [NSString stringWithUTF8String:param2.c_str()];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[sharedViewController pickPhoto:filename requestId:requestId];
|
||||
});
|
||||
return true;
|
||||
}
|
||||
case SystemRequestType::CAMERA_COMMAND:
|
||||
if (!strncmp(param1.c_str(), "startVideo", 10)) {
|
||||
int width = 0, height = 0;
|
||||
|
||||
Reference in new issue
Block a user