From 1304d0416122390da1b6bd53aaed1991ff28f4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 18 Jan 2024 11:55:39 +0100 Subject: [PATCH] Fix a particular type of race condition in file dialog requests It seems to be possible for a user to back out of a screen before receiving the "dialog completed" callback on Android, in which case things pointed to by the callback might be gone. In this case, it's better to simply not call the callback, rather than crashing. This is accomplished by assigning "Tokens" to screens that cause requests, and in ~Screen, invalidate any pending requests belonging to that token. --- Common/System/Request.cpp | 32 +++++++++++---- Common/System/Request.h | 74 +++++++++++++++++++--------------- Common/UI/PopupScreens.cpp | 18 ++++----- Common/UI/PopupScreens.h | 10 +++-- Common/UI/Screen.cpp | 16 ++++++++ Common/UI/Screen.h | 9 +++-- Core/Dialog/PSPOskDialog.cpp | 2 +- UI/ChatScreen.cpp | 4 +- UI/ChatScreen.h | 4 +- UI/CwCheatScreen.cpp | 2 +- UI/DriverManagerScreen.cpp | 2 +- UI/EmuScreen.cpp | 2 +- UI/GameSettingsScreen.cpp | 38 ++++++++--------- UI/MainScreen.cpp | 26 ++++++------ UI/MainScreen.h | 3 +- UI/MemStickScreen.cpp | 4 +- UI/RemoteISOScreen.cpp | 8 ++-- UI/RetroAchievementScreens.cpp | 16 ++++---- UI/SavedataScreen.cpp | 2 +- UI/TabbedDialogScreen.cpp | 2 +- Windows/MainWindowMenu.cpp | 18 ++++----- Windows/MainWindowMenu.h | 4 +- headless/Headless.cpp | 1 - libretro/libretro.cpp | 1 - 24 files changed, 174 insertions(+), 124 deletions(-) diff --git a/Common/System/Request.cpp b/Common/System/Request.cpp index 1635152a24f2..2ad6f27e10b8 100644 --- a/Common/System/Request.cpp +++ b/Common/System/Request.cpp @@ -31,13 +31,21 @@ const char *RequestTypeAsString(SystemRequestType type) { } } -bool RequestManager::MakeSystemRequest(SystemRequestType type, RequestCallback callback, RequestFailedCallback failedCallback, const std::string ¶m1, const std::string ¶m2, int param3) { +bool RequestManager::MakeSystemRequest(SystemRequestType type, RequesterToken token, RequestCallback callback, RequestFailedCallback failedCallback, const std::string ¶m1, const std::string ¶m2, int param3) { + if (token == NO_REQUESTER_TOKEN) { + _dbg_assert_(!callback); + _dbg_assert_(!failedCallback); + } + if (callback || failedCallback) { + _dbg_assert_(token != NO_REQUESTER_TOKEN); + } + int requestId = idCounter_++; // NOTE: We need to register immediately, in order to support synchronous implementations. if (callback || failedCallback) { std::lock_guard guard(callbackMutex_); - callbackMap_[requestId] = { callback, failedCallback }; + callbackMap_[requestId] = { callback, failedCallback, token }; } VERBOSE_LOG(SYSTEM, "Making system request %s: id %d", RequestTypeAsString(type), requestId); @@ -52,6 +60,16 @@ bool RequestManager::MakeSystemRequest(SystemRequestType type, RequestCallback c return true; } +void RequestManager::ForgetRequestsWithToken(RequesterToken token) { + for (auto &iter : callbackMap_) { + if (iter.second.token == token) { + INFO_LOG(SYSTEM, "Forgetting about requester with token %d", token); + iter.second.callback = nullptr; + iter.second.failedCallback = nullptr; + } + } +} + void RequestManager::PostSystemSuccess(int requestId, const char *responseString, int responseValue) { std::lock_guard guard(callbackMutex_); auto iter = callbackMap_.find(requestId); @@ -82,7 +100,7 @@ void RequestManager::PostSystemFailure(int requestId) { std::lock_guard responseGuard(responseMutex_); PendingFailure response; - response.callback = iter->second.failedCallback; + response.failedCallback = iter->second.failedCallback; pendingFailures_.push_back(response); callbackMap_.erase(iter); } @@ -96,8 +114,8 @@ void RequestManager::ProcessRequests() { } pendingSuccesses_.clear(); for (auto &iter : pendingFailures_) { - if (iter.callback) { - iter.callback(); + if (iter.failedCallback) { + iter.failedCallback(); } } pendingFailures_.clear(); @@ -113,9 +131,9 @@ void RequestManager::Clear() { } void System_CreateGameShortcut(const Path &path, const std::string &title) { - g_requestManager.MakeSystemRequest(SystemRequestType::CREATE_GAME_SHORTCUT, nullptr, nullptr, path.ToString(), title, 0); + g_requestManager.MakeSystemRequest(SystemRequestType::CREATE_GAME_SHORTCUT, NO_REQUESTER_TOKEN, nullptr, nullptr, path.ToString(), title, 0); } void System_ShowFileInFolder(const Path &path) { - g_requestManager.MakeSystemRequest(SystemRequestType::SHOW_FILE_IN_FOLDER, nullptr, nullptr, path.ToString(), "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::SHOW_FILE_IN_FOLDER, NO_REQUESTER_TOKEN, nullptr, nullptr, path.ToString(), "", 0); } diff --git a/Common/System/Request.h b/Common/System/Request.h index 314dd7f88a03..3cd4f2a3be2f 100644 --- a/Common/System/Request.h +++ b/Common/System/Request.h @@ -12,6 +12,10 @@ class Path; typedef std::function RequestCallback; typedef std::function RequestFailedCallback; +typedef int RequesterToken; +#define NO_REQUESTER_TOKEN -1 +#define NON_EPHEMERAL_TOKEN -2 + // Platforms often have to process requests asynchronously, on wildly different threads, // and then somehow pass a response back to the main thread (especially Android...) // This acts as bridge and buffer. @@ -23,7 +27,7 @@ class RequestManager { // The callback you pass in will be called on the main thread later. // Params are at the end since it's the part most likely to recieve additions in the future, // now that we have both callbacks. - bool MakeSystemRequest(SystemRequestType type, RequestCallback callback, RequestFailedCallback failedCallback, const std::string ¶m1, const std::string ¶m2, int param3); + bool MakeSystemRequest(SystemRequestType type, RequesterToken token, RequestCallback callback, RequestFailedCallback failedCallback, const std::string ¶m1, const std::string ¶m2, int param3); // Called by the platform implementation, when it's finished with a request. void PostSystemSuccess(int requestId, const char *responseString, int responseValue = 0); @@ -33,19 +37,21 @@ class RequestManager { // This will call the callback of any finished requests. void ProcessRequests(); + RequesterToken GenerateRequesterToken() { + int token = tokenGen_++; + return token; + } + + void ForgetRequestsWithToken(RequesterToken token); + // Unclear if we need this... void Clear(); private: - struct PendingRequest { - SystemRequestType type; - RequestCallback callback; - RequestFailedCallback failedCallback; - }; - struct CallbackPair { RequestCallback callback; RequestFailedCallback failedCallback; + RequesterToken token; }; std::map callbackMap_; @@ -58,7 +64,7 @@ class RequestManager { }; struct PendingFailure { - RequestFailedCallback callback; + RequestFailedCallback failedCallback; }; // Let's start at 10 to get a recognizably valid ID in logs. @@ -66,6 +72,8 @@ class RequestManager { std::vector pendingSuccesses_; std::vector pendingFailures_; std::mutex responseMutex_; + + RequesterToken tokenGen_ = 20000; }; const char *RequestTypeAsString(SystemRequestType type); @@ -75,14 +83,14 @@ extern RequestManager g_requestManager; // Wrappers for easy requests. // NOTE: Semantics have changed - this no longer calls the callback on cancellation, instead you // can specify a different callback for that. -inline void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { - g_requestManager.MakeSystemRequest(SystemRequestType::INPUT_TEXT_MODAL, callback, failedCallback, title, defaultValue, 0); +inline void System_InputBoxGetString(RequesterToken token, const std::string &title, const std::string &defaultValue, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { + g_requestManager.MakeSystemRequest(SystemRequestType::INPUT_TEXT_MODAL, token, callback, failedCallback, title, defaultValue, 0); } // 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(const std::string &title, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { - g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_IMAGE, callback, failedCallback, title, "", 0); +inline void System_BrowseForImage(RequesterToken token, const std::string &title, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { + g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_IMAGE, token, callback, failedCallback, title, "", 0); } enum class BrowseFileType { @@ -95,78 +103,78 @@ enum class BrowseFileType { ANY, }; -inline void System_BrowseForFile(const std::string &title, BrowseFileType type, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { - g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_FILE, callback, failedCallback, title, "", (int)type); +inline void System_BrowseForFile(RequesterToken token, const std::string &title, BrowseFileType type, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { + g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_FILE, token, callback, failedCallback, title, "", (int)type); } -inline void System_BrowseForFolder(const std::string &title, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { - g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_FOLDER, callback, failedCallback, title, "", 0); +inline void System_BrowseForFolder(RequesterToken token, const std::string &title, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { + g_requestManager.MakeSystemRequest(SystemRequestType::BROWSE_FOR_FOLDER, token, callback, failedCallback, title, "", 0); } // The returned string is username + '\n' + password. -inline void System_AskUsernamePassword(const std::string &title, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { - g_requestManager.MakeSystemRequest(SystemRequestType::ASK_USERNAME_PASSWORD, callback, failedCallback, title, "", 0); +inline void System_AskUsernamePassword(RequesterToken token, const std::string &title, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { + g_requestManager.MakeSystemRequest(SystemRequestType::ASK_USERNAME_PASSWORD, token, callback, failedCallback, title, "", 0); } inline void System_CopyStringToClipboard(const std::string &string) { - g_requestManager.MakeSystemRequest(SystemRequestType::COPY_TO_CLIPBOARD, nullptr, nullptr, string, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::COPY_TO_CLIPBOARD, NO_REQUESTER_TOKEN, nullptr, nullptr, string, "", 0); } inline void System_ExitApp() { - g_requestManager.MakeSystemRequest(SystemRequestType::EXIT_APP, nullptr, nullptr, "", "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::EXIT_APP, NO_REQUESTER_TOKEN, nullptr, nullptr, "", "", 0); } inline void System_RestartApp(const std::string ¶ms) { - g_requestManager.MakeSystemRequest(SystemRequestType::RESTART_APP, nullptr, nullptr, params, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::RESTART_APP, NO_REQUESTER_TOKEN, nullptr, nullptr, params, "", 0); } inline void System_RecreateActivity() { - g_requestManager.MakeSystemRequest(SystemRequestType::RECREATE_ACTIVITY, nullptr, nullptr, "", "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::RECREATE_ACTIVITY, NO_REQUESTER_TOKEN, nullptr, nullptr, "", "", 0); } // The design is a little weird, just a holdover from the old message. Can either toggle or set to on or off. inline void System_ToggleFullscreenState(const std::string ¶m) { - g_requestManager.MakeSystemRequest(SystemRequestType::TOGGLE_FULLSCREEN_STATE, nullptr, nullptr, param, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::TOGGLE_FULLSCREEN_STATE, NO_REQUESTER_TOKEN, nullptr, nullptr, param, "", 0); } inline void System_GraphicsBackendFailedAlert(const std::string ¶m) { - g_requestManager.MakeSystemRequest(SystemRequestType::GRAPHICS_BACKEND_FAILED_ALERT, nullptr, nullptr, param, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::GRAPHICS_BACKEND_FAILED_ALERT, NO_REQUESTER_TOKEN, nullptr, nullptr, param, "", 0); } inline void System_CameraCommand(const std::string &command) { - g_requestManager.MakeSystemRequest(SystemRequestType::CAMERA_COMMAND, nullptr, nullptr, command, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::CAMERA_COMMAND, NO_REQUESTER_TOKEN, nullptr, nullptr, command, "", 0); } inline void System_GPSCommand(const std::string &command) { - g_requestManager.MakeSystemRequest(SystemRequestType::GPS_COMMAND, nullptr, nullptr, command, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::GPS_COMMAND, NO_REQUESTER_TOKEN, nullptr, nullptr, command, "", 0); } inline void System_InfraredCommand(const std::string &command) { - g_requestManager.MakeSystemRequest(SystemRequestType::INFRARED_COMMAND, nullptr, nullptr, command, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::INFRARED_COMMAND, NO_REQUESTER_TOKEN, nullptr, nullptr, command, "", 0); } inline void System_MicrophoneCommand(const std::string &command) { - g_requestManager.MakeSystemRequest(SystemRequestType::MICROPHONE_COMMAND, nullptr, nullptr, command, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::MICROPHONE_COMMAND, NO_REQUESTER_TOKEN, nullptr, nullptr, command, "", 0); } inline void System_ShareText(const std::string &text) { - g_requestManager.MakeSystemRequest(SystemRequestType::SHARE_TEXT, nullptr, nullptr, text, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::SHARE_TEXT, NO_REQUESTER_TOKEN, nullptr, nullptr, text, "", 0); } inline void System_NotifyUIState(const std::string &state) { - g_requestManager.MakeSystemRequest(SystemRequestType::NOTIFY_UI_STATE, nullptr, nullptr, state, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::NOTIFY_UI_STATE, NO_REQUESTER_TOKEN, nullptr, nullptr, state, "", 0); } inline void System_SetWindowTitle(const std::string ¶m) { - g_requestManager.MakeSystemRequest(SystemRequestType::SET_WINDOW_TITLE, nullptr, nullptr, param, "", 0); + g_requestManager.MakeSystemRequest(SystemRequestType::SET_WINDOW_TITLE, NO_REQUESTER_TOKEN, nullptr, nullptr, param, "", 0); } inline bool System_SendDebugOutput(const std::string &string) { - return g_requestManager.MakeSystemRequest(SystemRequestType::SEND_DEBUG_OUTPUT, nullptr, nullptr, string, "", 0); + return g_requestManager.MakeSystemRequest(SystemRequestType::SEND_DEBUG_OUTPUT, NO_REQUESTER_TOKEN, nullptr, nullptr, string, "", 0); } inline void System_SendDebugScreenshot(const std::string &data, int height) { - g_requestManager.MakeSystemRequest(SystemRequestType::SEND_DEBUG_SCREENSHOT, nullptr, nullptr, data, "", height); + g_requestManager.MakeSystemRequest(SystemRequestType::SEND_DEBUG_SCREENSHOT, NO_REQUESTER_TOKEN, nullptr, nullptr, data, "", height); } // Non-inline to avoid including Path.h diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index d586d7c931a9..f4110d9d7df9 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -500,8 +500,8 @@ void SliderFloatPopupScreen::OnCompleted(DialogResult result) { } } -PopupTextInputChoice::PopupTextInputChoice(std::string *value, const std::string &title, const std::string &placeholder, int maxLen, ScreenManager *screenManager, LayoutParams *layoutParams) - : AbstractChoiceWithValueDisplay(title, layoutParams), screenManager_(screenManager), value_(value), placeHolder_(placeholder), maxLen_(maxLen) { +PopupTextInputChoice::PopupTextInputChoice(RequesterToken token, std::string *value, const std::string &title, const std::string &placeholder, int maxLen, ScreenManager *screenManager, LayoutParams *layoutParams) + : AbstractChoiceWithValueDisplay(title, layoutParams), screenManager_(screenManager), value_(value), placeHolder_(placeholder), maxLen_(maxLen), token_(token) { OnClick.Handle(this, &PopupTextInputChoice::HandleClick); } @@ -510,7 +510,7 @@ EventReturn PopupTextInputChoice::HandleClick(EventParams &e) { // Choose method depending on platform capabilities. if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) { - System_InputBoxGetString(text_, *value_ , [=](const std::string &enteredValue, int) { + System_InputBoxGetString(token_, text_, *value_ , [=](const std::string &enteredValue, int) { *value_ = StripSpaces(enteredValue); EventParams params{}; OnChange.Trigger(params); @@ -668,10 +668,10 @@ std::string ChoiceWithValueDisplay::ValueText() const { return valueText.str(); } -FileChooserChoice::FileChooserChoice(std::string *value, const std::string &text, BrowseFileType fileType, LayoutParams *layoutParams) - : AbstractChoiceWithValueDisplay(text, layoutParams), value_(value), fileType_(fileType) { +FileChooserChoice::FileChooserChoice(RequesterToken token, std::string *value, const std::string &text, BrowseFileType fileType, LayoutParams *layoutParams) + : AbstractChoiceWithValueDisplay(text, layoutParams), value_(value), fileType_(fileType), token_(token) { OnClick.Add([=](UI::EventParams &) { - System_BrowseForFile(text_, fileType, [=](const std::string &returnValue, int) { + System_BrowseForFile(token, text_, fileType, [=](const std::string &returnValue, int) { if (*value_ != returnValue) { *value = returnValue; UI::EventParams e{}; @@ -692,10 +692,10 @@ std::string FileChooserChoice::ValueText() const { return path.GetFilename(); } -FolderChooserChoice::FolderChooserChoice(std::string *value, const std::string &text, LayoutParams *layoutParams) - : AbstractChoiceWithValueDisplay(text, layoutParams), value_(value) { +FolderChooserChoice::FolderChooserChoice(RequesterToken token, std::string *value, const std::string &text, LayoutParams *layoutParams) + : AbstractChoiceWithValueDisplay(text, layoutParams), value_(value), token_(token) { OnClick.Add([=](UI::EventParams &) { - System_BrowseForFolder(text_, [=](const std::string &returnValue, int) { + System_BrowseForFolder(token_, text_, [=](const std::string &returnValue, int) { if (*value_ != returnValue) { *value = returnValue; UI::EventParams e{}; diff --git a/Common/UI/PopupScreens.h b/Common/UI/PopupScreens.h index 213206b6bc35..ee5a36272ff6 100644 --- a/Common/UI/PopupScreens.h +++ b/Common/UI/PopupScreens.h @@ -360,7 +360,7 @@ class PopupSliderChoiceFloat : public AbstractChoiceWithValueDisplay { // NOTE: This one will defer to a system-native dialog if possible. class PopupTextInputChoice : public AbstractChoiceWithValueDisplay { public: - PopupTextInputChoice(std::string *value, const std::string &title, const std::string &placeholder, int maxLen, ScreenManager *screenManager, LayoutParams *layoutParams = 0); + PopupTextInputChoice(RequesterToken token, std::string *value, const std::string &title, const std::string &placeholder, int maxLen, ScreenManager *screenManager, LayoutParams *layoutParams = 0); Event OnChange; @@ -370,6 +370,7 @@ class PopupTextInputChoice : public AbstractChoiceWithValueDisplay { private: EventReturn HandleClick(EventParams &e); EventReturn HandleChange(EventParams &e); + RequesterToken token_; ScreenManager *screenManager_; std::string *value_; std::string placeHolder_; @@ -405,7 +406,7 @@ enum class FileChooserFileType { class FileChooserChoice : public AbstractChoiceWithValueDisplay { public: - FileChooserChoice(std::string *value, const std::string &title, BrowseFileType fileType, LayoutParams *layoutParams = nullptr); + FileChooserChoice(RequesterToken token, std::string *value, const std::string &title, BrowseFileType fileType, LayoutParams *layoutParams = nullptr); std::string ValueText() const override; Event OnChange; @@ -413,11 +414,12 @@ class FileChooserChoice : public AbstractChoiceWithValueDisplay { private: std::string *value_; BrowseFileType fileType_; + RequesterToken token_; }; class FolderChooserChoice : public AbstractChoiceWithValueDisplay { public: - FolderChooserChoice(std::string *value, const std::string &title, LayoutParams *layoutParams = nullptr); + FolderChooserChoice(RequesterToken token, std::string *value, const std::string &title, LayoutParams *layoutParams = nullptr); std::string ValueText() const override; Event OnChange; @@ -425,7 +427,7 @@ class FolderChooserChoice : public AbstractChoiceWithValueDisplay { private: std::string *value_; BrowseFileType fileType_; + RequesterToken token_; }; - } // namespace UI diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 657aaddcca5b..12c360d6f5f5 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -1,4 +1,5 @@ #include "Common/System/Display.h" +#include "Common/System/Request.h" #include "Common/Input/InputState.h" #include "Common/UI/Root.h" #include "Common/UI/Screen.h" @@ -21,6 +22,21 @@ void Screen::focusChanged(ScreenFocusChange focusChange) { DEBUG_LOG(SYSTEM, "Screen %s got %s", this->tag(), eventName); } +int Screen::GetRequesterToken() { + if (token_ < 0) { + token_ = g_requestManager.GenerateRequesterToken(); + } + return token_; +} + +Screen::~Screen() { + screenManager_ = nullptr; + if (token_ >= 0) { + // To avoid expired callbacks getting called. + g_requestManager.ForgetRequestsWithToken(token_); + } +} + ScreenManager::~ScreenManager() { shutdown(); } diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index e1f7bf8298cb..de40bae6848c 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -64,9 +64,7 @@ ENUM_CLASS_BITOPS(ScreenRenderFlags); class Screen { public: Screen() : screenManager_(nullptr) { } - virtual ~Screen() { - screenManager_ = nullptr; - } + virtual ~Screen(); virtual void onFinish(DialogResult reason) {} virtual void update() {} @@ -100,8 +98,13 @@ class Screen { virtual TouchInput transformTouch(const TouchInput &touch) { return touch; } +protected: + int GetRequesterToken(); + private: ScreenManager *screenManager_; + int token_ = -1; + DISALLOW_COPY_AND_ASSIGN(Screen); }; diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index 555d638320bd..025d701d05cc 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -897,7 +897,7 @@ int PSPOskDialog::NativeKeyboard() { defaultText.assign(u"VALUE"); // There's already ConvertUCS2ToUTF8 in this file. Should we use that instead of the global ones? - System_InputBoxGetString(::ConvertUCS2ToUTF8(titleText), ::ConvertUCS2ToUTF8(defaultText), + System_InputBoxGetString(NON_EPHEMERAL_TOKEN, ::ConvertUCS2ToUTF8(titleText), ::ConvertUCS2ToUTF8(defaultText), [&](const std::string &value, int) { // Success callback std::lock_guard guard(nativeMutex_); diff --git a/UI/ChatScreen.cpp b/UI/ChatScreen.cpp index 4d4faa882a36..1a79eaee06bb 100644 --- a/UI/ChatScreen.cpp +++ b/UI/ChatScreen.cpp @@ -97,7 +97,7 @@ UI::EventReturn ChatMenu::OnSubmit(UI::EventParams &e) { sendChat(chat); #elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(n->T("Chat"), "", [](const std::string &value, int) { + System_InputBoxGetString(token_, n->T("Chat"), "", [](const std::string &value, int) { sendChat(value); }); #endif @@ -180,7 +180,7 @@ void ChatMenu::Update() { // Could remove the fullscreen check here, it works now. auto n = GetI18NCategory(I18NCat::NETWORKING); if (promptInput_ && g_Config.bBypassOSKWithKeyboard && !g_Config.UseFullScreen()) { - System_InputBoxGetString(n->T("Chat"), n->T("Chat Here"), [](const std::string &value, int) { + System_InputBoxGetString(token_, n->T("Chat"), n->T("Chat Here"), [](const std::string &value, int) { sendChat(value); }); promptInput_ = false; diff --git a/UI/ChatScreen.h b/UI/ChatScreen.h index b068ddb1f075..5c2bee05324e 100644 --- a/UI/ChatScreen.h +++ b/UI/ChatScreen.h @@ -5,7 +5,8 @@ class ChatMenu : public UI::AnchorLayout { public: - ChatMenu(const Bounds &screenBounds, UI::LayoutParams *lp = nullptr): UI::AnchorLayout(lp) { + ChatMenu(int token, const Bounds &screenBounds, UI::LayoutParams *lp = nullptr) + : UI::AnchorLayout(lp), token_(token) { CreateSubviews(screenBounds); } void Update() override; @@ -41,4 +42,5 @@ class ChatMenu : public UI::AnchorLayout { int chatChangeID_ = 0; bool toBottom_ = true; bool promptInput_ = false; + int token_; }; diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 1b63895c95ac..0fe6eb040759 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -206,7 +206,7 @@ static char *GetLineNoNewline(char *temp, int sz, FILE *fp) { } UI::EventReturn CwCheatScreen::OnImportBrowse(UI::EventParams ¶ms) { - System_BrowseForFile("Open cheat DB file", BrowseFileType::DB, [&](const std::string &value, int) { + System_BrowseForFile(GetRequesterToken(), "Open cheat DB file", BrowseFileType::DB, [&](const std::string &value, int) { Path path(value); INFO_LOG(SYSTEM, "Attempting to load cheats from: '%s'", path.ToVisualString().c_str()); if (ImportCheats(path)) { diff --git a/UI/DriverManagerScreen.cpp b/UI/DriverManagerScreen.cpp index 67a0148a344c..e2f037cdbd89 100644 --- a/UI/DriverManagerScreen.cpp +++ b/UI/DriverManagerScreen.cpp @@ -212,7 +212,7 @@ UI::EventReturn DriverManagerScreen::OnCustomDriverUninstall(UI::EventParams &e) UI::EventReturn DriverManagerScreen::OnCustomDriverInstall(UI::EventParams &e) { auto gr = GetI18NCategory(I18NCat::GRAPHICS); - System_BrowseForFile(gr->T("Install custom driver..."), BrowseFileType::ZIP, [this](const std::string &value, int) { + System_BrowseForFile(GetRequesterToken(), gr->T("Install custom driver..."), BrowseFileType::ZIP, [this](const std::string &value, int) { if (value.empty()) { return; } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 6541c1dbe43d..f4e58c513e65 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1039,7 +1039,7 @@ void EmuScreen::CreateViews() { root_->Add(btn)->OnClick.Handle(this, &EmuScreen::OnChat); chatButton_ = btn; } - chatMenu_ = root_->Add(new ChatMenu(screenManager()->getUIContext()->GetBounds(), new LayoutParams(FILL_PARENT, FILL_PARENT))); + chatMenu_ = root_->Add(new ChatMenu(GetRequesterToken(), screenManager()->getUIContext()->GetBounds(), new LayoutParams(FILL_PARENT, FILL_PARENT))); chatMenu_->SetVisibility(UI::V_GONE); } else { chatButton_ = nullptr; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 5593b57c5816..a206b091cca0 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -805,12 +805,12 @@ void GameSettingsScreen::CreateControlsSettings(UI::ViewGroup *controlsSettings) // Compound view just like the audio file choosers class MacAddressChooser : public UI::LinearLayout { public: - MacAddressChooser(Path gamePath, std::string *value, const std::string &title, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr); + MacAddressChooser(RequesterToken token, Path gamePath, std::string *value, const std::string &title, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr); }; static constexpr UI::Size ITEM_HEIGHT = 64.f; -MacAddressChooser::MacAddressChooser(Path gamePath_, std::string *value, const std::string &title, ScreenManager *screenManager, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams) { +MacAddressChooser::MacAddressChooser(RequesterToken token, Path gamePath_, std::string *value, const std::string &title, ScreenManager *screenManager, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams) { using namespace UI; SetSpacing(5.0f); if (!layoutParams) { @@ -820,7 +820,7 @@ MacAddressChooser::MacAddressChooser(Path gamePath_, std::string *value, const s auto n = GetI18NCategory(I18NCat::NETWORKING); std::string initialValue = *value; - Add(new PopupTextInputChoice(value, title, g_Config.sMACAddress, 17, screenManager, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) { + Add(new PopupTextInputChoice(token, value, title, g_Config.sMACAddress, 17, screenManager, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) { // Validate the chosen address, and restore to initialValue if bad. if (g_Config.sMACAddress.size() != 17) { // TODO: Alert the user @@ -860,7 +860,7 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti networkingSettings->Add(new Choice(n->T("Open PPSSPP Multiplayer Wiki Page")))->OnClick.Handle(this, &GameSettingsScreen::OnAdhocGuides); networkingSettings->Add(new CheckBox(&g_Config.bEnableWlan, n->T("Enable networking", "Enable networking/wlan (beta)"))); - networkingSettings->Add(new MacAddressChooser(gamePath_, &g_Config.sMACAddress, n->T("Change Mac Address"), screenManager())); + networkingSettings->Add(new MacAddressChooser(GetRequesterToken(), gamePath_, &g_Config.sMACAddress, n->T("Change Mac Address"), screenManager())); static const char* wlanChannels[] = { "Auto", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" }; auto wlanChannelChoice = networkingSettings->Add(new PopupMultiChoice(&g_Config.iWlanAdhocChannel, n->T("WLAN Channel"), wlanChannels, 0, ARRAY_SIZE(wlanChannels), I18NCat::NETWORKING, screenManager())); for (int i = 0; i < 4; i++) { @@ -897,11 +897,11 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti #endif #if !defined(MOBILE_DEVICE) && !defined(USING_QT_UI) // TODO: Add all platforms where KEY_CHAR support is added - PopupTextInputChoice *qc1 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat0, n->T("Quick Chat 1"), "", 32, screenManager())); - PopupTextInputChoice *qc2 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat1, n->T("Quick Chat 2"), "", 32, screenManager())); - PopupTextInputChoice *qc3 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat2, n->T("Quick Chat 3"), "", 32, screenManager())); - PopupTextInputChoice *qc4 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat3, n->T("Quick Chat 4"), "", 32, screenManager())); - PopupTextInputChoice *qc5 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat4, n->T("Quick Chat 5"), "", 32, screenManager())); + PopupTextInputChoice *qc1 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat0, n->T("Quick Chat 1"), "", 32, screenManager())); + PopupTextInputChoice *qc2 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat1, n->T("Quick Chat 2"), "", 32, screenManager())); + PopupTextInputChoice *qc3 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat2, n->T("Quick Chat 3"), "", 32, screenManager())); + PopupTextInputChoice *qc4 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat3, n->T("Quick Chat 4"), "", 32, screenManager())); + PopupTextInputChoice *qc5 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat4, n->T("Quick Chat 5"), "", 32, screenManager())); #elif defined(USING_QT_UI) Choice *qc1 = networkingSettings->Add(new Choice(n->T("Quick Chat 1"))); Choice *qc2 = networkingSettings->Add(new Choice(n->T("Quick Chat 2"))); @@ -1201,7 +1201,7 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { systemSettings->Add(new PopupMultiChoice(&g_Config.iLanguage, psps->T("Game language"), defaultLanguages, -1, ARRAY_SIZE(defaultLanguages), I18NCat::PSPSETTINGS, screenManager())); static const char *models[] = { "PSP-1000", "PSP-2000/3000" }; systemSettings->Add(new PopupMultiChoice(&g_Config.iPSPModel, sy->T("PSP Model"), models, 0, ARRAY_SIZE(models), I18NCat::SYSTEM, screenManager()))->SetEnabled(!PSP_IsInited()); - systemSettings->Add(new PopupTextInputChoice(&g_Config.sNickName, sy->T("Change Nickname"), "", 32, screenManager())); + systemSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sNickName, sy->T("Change Nickname"), "", 32, screenManager())); systemSettings->Add(new CheckBox(&g_Config.bScreenshotsAsPNG, sy->T("Screenshots as PNG"))); #if defined(_WIN32) || (defined(USING_QT_UI) && !defined(MOBILE_DEVICE)) @@ -1294,7 +1294,7 @@ UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { #if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) - System_BrowseForFolder("", [](const std::string &value, int) { + System_BrowseForFolder(GetRequesterToken(), "", [](const std::string &value, int) { DarwinFileSystemServices::setUserPreferredMemoryStickDirectory(Path(value)); }); #else @@ -1374,7 +1374,7 @@ UI::EventReturn GameSettingsScreen::OnChangeBackground(UI::EventParams &e) { RecreateViews(); } else { auto sy = GetI18NCategory(I18NCat::SYSTEM); - System_BrowseForImage(sy->T("Set UI background..."), [=](const std::string &value, int) { + System_BrowseForImage(GetRequesterToken(), 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); @@ -1565,7 +1565,7 @@ UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) { #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(n->T("Enter Quick Chat 1"), g_Config.sQuickChat0, [](const std::string &value, int) { + System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 1"), g_Config.sQuickChat0, [](const std::string &value, int) { g_Config.sQuickChat0 = value; }); #endif @@ -1575,7 +1575,7 @@ UI::EventReturn GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) { #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(n->T("Enter Quick Chat 2"), g_Config.sQuickChat1, [](const std::string &value, int) { + System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 2"), g_Config.sQuickChat1, [](const std::string &value, int) { g_Config.sQuickChat1 = value; }); #endif @@ -1585,7 +1585,7 @@ UI::EventReturn GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) { #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(n->T("Enter Quick Chat 3"), g_Config.sQuickChat2, [](const std::string &value, int) { + System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 3"), g_Config.sQuickChat2, [](const std::string &value, int) { g_Config.sQuickChat2 = value; }); #endif @@ -1595,7 +1595,7 @@ UI::EventReturn GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) { #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(n->T("Enter Quick Chat 4"), g_Config.sQuickChat3, [](const std::string &value, int) { + System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 4"), g_Config.sQuickChat3, [](const std::string &value, int) { g_Config.sQuickChat3 = value; }); #endif @@ -1605,7 +1605,7 @@ UI::EventReturn GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) { #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(n->T("Enter Quick Chat 5"), g_Config.sQuickChat4, [](const std::string &value, int) { + System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 5"), g_Config.sQuickChat4, [](const std::string &value, int) { g_Config.sQuickChat4 = value; }); #endif @@ -1615,7 +1615,7 @@ UI::EventReturn GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(n->T("Enter a new PSP nickname"), g_Config.sNickName, [](const std::string &value, int) { + System_InputBoxGetString(GetRequesterToken(), n->T("Enter a new PSP nickname"), g_Config.sNickName, [](const std::string &value, int) { g_Config.sNickName = StripSpaces(value); }); #endif @@ -2077,7 +2077,7 @@ UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) { UI::EventReturn HostnameSelectScreen::OnEditClick(UI::EventParams& e) { auto n = GetI18NCategory(I18NCat::NETWORKING); - System_InputBoxGetString(n->T("proAdhocServer Address:"), addrView_->GetText(), [this](const std::string& value, int) { + System_InputBoxGetString(GetRequesterToken(), n->T("proAdhocServer Address:"), addrView_->GetText(), [this](const std::string& value, int) { addrView_->SetText(value); }); return UI::EVENT_DONE; diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index ce86f87b483e..ae01a4d04b50 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -510,8 +510,8 @@ void DirButton::Draw(UIContext &dc) { } } -GameBrowser::GameBrowser(const Path &path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams) - : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), browseFlags_(browseFlags), lastText_(lastText), lastLink_(lastLink), screenManager_(screenManager) { +GameBrowser::GameBrowser(int token, const Path &path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams) + : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), browseFlags_(browseFlags), lastText_(lastText), lastLink_(lastLink), screenManager_(screenManager), token_(token) { using namespace UI; path_.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); path_.SetRootAlias("ms:", GetSysDirectory(DIRECTORY_MEMSTICK_ROOT).ToVisualString()); @@ -604,7 +604,7 @@ UI::EventReturn GameBrowser::LastClick(UI::EventParams &e) { UI::EventReturn GameBrowser::BrowseClick(UI::EventParams &e) { auto mm = GetI18NCategory(I18NCat::MAINMENU); - System_BrowseForFolder(mm->T("Choose folder"), [this](const std::string &filename, int) { + System_BrowseForFolder(token_, mm->T("Choose folder"), [this](const std::string &filename, int) { this->SetPath(Path(filename)); }); return UI::EVENT_DONE; @@ -781,7 +781,7 @@ void GameBrowser::Refresh() { if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) { topBar->Add(new Choice(mm->T("Enter Path"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Add([=](UI::EventParams &) { auto mm = GetI18NCategory(I18NCat::MAINMENU); - System_InputBoxGetString(mm->T("Enter Path"), path_.GetPath().ToString(), [=](const char *responseString, int responseValue) { + System_InputBoxGetString(token_, mm->T("Enter Path"), path_.GetPath().ToString(), [=](const char *responseString, int responseValue) { this->SetPath(Path(responseString)); }); return UI::EVENT_DONE; @@ -1098,7 +1098,7 @@ void MainScreen::CreateViews() { if (showRecent) { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("MainScreenRecentGames"); - GameBrowser *tabRecentGames = new GameBrowser( + GameBrowser *tabRecentGames = new GameBrowser(GetRequesterToken(), Path("!RECENT"), BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRecentGames); @@ -1118,10 +1118,10 @@ void MainScreen::CreateViews() { scrollHomebrew->SetTag("MainScreenHomebrew"); - GameBrowser *tabAllGames = new GameBrowser(Path(g_Config.currentDirectory), BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), + GameBrowser *tabAllGames = new GameBrowser(GetRequesterToken(), Path(g_Config.currentDirectory), BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), mm->T("How to get games"), "https://www.ppsspp.org/getgames", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); - GameBrowser *tabHomebrew = new GameBrowser(GetSysDirectory(DIRECTORY_GAME), BrowseFlags::HOMEBREW_STORE, &g_Config.bGridView3, screenManager(), + GameBrowser *tabHomebrew = new GameBrowser(GetRequesterToken(), GetSysDirectory(DIRECTORY_GAME), BrowseFlags::HOMEBREW_STORE, &g_Config.bGridView3, screenManager(), mm->T("How to get homebrew & demos", "How to get homebrew && demos"), "https://www.ppsspp.org/gethomebrew", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); @@ -1151,7 +1151,7 @@ void MainScreen::CreateViews() { Path remotePath(FormatRemoteISOUrl(g_Config.sLastRemoteISOServer.c_str(), g_Config.iLastRemoteISOPort, RemoteSubdir().c_str())); - GameBrowser *tabRemote = new GameBrowser(remotePath, BrowseFlags::NAVIGATE, &g_Config.bGridView3, screenManager(), + GameBrowser *tabRemote = new GameBrowser(GetRequesterToken(), remotePath, BrowseFlags::NAVIGATE, &g_Config.bGridView3, screenManager(), ri->T("Remote disc streaming"), "https://www.ppsspp.org/docs/reference/disc-streaming", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); tabRemote->SetHomePath(remotePath); @@ -1330,7 +1330,7 @@ bool MainScreen::key(const KeyInput &touch) { searchKeyModifier_ = true; if (touch.keyCode == NKCODE_F && searchKeyModifier_ && System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) { auto se = GetI18NCategory(I18NCat::SEARCH); - System_InputBoxGetString(se->T("Search term"), searchFilter_, [&](const std::string &value, int) { + System_InputBoxGetString(GetRequesterToken(), se->T("Search term"), searchFilter_, [&](const std::string &value, int) { searchFilter_ = StripSpaces(value); searchChanged_ = true; }); @@ -1399,7 +1399,7 @@ void MainScreen::update() { UI::EventReturn MainScreen::OnLoadFile(UI::EventParams &e) { if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { auto mm = GetI18NCategory(I18NCat::MAINMENU); - System_BrowseForFile(mm->T("Load"), BrowseFileType::BOOTABLE, [](const std::string &value, int) { + System_BrowseForFile(GetRequesterToken(), mm->T("Load"), BrowseFileType::BOOTABLE, [](const std::string &value, int) { System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, value); }); } @@ -1609,7 +1609,7 @@ void UmdReplaceScreen::CreateViews() { if (g_Config.iMaxRecent > 0) { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("UmdReplaceRecentGames"); - GameBrowser *tabRecentGames = new GameBrowser( + GameBrowser *tabRecentGames = new GameBrowser(GetRequesterToken(), Path("!RECENT"), BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRecentGames); @@ -1620,7 +1620,7 @@ void UmdReplaceScreen::CreateViews() { ScrollView *scrollAllGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollAllGames->SetTag("UmdReplaceAllGames"); - GameBrowser *tabAllGames = new GameBrowser(Path(g_Config.currentDirectory), BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), + GameBrowser *tabAllGames = new GameBrowser(GetRequesterToken(), Path(g_Config.currentDirectory), BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), mm->T("How to get games"), "https://www.ppsspp.org/getgames.html", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); @@ -1635,7 +1635,7 @@ void UmdReplaceScreen::CreateViews() { if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { rightColumnItems->Add(new Choice(mm->T("Load", "Load...")))->OnClick.Add([&](UI::EventParams &e) { auto mm = GetI18NCategory(I18NCat::MAINMENU); - System_BrowseForFile(mm->T("Load"), BrowseFileType::BOOTABLE, [&](const std::string &value, int) { + System_BrowseForFile(GetRequesterToken(), mm->T("Load"), BrowseFileType::BOOTABLE, [&](const std::string &value, int) { __UmdReplace(Path(value)); TriggerFinish(DR_OK); }); diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 994cb82ba2a4..1e8d98521f3b 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -44,7 +44,7 @@ bool LaunchFile(ScreenManager *screenManager, const Path &path); class GameBrowser : public UI::LinearLayout { public: - GameBrowser(const Path &path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr); + GameBrowser(int token, const Path &path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr); UI::Event OnChoice; UI::Event OnHoldChoice; @@ -109,6 +109,7 @@ class GameBrowser : public UI::LinearLayout { float lastScale_ = 1.0f; bool lastLayoutWasGrid_ = true; ScreenManager *screenManager_; + int token_; }; class RemoteISOBrowseScreen; diff --git a/UI/MemStickScreen.cpp b/UI/MemStickScreen.cpp index 41d733201261..03b7f6fbe3d9 100644 --- a/UI/MemStickScreen.cpp +++ b/UI/MemStickScreen.cpp @@ -356,7 +356,7 @@ UI::EventReturn MemStickScreen::SetFolderManually(UI::EventParams ¶ms) { // The old way, from before scoped storage. #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) auto sy = GetI18NCategory(I18NCat::SYSTEM); - System_InputBoxGetString(sy->T("Memory Stick Folder"), g_Config.memStickDirectory.ToString(), [&](const std::string &value, int) { + System_InputBoxGetString(GetRequesterToken(), sy->T("Memory Stick Folder"), g_Config.memStickDirectory.ToString(), [&](const std::string &value, int) { auto sy = GetI18NCategory(I18NCat::SYSTEM); auto di = GetI18NCategory(I18NCat::DIALOG); @@ -469,7 +469,7 @@ UI::EventReturn MemStickScreen::UseStorageRoot(UI::EventParams ¶ms) { UI::EventReturn MemStickScreen::Browse(UI::EventParams ¶ms) { auto mm = GetI18NCategory(I18NCat::MAINMENU); - System_BrowseForFolder(mm->T("Choose folder"), [=](const std::string &value, int) { + System_BrowseForFolder(GetRequesterToken(), mm->T("Choose folder"), [=](const std::string &value, int) { Path pendingMemStickFolder = Path(value); INFO_LOG(SYSTEM, "Got folder: '%s'", pendingMemStickFolder.c_str()); // Browse finished. Let's pop up the confirmation dialog. diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 0a170d60cb60..8ed622622ee7 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -530,7 +530,7 @@ void RemoteISOBrowseScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("RemoteGamesTab"); - GameBrowser *tabRemoteGames = new GameBrowser( + GameBrowser *tabRemoteGames = new GameBrowser(GetRequesterToken(), Path(url_), BrowseFlags::NAVIGATE, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); tabRemoteGames->SetHomePath(Path(url_)); @@ -601,7 +601,7 @@ void RemoteISOSettingsScreen::CreateViews() { if (System_GetPropertyBool(SYSPROP_HAS_FOLDER_BROWSER)) { static const char *shareTypes[] = { "Recent files", "Choose directory" }; remoteisoSettings->Add(new PopupMultiChoice(&g_Config.iRemoteISOShareType, ri->T("Files to share"), shareTypes, 0, ARRAY_SIZE(shareTypes), I18NCat::REMOTEISO, screenManager())); - FolderChooserChoice *folderChooser = remoteisoSettings->Add(new FolderChooserChoice(&g_Config.sRemoteISOSharedDir, ri->T("Files to share"))); + FolderChooserChoice *folderChooser = remoteisoSettings->Add(new FolderChooserChoice(GetRequesterToken(), &g_Config.sRemoteISOSharedDir, ri->T("Files to share"))); folderChooser->SetEnabledFunc([=]() { return g_Config.iRemoteISOShareType == (int)RemoteISOShareType::LOCAL_FOLDER; }); @@ -610,7 +610,7 @@ void RemoteISOSettingsScreen::CreateViews() { g_Config.iRemoteISOShareType = (int)RemoteISOShareType::RECENT; } - UI::Choice *remoteServer = new PopupTextInputChoice(&g_Config.sLastRemoteISOServer, ri->T("Remote Server"), "", 255, screenManager()); + UI::Choice *remoteServer = new PopupTextInputChoice(GetRequesterToken(), &g_Config.sLastRemoteISOServer, ri->T("Remote Server"), "", 255, screenManager()); remoteisoSettings->Add(remoteServer); remoteServer->SetEnabledPtr(&g_Config.bRemoteISOManual); @@ -619,7 +619,7 @@ void RemoteISOSettingsScreen::CreateViews() { UI::Choice *remoteSubdir; { - PopupTextInputChoice *remoteSubdirInput = new PopupTextInputChoice(&g_Config.sRemoteISOSubdir, ri->T("Remote Subdirectory"), "", 255, screenManager()); + PopupTextInputChoice *remoteSubdirInput = new PopupTextInputChoice(GetRequesterToken(), &g_Config.sRemoteISOSubdir, ri->T("Remote Subdirectory"), "", 255, screenManager()); remoteSubdirInput->OnChange.Handle(this, &RemoteISOSettingsScreen::OnChangeRemoteISOSubdir); remoteSubdir = remoteSubdirInput; } diff --git a/UI/RetroAchievementScreens.cpp b/UI/RetroAchievementScreens.cpp index 2e09550f20d5..203a074b58ef 100644 --- a/UI/RetroAchievementScreens.cpp +++ b/UI/RetroAchievementScreens.cpp @@ -20,14 +20,14 @@ static inline const char *DeNull(const char *ptr) { // Compound view, creating a FileChooserChoice inside. class AudioFileChooser : public UI::LinearLayout { public: - AudioFileChooser(std::string *value, const std::string &title, UI::UISound sound, UI::LayoutParams *layoutParams = nullptr); + AudioFileChooser(RequesterToken token, std::string *value, const std::string &title, UI::UISound sound, UI::LayoutParams *layoutParams = nullptr); UI::UISound sound_; }; static constexpr UI::Size ITEM_HEIGHT = 64.f; -AudioFileChooser::AudioFileChooser(std::string *value, const std::string &title, UI::UISound sound, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams), sound_(sound) { +AudioFileChooser::AudioFileChooser(RequesterToken token, std::string *value, const std::string &title, UI::UISound sound, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams), sound_(sound) { using namespace UI; SetSpacing(2.0f); if (!layoutParams) { @@ -38,7 +38,7 @@ AudioFileChooser::AudioFileChooser(std::string *value, const std::string &title, g_BackgroundAudio.SFX().Play(sound_, 0.6f); return UI::EVENT_DONE; }); - Add(new FileChooserChoice(value, title, BrowseFileType::SOUND_EFFECT, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) { + Add(new FileChooserChoice(token, value, title, BrowseFileType::SOUND_EFFECT, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) { std::string path = e.s; Sample *sample = Sample::Load(path); if (sample) { @@ -299,7 +299,7 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup) }); } else if (System_GetPropertyBool(SYSPROP_HAS_LOGIN_DIALOG)) { viewGroup->Add(new Choice(di->T("Log in")))->OnClick.Add([=](UI::EventParams &) -> UI::EventReturn { - System_AskUsernamePassword(StringFromFormat("RetroAchievements: %s", di->T("Log in")), [](const std::string &value, int) { + System_AskUsernamePassword(GetRequesterToken(), StringFromFormat("RetroAchievements: %s", di->T("Log in")), [](const std::string &value, int) { std::vector parts; SplitString(value, '\n', parts); if (parts.size() == 2 && !parts[0].empty() && !parts[1].empty()) { @@ -310,8 +310,8 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup) }); } else { // Hack up a temporary quick login-form-ish-thing - viewGroup->Add(new PopupTextInputChoice(&username_, di->T("Username"), "", 128, screenManager())); - viewGroup->Add(new PopupTextInputChoice(&password_, di->T("Password"), "", 128, screenManager()))->SetPasswordDisplay(); + viewGroup->Add(new PopupTextInputChoice(GetRequesterToken(), &username_, di->T("Username"), "", 128, screenManager())); + viewGroup->Add(new PopupTextInputChoice(GetRequesterToken(), &password_, di->T("Password"), "", 128, screenManager()))->SetPasswordDisplay(); Choice *loginButton = viewGroup->Add(new Choice(di->T("Log in"))); loginButton->OnClick.Add([=](UI::EventParams &) -> UI::EventReturn { if (!username_.empty() && !password_.empty()) { @@ -357,8 +357,8 @@ void RetroAchievementsSettingsScreen::CreateCustomizeTab(UI::ViewGroup *viewGrou using namespace UI; viewGroup->Add(new ItemHeader(ac->T("Sound Effects"))); - viewGroup->Add(new AudioFileChooser(&g_Config.sAchievementsUnlockAudioFile, ac->T("Achievement unlocked"), UISound::ACHIEVEMENT_UNLOCKED)); - viewGroup->Add(new AudioFileChooser(&g_Config.sAchievementsLeaderboardSubmitAudioFile, ac->T("Leaderboard score submission"), UISound::LEADERBOARD_SUBMITTED)); + viewGroup->Add(new AudioFileChooser(GetRequesterToken(), &g_Config.sAchievementsUnlockAudioFile, ac->T("Achievement unlocked"), UISound::ACHIEVEMENT_UNLOCKED)); + viewGroup->Add(new AudioFileChooser(GetRequesterToken(), &g_Config.sAchievementsLeaderboardSubmitAudioFile, ac->T("Leaderboard score submission"), UISound::LEADERBOARD_SUBMITTED)); static const char *positions[] = { "None", "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right" }; diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index c39428204b3e..9b384db1003c 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -674,7 +674,7 @@ UI::EventReturn SavedataScreen::OnSortClick(UI::EventParams &e) { UI::EventReturn SavedataScreen::OnSearch(UI::EventParams &e) { if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) { auto di = GetI18NCategory(I18NCat::DIALOG); - System_InputBoxGetString(di->T("Filter"), searchFilter_, [](const std::string &value, int ivalue) { + System_InputBoxGetString(GetRequesterToken(), di->T("Filter"), searchFilter_, [](const std::string &value, int ivalue) { System_PostUIMessage(UIMessage::SAVEDATA_SEARCH, value); }); } diff --git a/UI/TabbedDialogScreen.cpp b/UI/TabbedDialogScreen.cpp index 7c570b0655ec..88a7fe8afe96 100644 --- a/UI/TabbedDialogScreen.cpp +++ b/UI/TabbedDialogScreen.cpp @@ -84,7 +84,7 @@ void TabbedUIDialogScreenWithGameBackground::CreateViews() { LinearLayout *searchSettings = AddTab("GameSettingsSearch", ms->T("Search"), true); searchSettings->Add(new ItemHeader(se->T("Find settings"))); - searchSettings->Add(new PopupTextInputChoice(&searchFilter_, se->T("Filter"), "", 64, screenManager()))->OnChange.Add([=](UI::EventParams &e) { + searchSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &searchFilter_, se->T("Filter"), "", 64, screenManager()))->OnChange.Add([=](UI::EventParams &e) { System_PostUIMessage(UIMessage::GAMESETTINGS_SEARCH, StripSpaces(searchFilter_)); return UI::EVENT_DONE; }); diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index b4c4d1c9ba83..3682f736abc4 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -321,7 +321,7 @@ namespace MainWindow { void BrowseAndBootDone(std::string filename); - void BrowseAndBoot(std::string defaultPath, bool browseDirectory) { + void BrowseAndBoot(RequesterToken token, std::string defaultPath, bool browseDirectory) { browsePauseAfter = false; if (GetUIState() == UISTATE_INGAME) { browsePauseAfter = Core_IsStepping(); @@ -333,11 +333,11 @@ namespace MainWindow { W32Util::MakeTopMost(GetHWND(), false); if (browseDirectory) { - System_BrowseForFolder(mm->T("Load"), [](const std::string &value, int) { + System_BrowseForFolder(token, mm->T("Load"), [](const std::string &value, int) { BrowseAndBootDone(value); }); } else { - System_BrowseForFile(mm->T("Load"), BrowseFileType::BOOTABLE, [](const std::string &value, int) { + System_BrowseForFile(token, mm->T("Load"), BrowseFileType::BOOTABLE, [](const std::string &value, int) { BrowseAndBootDone(value); }); } @@ -352,9 +352,9 @@ namespace MainWindow { W32Util::MakeTopMost(GetHWND(), g_Config.bTopMost); } - static void UmdSwitchAction() { + static void UmdSwitchAction(RequesterToken token) { auto mm = GetI18NCategory(I18NCat::MAINMENU); - System_BrowseForFile(mm->T("Switch UMD"), BrowseFileType::BOOTABLE, [](const std::string &value, int) { + System_BrowseForFile(token, mm->T("Switch UMD"), BrowseFileType::BOOTABLE, [](const std::string &value, int) { __UmdReplace(Path(value)); }); } @@ -441,15 +441,15 @@ namespace MainWindow { // Parse the menu selections: switch (wmId) { case ID_FILE_LOAD: - BrowseAndBoot("", false); + BrowseAndBoot(NON_EPHEMERAL_TOKEN, "", false); break; case ID_FILE_LOAD_DIR: - BrowseAndBoot("", true); + BrowseAndBoot(NON_EPHEMERAL_TOKEN, "", true); break; case ID_FILE_LOAD_MEMSTICK: - BrowseAndBoot(GetSysDirectory(DIRECTORY_GAME).ToString()); + BrowseAndBoot(NON_EPHEMERAL_TOKEN, GetSysDirectory(DIRECTORY_GAME).ToString()); break; case ID_FILE_OPEN_NEW_INSTANCE: @@ -500,7 +500,7 @@ namespace MainWindow { break; case ID_EMULATION_SWITCH_UMD: - UmdSwitchAction(); + UmdSwitchAction(NON_EPHEMERAL_TOKEN); break; case ID_EMULATION_ROTATION_H: g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL; break; diff --git a/Windows/MainWindowMenu.h b/Windows/MainWindowMenu.h index fe34643718a4..5a15f9950dcc 100644 --- a/Windows/MainWindowMenu.h +++ b/Windows/MainWindowMenu.h @@ -2,12 +2,14 @@ #include "Common/CommonWindows.h" #include + +#include "Common/System/Request.h" #include "Core/System.h" namespace MainWindow { void MainWindowMenu_Process(HWND hWnd, WPARAM wParam); void TranslateMenus(HWND hWnd, HMENU menu); - void BrowseAndBoot(std::string defaultPath, bool browseDirectory = false); + void BrowseAndBoot(RequesterToken token, std::string defaultPath, bool browseDirectory = false); void setTexScalingMultiplier(int level); void SetIngameMenuItemStates(HMENU menu, const GlobalUIState state); } diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 6503191361f3..815d1d622ed3 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -135,7 +135,6 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string return false; } } -void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb) { cb(false, ""); } void System_AskForPermission(SystemPermission permission) {} PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; } void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; } diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index e890a9ba0cb5..88615489bd2e 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1750,7 +1750,6 @@ std::vector System_GetCameraDeviceList() { return std::vector cb) { cb(false, ""); } #endif // TODO: To avoid having to define these here, these should probably be turned into system "requests".