diff --git a/Common/System/Request.cpp b/Common/System/Request.cpp index 883514d8da..0effd87f1c 100644 --- a/Common/System/Request.cpp +++ b/Common/System/Request.cpp @@ -7,12 +7,14 @@ RequestManager g_requestManager; const char *RequestTypeAsString(SystemRequestType type) { switch (type) { case SystemRequestType::INPUT_TEXT_MODAL: return "INPUT_TEXT_MODAL"; + case SystemRequestType::BROWSE_FOR_IMAGE: return "BROWSE_FOR_IMAGE"; default: return "N/A"; } } bool RequestManager::MakeSystemRequest(SystemRequestType type, RequestCallback callback, const std::string ¶m1, const std::string ¶m2) { int requestId = idCounter_++; + INFO_LOG(SYSTEM, "Making system request %s: id %d, callback_valid %d", RequestTypeAsString(type), requestId, callback != nullptr); if (!System_MakeRequest(type, requestId, param1, param2)) { return false; } @@ -23,6 +25,7 @@ bool RequestManager::MakeSystemRequest(SystemRequestType type, RequestCallback c } std::lock_guard guard(callbackMutex_); + INFO_LOG(SYSTEM, "Registering pending callback %d", requestId); callbackMap_[requestId] = callback; return true; } @@ -41,6 +44,7 @@ void RequestManager::PostSystemSuccess(int requestId, const char *responseString response.responseString = responseString; response.responseValue = responseValue; pendingResponses_.push_back(response); + INFO_LOG(SYSTEM, "PostSystemSuccess: Request %d (%s, %d)", requestId, responseString, responseValue); } void RequestManager::PostSystemFailure(int requestId) { @@ -50,6 +54,7 @@ void RequestManager::PostSystemFailure(int requestId) { ERROR_LOG(SYSTEM, "PostSystemFailure: Unexpected request ID %d", requestId); return; } + INFO_LOG(SYSTEM, "PostSystemFailure: Request %d failed", requestId); callbackMap_.erase(iter); } diff --git a/Common/System/Request.h b/Common/System/Request.h index 19afe99721..99e42eb500 100644 --- a/Common/System/Request.h +++ b/Common/System/Request.h @@ -58,3 +58,7 @@ extern RequestManager g_requestManager; inline void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, RequestCallback callback) { g_requestManager.MakeSystemRequest(SystemRequestType::INPUT_TEXT_MODAL, callback, title, defaultValue); } + +inline void System_BrowseForImage(const std::string &title, RequestCallback callback) { + g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_IMAGE, callback, title, ""); +} diff --git a/Common/System/System.h b/Common/System/System.h index b0222aeea0..dbb79edaab 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -53,6 +53,7 @@ void System_LaunchUrl(LaunchUrlType urlType, const char *url); enum class SystemRequestType { INPUT_TEXT_MODAL, + BROWSE_FOR_IMAGE, }; // Implementations are supposed to process the request, and post the response to the g_RequestManager (see Message.h). diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 767f22d777..0b2781419d 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -490,7 +490,7 @@ void SetBackgroundPopupScreen::update() { File::WriteStringToFile(false, pic->data, bgPng); } - NativeMessageReceived("bgImage_updated", ""); + UIBackgroundShutdown(); // It's worse if it flickers, stay open for at least 1s. timeDone_ = timeStart_ + 1.0; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 43f7ed44a5..85cb888bf6 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1312,12 +1312,17 @@ UI::EventReturn GameSettingsScreen::OnChangeBackground(UI::EventParams &e) { if (File::Exists(bgPng) || File::Exists(bgJpg)) { File::Delete(bgPng); File::Delete(bgJpg); - - NativeMessageReceived("bgImage_updated", ""); + UIBackgroundShutdown(); } else { - if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER)) { - System_SendMessage("bgImage_browse", ""); - } + auto sy = GetI18NCategory("System"); + System_BrowseForImage(sy->T("Set UI background..."), [](const std::string &value, int) { + if (!value.empty()) { + Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / (endsWithNoCase(value, ".jpg") ? "background.jpg" : "background.png"); + File::Copy(Path(value), dest); + } + // It will init again automatically. We can't init outside a frame on Vulkan. + UIBackgroundShutdown(); + }); } // Change to a browse or clear button. diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 35503efce0..ba675e4b29 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1167,14 +1167,6 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { else if (msg == "inputDeviceConnected") { KeyMap::NotifyPadConnected(nextInputDeviceID, value); } - else if (msg == "bgImage_updated") { - if (!value.empty()) { - Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / (endsWithNoCase(value, ".jpg") ? "background.jpg" : "background.png"); - File::Copy(Path(value), dest); - } - UIBackgroundShutdown(); - // It will init again automatically. We can't init outside a frame on Vulkan. - } else if (msg == "savestate_displayslot") { auto sy = GetI18NCategory("System"); std::string msg = StringFromFormat("%s: %d", sy->T("Savestate Slot"), SaveState::GetCurrentSlot() + 1); diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index d81d20b121..a5ede38046 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -1064,10 +1064,6 @@ namespace MainWindow BrowseAndBootDone(); break; - case WM_USER_BROWSE_BG_DONE: - BrowseBackgroundDone(); - break; - case WM_USER_RESTART_EMUTHREAD: NativeSetRestarting(); InputDevice::StopPolling(); diff --git a/Windows/MainWindow.h b/Windows/MainWindow.h index abf170fb84..e1f51c2b2d 100644 --- a/Windows/MainWindow.h +++ b/Windows/MainWindow.h @@ -15,7 +15,6 @@ namespace MainWindow enum { WM_USER_SAVESTATE_FINISH = WM_USER + 100, WM_USER_UPDATE_UI = WM_USER + 101, - WM_USER_BROWSE_BG_DONE = WM_USER + 102, WM_USER_WINDOW_TITLE_CHANGED = WM_USER + 103, WM_USER_BROWSE_BOOT_DONE = WM_USER + 104, WM_USER_TOGGLE_FULLSCREEN = WM_USER + 105, diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 7105f390c9..d4bc5ab3dd 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -347,38 +347,6 @@ namespace MainWindow { browseDialog = 0; } - void BrowseBackground() { - static std::wstring filter = L"All supported images (*.jpg *.jpeg *.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*||"; - for (size_t i = 0; i < filter.length(); i++) { - if (filter[i] == '|') - filter[i] = '\0'; - } - - W32Util::MakeTopMost(GetHWND(), false); - browseImageDialog = new W32Util::AsyncBrowseDialog(W32Util::AsyncBrowseDialog::OPEN, GetHWND(), WM_USER_BROWSE_BG_DONE, L"LoadFile", L"", filter, L"*.jpg;*.jpeg;*.png;"); - } - - void BrowseBackgroundDone() { - std::string filename; - if (browseImageDialog->GetResult(filename)) { - std::wstring src = ConvertUTF8ToWString(filename); - std::wstring dest; - if (filename.size() >= 5 && (filename.substr(filename.size() - 4) == ".jpg" || filename.substr(filename.size() - 5) == ".jpeg")) { - dest = (GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg").ToWString(); - } else { - dest = (GetSysDirectory(DIRECTORY_SYSTEM) / "background.png").ToWString(); - } - - CopyFileW(src.c_str(), dest.c_str(), FALSE); - NativeMessageReceived("bgImage_updated", ""); - } - - W32Util::MakeTopMost(GetHWND(), g_Config.bTopMost); - - delete browseImageDialog; - browseImageDialog = nullptr; - } - static void UmdSwitchAction() { std::string fn; std::string filter = "PSP ROMs (*.iso *.cso *.pbp *.elf)|*.pbp;*.elf;*.iso;*.cso;*.prx|All files (*.*)|*.*||"; diff --git a/Windows/MainWindowMenu.h b/Windows/MainWindowMenu.h index 5669335c22..dc194855e0 100644 --- a/Windows/MainWindowMenu.h +++ b/Windows/MainWindowMenu.h @@ -9,8 +9,6 @@ namespace MainWindow { void TranslateMenus(HWND hWnd, HMENU menu); void BrowseAndBoot(std::string defaultPath, bool browseDirectory = false); void BrowseAndBootDone(); - void BrowseBackground(); - void BrowseBackgroundDone(); void setTexScalingMultiplier(int level); void SetIngameMenuItemStates(HMENU menu, const GlobalUIState state); } diff --git a/Windows/main.cpp b/Windows/main.cpp index 15fd6891d0..4314acda19 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -44,6 +44,7 @@ #include "Common/Data/Encoding/Utf8.h" #include "Common/Net/Resolve.h" #include "W32Util/DarkMode.h" +#include "W32Util/ShellUtil.h" #include "Core/Config.h" #include "Core/ConfigValues.h" @@ -110,8 +111,10 @@ static std::string restartArgs; int g_activeWindow = 0; -static std::thread g_inputBoxThread; -static bool g_inputBoxRunning = false; +// Used for all the system dialogs. +static std::thread g_dialogThread; +static bool g_dialogRunning = false; + int g_lastNumInstances = 0; void System_ShowFileInFolder(const char *path) { @@ -430,15 +433,23 @@ void System_Notify(SystemNotification notification) { } } +std::wstring MakeFilter(std::wstring filter) { + for (size_t i = 0; i < filter.length(); i++) { + if (filter[i] == '|') + filter[i] = '\0'; + } + return filter; +} + bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2) { switch (type) { case SystemRequestType::INPUT_TEXT_MODAL: - if (g_inputBoxRunning) { - g_inputBoxThread.join(); + if (g_dialogRunning) { + g_dialogThread.join(); } - g_inputBoxRunning = true; - g_inputBoxThread = std::thread([=] { + g_dialogRunning = true; + g_dialogThread = std::thread([=] { std::string out; if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), ConvertUTF8ToWString(param1).c_str(), param2, out)) { g_requestManager.PostSystemSuccess(requestId, out.c_str()); @@ -447,6 +458,22 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string } }); return true; + case SystemRequestType::BROWSE_FOR_IMAGE: + if (g_dialogRunning) { + g_dialogThread.join(); + } + + g_dialogRunning = true; + g_dialogThread = std::thread([=] { + std::string out; + if (W32Util::BrowseForFileName(true, MainWindow::GetHWND(), ConvertUTF8ToWString(param1).c_str(), nullptr, + MakeFilter(L"All supported images (*.jpg *.jpeg *.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*||").c_str(), L"jpg", out)) { + g_requestManager.PostSystemSuccess(requestId, out.c_str()); + } else { + g_requestManager.PostSystemFailure(requestId); + } + }); + return true; default: return false; } @@ -483,8 +510,6 @@ void System_SendMessage(const char *command, const char *parameter) { std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), mm->T("Choose folder")); if (folder.size()) NativeMessageReceived("browse_folderSelect", folder.c_str()); - } else if (!strcmp(command, "bgImage_browse")) { - MainWindow::BrowseBackground(); } else if (!strcmp(command, "toggle_fullscreen")) { bool flag = !MainWindow::IsFullscreen(); if (strcmp(parameter, "0") == 0) { @@ -627,9 +652,9 @@ static void WinMainInit() { } static void WinMainCleanup() { - if (g_inputBoxRunning) { - g_inputBoxThread.join(); - g_inputBoxRunning = false; + if (g_dialogRunning) { + g_dialogThread.join(); + g_dialogRunning = false; } net::Shutdown(); CoUninitialize(); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 6a9e42a829..60a4aa46d7 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -539,8 +539,6 @@ bool System_GetPropertyBool(SystemProperty prop) { } void System_Notify(SystemNotification notification) { - switch (notification) { - } } std::string Android_GetInputDeviceDebugString() { @@ -1037,33 +1035,29 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string PushCommand("inputbox", serialized.c_str()); return true; } + case SystemRequestType::BROWSE_FOR_IMAGE: + PushCommand("browse_image", StringFromFormat("%d", requestId)); + return true; default: return false; } } -extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendInputBox(JNIEnv *env, jclass, jstring jseqID, jboolean result, jstring jvalue) { - std::string seqID = GetJavaString(env, jseqID); +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendRequestResult(JNIEnv *env, jclass, jint jrequestID, jboolean result, jstring jvalue, jint jintValue) { std::string value = GetJavaString(env, jvalue); - static std::string lastSeqID = ""; - if (lastSeqID == seqID) { + static jint lastSeqID = -1; + if (lastSeqID == jrequestID) { // We send this on dismiss, so twice in many cases. - DEBUG_LOG(SYSTEM, "Ignoring duplicate sendInputBox"); - return; - } - lastSeqID = seqID; - - int seq = 0; - if (!TryParse(seqID, &seq)) { - ERROR_LOG(SYSTEM, "Invalid inputbox seqID value: %s", seqID.c_str()); + WARN_LOG(SYSTEM, "Ignoring duplicate sendInputBox"); return; } + lastSeqID = jrequestID; if (result) { - g_requestManager.PostSystemSuccess(seq, value.c_str()); + g_requestManager.PostSystemSuccess(jrequestID, value.c_str()); } else { - g_requestManager.PostSystemFailure(seq); + g_requestManager.PostSystemFailure(jrequestID); } } @@ -1225,7 +1219,7 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendMessage(JNIEnv *env } else if (msg == "sustained_perf_supported") { sustainedPerfSupported = true; } else if (msg == "safe_insets") { - INFO_LOG(SYSTEM, "Got insets: %s", prm.c_str()); + // INFO_LOG(SYSTEM, "Got insets: %s", prm.c_str()); // We don't bother with supporting exact rectangular regions. Safe insets are good enough. int left, right, top, bottom; if (4 == sscanf(prm.c_str(), "%d:%d:%d:%d", &left, &right, &top, &bottom)) { @@ -1316,9 +1310,7 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setSatInfoAndroid(JNIEn GPS::setSatInfo(index, id, elevation, azimuth, snr, good); } -extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImageAndroid(JNIEnv *env, jclass, - jbyteArray image) { - +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImageAndroid(JNIEnv *env, jclass, jbyteArray image) { if (image != NULL) { jlong size = env->GetArrayLength(image); jbyte* buffer = env->GetByteArrayElements(image, NULL); diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 2a40786d35..5a249cc702 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -105,6 +105,8 @@ public abstract class NativeActivity extends Activity { private static final int RESULT_OPEN_DOCUMENT = 2; private static final int RESULT_OPEN_DOCUMENT_TREE = 3; + private int imageRequestId = -1; + // Allow for multiple connected gamepads but just consider them the same for now. // Actually this is not entirely true, see the code. private ArrayList inputPlayers = new ArrayList(); @@ -1130,15 +1132,16 @@ public abstract class NativeActivity extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (resultCode != RESULT_OK || data == null) { - return; - } if (requestCode == RESULT_LOAD_IMAGE) { + if (resultCode != RESULT_OK || data == null) { + NativeApp.sendRequestResult(imageRequestId, false, "", 0); + return; + } try { Uri selectedImage = data.getData(); if (selectedImage != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - NativeApp.sendMessage("bgImage_updated", selectedImage.toString()); + NativeApp.sendRequestResult(imageRequestId, true, selectedImage.toString(), 0); } else { String[] filePathColumn = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null); @@ -1147,14 +1150,20 @@ public abstract class NativeActivity extends Activity { int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String picturePath = cursor.getString(columnIndex); cursor.close(); - NativeApp.sendMessage("bgImage_updated", picturePath); + NativeApp.sendRequestResult(imageRequestId, true, picturePath, 0); } } + } else { + NativeApp.sendRequestResult(imageRequestId, false, "", 0); } } catch (Exception e) { Log.w(TAG, "Exception receiving image: " + e); } + imageRequestId = -1; } else if (requestCode == RESULT_OPEN_DOCUMENT) { + if (resultCode != RESULT_OK || data == null) { + return; + } Uri selectedFile = data.getData(); if (selectedFile != null) { try { @@ -1171,6 +1180,9 @@ public abstract class NativeActivity extends Activity { NativeApp.sendMessage("browse_fileSelect", selectedFile.toString()); } } else if (requestCode == RESULT_OPEN_DOCUMENT_TREE) { + if (resultCode != RESULT_OK || data == null) { + return; + } Uri selectedDirectoryUri = data.getData(); if (selectedDirectoryUri != null) { String path = selectedDirectoryUri.toString(); @@ -1230,11 +1242,11 @@ public abstract class NativeActivity extends Activity { return bld; } - // The return value is sent to C++ via seqID. - public void inputBox(final String seqID, final String title, String defaultText, String defaultAction) { + // The return value is sent to C++ via requestID. + public void inputBox(final int requestId, final String title, String defaultText, String defaultAction) { // Workaround for issue #13363 to fix Split/Second game start if (isVRDevice()) { - NativeApp.sendInputBox(seqID, false, defaultText); + NativeApp.sendRequestResult(requestId, false, defaultText, 0); return; } @@ -1269,14 +1281,14 @@ public abstract class NativeActivity extends Activity { .setPositiveButton(defaultAction, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface d, int which) { - NativeApp.sendInputBox(seqID, true, input.getText().toString()); + NativeApp.sendRequestResult(requestId, true, input.getText().toString(), 0); d.dismiss(); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface d, int which) { - NativeApp.sendInputBox(seqID, false, ""); + NativeApp.sendRequestResult(requestId, false, "", 0); d.cancel(); } }); @@ -1284,7 +1296,7 @@ public abstract class NativeActivity extends Activity { builder.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface d) { - NativeApp.sendInputBox(seqID, false, ""); + NativeApp.sendRequestResult(requestId, false, "", 0); updateSystemUiVisibility(); } }); @@ -1321,8 +1333,10 @@ public abstract class NativeActivity extends Activity { Log.e(TAG, e.toString()); return false; } - } else if (command.equals("bgImage_browse")) { + } else if (command.equals("browse_image")) { try { + imageRequestId = Integer.parseInt(params); + Log.i(TAG, "image request ID: " + imageRequestId); Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, RESULT_LOAD_IMAGE); return true; @@ -1419,13 +1433,13 @@ public abstract class NativeActivity extends Activity { String title = "Input"; String defString = ""; String[] param = params.split(":@:", 3); - String seqID = param[0]; + int requestID = Integer.parseInt(param[0]); if (param.length > 1 && param[1].length() > 0) title = param[1]; if (param.length > 2) defString = param[2]; - Log.i(TAG, "Launching inputbox: #" + seqID + " " + title + " " + defString); - inputBox(seqID, title, defString, "OK"); + Log.i(TAG, "Launching inputbox: #" + requestID + " " + title + " " + defString); + inputBox(requestID, title, defString, "OK"); return true; } else if (command.equals("vibrate")) { int milliseconds = -1; diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index 65fa15500f..00ee71d57a 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -51,8 +51,7 @@ public class NativeApp { public static native void accelerometer(float x, float y, float z); public static native void sendMessage(String msg, String arg); - public static native void sendInputBox(String seqID, boolean result, String value); - + public static native void sendRequestResult(int seqID, boolean result, String value, int iValue); public static native String queryConfig(String queryName); public static native int getSelectedCamera();