diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index 81f127fece..85b2ad3342 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -15,9 +15,10 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "i18n/i18n.h" #include "math/math_util.h" -#include +#include "util/text/utf8.h" #include "Core/Dialog/PSPOskDialog.h" #include "Core/Util/PPGeDraw.h" @@ -853,14 +854,19 @@ int PSPOskDialog::NativeKeyboard() { if (defaultText.empty()) defaultText.assign(L"VALUE"); - if (System_InputBoxGetWString(titleText.c_str(), defaultText, inputChars)) { - u32 maxLength = FieldMaxLength(); - if (inputChars.length() > maxLength) { - ERROR_LOG(SCEUTILITY, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength); - inputChars.erase(maxLength, std::string::npos); + // TODO: This is USING_WIN_UI only, so we rely on it being synchronous... + // But we should really have this set some state that is checked each time NativeKeyboard is called. + System_InputBoxGetString(ConvertWStringToUTF8(titleText), ConvertWStringToUTF8(defaultText), [&](bool result, const std::string &value) { + if (result) { + inputChars = ConvertUTF8ToWString(value); + u32 maxLength = FieldMaxLength(); + if (inputChars.length() > maxLength) { + ERROR_LOG(SCEUTILITY, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength); + inputChars.erase(maxLength, std::string::npos); + } } - } - ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); + }); u16_le *outText = oskParams->fields[0].outtext; diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 7dd7469163..9b3345678a 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -227,13 +227,13 @@ void System_SendMessage(const char *command, const char *parameter) { void System_AskForPermission(SystemPermission permission) {} PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; } -bool System_InputBoxGetString(const char *title, const char *defaultValue, char *outValue, size_t outLength) -{ - QString text = emugl->InputBoxGetQString(QString(title), QString(defaultValue)); - if (text.isEmpty()) - return false; - strcpy(outValue, text.toStdString().c_str()); - return true; +void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb) { + QString text = emugl->InputBoxGetQString(QString::fromStdString(title), QString::fromStdString(defaultValue)); + if (text.isEmpty()) { + cb(false, ""); + } else { + cb(true, text.toStdString()); + } } void Vibrate(int length_ms) { diff --git a/UI/ChatScreen.cpp b/UI/ChatScreen.cpp index 21d4dfba5c..e3b5229e27 100644 --- a/UI/ChatScreen.cpp +++ b/UI/ChatScreen.cpp @@ -26,13 +26,11 @@ void ChatMenu::CreatePopupContents(UI::ViewGroup *parent) { #if defined(USING_WIN_UI) //freeze the ui when using ctrl + C hotkey need workaround if (g_Config.bBypassOSKWithKeyboard && !g_Config.bFullScreen) { - std::wstring titleText = ConvertUTF8ToWString(n->T("Chat")); - std::wstring defaultText = ConvertUTF8ToWString(n->T("Chat Here")); - std::wstring inputChars; - if (System_InputBoxGetWString(titleText.c_str(), defaultText, inputChars)) { - //chatEdit_->SetText(ConvertWStringToUTF8(inputChars)); - sendChat(ConvertWStringToUTF8(inputChars)); - } + System_InputBoxGetString(n->T("Chat"), n->T("Chat Here"), [](bool result, const std::string &value) { + if (result) { + sendChat(value); + } + }); } #endif chatEdit_->OnEnter.Handle(this, &ChatMenu::OnSubmit); @@ -123,7 +121,10 @@ UI::EventReturn ChatMenu::OnSubmit(UI::EventParams &e) { chatEdit_->SetFocus(); sendChat(chat); #elif PPSSPP_PLATFORM(ANDROID) - System_SendMessage("inputbox", "Chat:"); + auto n = GetI18NCategory("Networking"); + System_InputBoxGetString(n->T("Chat"), "", [](bool result, const std::string &value) { + sendChat(value); + }); #endif return UI::EVENT_DONE; } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index d7e995f288..8d9d143b23 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -265,13 +265,6 @@ void GameSettingsScreen::CreateViews() { softwareGPU->SetEnabled(false); } - std::vector cameraList = Camera::getDeviceList(); - if (cameraList.size() >= 1) { - graphicsSettings->Add(new ItemHeader(gr->T("Camera"))); - PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, nullptr, screenManager())); - cameraChoice->OnChoice.Handle(this, &GameSettingsScreen::OnCameraDeviceChange); - } - graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"}; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkip, gr->T("Frame Skipping"), frameSkip, 0, ARRAY_SIZE(frameSkip), gr->GetName(), screenManager())); @@ -472,6 +465,13 @@ void GameSettingsScreen::CreateViews() { cardboardYShift->SetDisabledPtr(&g_Config.bSoftwareRendering); #endif + std::vector cameraList = Camera::getDeviceList(); + if (cameraList.size() >= 1) { + graphicsSettings->Add(new ItemHeader(gr->T("Camera"))); + PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, nullptr, screenManager())); + cameraChoice->OnChoice.Handle(this, &GameSettingsScreen::OnCameraDeviceChange); + } + graphicsSettings->Add(new ItemHeader(gr->T("Hack Settings", "Hack Settings (these WILL cause glitches)"))); static const char *bloomHackOptions[] = { "Off", "Safe", "Balanced", "Aggressive" }; @@ -982,7 +982,32 @@ UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { auto sy = GetI18NCategory("System"); - System_SendMessage("inputbox", (std::string(sy->T("Memory Stick Folder")) + ":" + g_Config.memStickDirectory).c_str()); + System_InputBoxGetString(sy->T("Memory Stick Folder"), g_Config.memStickDirectory, [&](bool result, const std::string &value) { + auto sy = GetI18NCategory("System"); + auto di = GetI18NCategory("Dialog"); + + if (result) { + std::string newPath = value; + size_t pos = newPath.find_last_not_of("/"); + // Gotta have at least something but a /, and also needs to start with a /. + if (newPath.empty() || pos == newPath.npos || newPath[0] != '/') { + settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr); + return; + } + if (pos != newPath.size() - 1) { + newPath = newPath.substr(0, pos + 1); + } + + pendingMemstickFolder_ = newPath; + std::string promptMessage = sy->T("ChangingMemstickPath", "Save games, save states, and other data will not be copied to this folder.\n\nChange the Memory Stick folder?"); + if (!File::Exists(newPath)) { + promptMessage = sy->T("ChangingMemstickPathNotExists", "That folder doesn't exist yet.\n\nSave games, save states, and other data will not be copied to this folder.\n\nCreate a new Memory Stick folder?"); + } + // Add the path for clarity and proper confirmation. + promptMessage += "\n\n" + newPath + "/"; + screenManager()->push(new PromptScreen(promptMessage, di->T("Yes"), di->T("No"), std::bind(&GameSettingsScreen::CallbackMemstickFolder, this, std::placeholders::_1))); + } + }); return UI::EVENT_DONE; } @@ -1139,43 +1164,6 @@ void GameSettingsScreen::onFinish(DialogResult result) { NativeMessageReceived("gpu_clearCache", ""); } -void GameSettingsScreen::sendMessage(const char *message, const char *value) { - UIDialogScreenWithGameBackground::sendMessage(message, value); - - auto sy = GetI18NCategory("System"); - auto di = GetI18NCategory("Dialog"); - - if (!strcmp(message, "inputbox_completed")) { - std::vector inputboxValue; - SplitString(value, ':', inputboxValue); - -#if PPSSPP_PLATFORM(ANDROID) - if (inputboxValue.size() >= 2 && inputboxValue[0] == sy->T("Memory Stick Folder")) { - // Allow colons in the path. - std::string newPath = std::string(value).substr(inputboxValue[0].size() + 1); - size_t pos = newPath.find_last_not_of("/"); - // Gotta have at least something but a /, and also needs to start with a /. - if (newPath.empty() || pos == newPath.npos || newPath[0] != '/') { - settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr); - return; - } - if (pos != newPath.size() - 1) { - newPath = newPath.substr(0, pos + 1); - } - - pendingMemstickFolder_ = newPath; - std::string promptMessage = sy->T("ChangingMemstickPath", "Save games, save states, and other data will not be copied to this folder.\n\nChange the Memory Stick folder?"); - if (!File::Exists(newPath)) { - promptMessage = sy->T("ChangingMemstickPathNotExists", "That folder doesn't exist yet.\n\nSave games, save states, and other data will not be copied to this folder.\n\nCreate a new Memory Stick folder?"); - } - // Add the path for clarity and proper confirmation. - promptMessage += "\n\n" + newPath + "/"; - screenManager()->push(new PromptScreen(promptMessage, di->T("Yes"), di->T("No"), std::bind(&GameSettingsScreen::CallbackMemstickFolder, this, std::placeholders::_1))); - } -#endif - } -} - #if PPSSPP_PLATFORM(ANDROID) void GameSettingsScreen::CallbackMemstickFolder(bool yes) { auto sy = GetI18NCategory("System"); @@ -1289,98 +1277,73 @@ UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) { -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) - const size_t chat_len = 64; - - char chat[chat_len]; - memset(chat, 0, sizeof(chat)); - - if (System_InputBoxGetString("Enter Quick Chat 1", g_Config.sQuickChat0.c_str(), chat, chat_len)) { - g_Config.sQuickChat0 = chat; - } -#elif defined(__ANDROID__) - System_SendMessage("inputbox", ("quickchat0:" + g_Config.sQuickChat0).c_str()); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + auto n = GetI18NCategory("Networking"); + System_InputBoxGetString(n->T("Enter Quick Chat 1"), g_Config.sQuickChat0, [](bool result, const std::string &value) { + if (result) { + g_Config.sQuickChat0 = value; + } + }); #endif return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) { -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) - const size_t chat_len = 64; - - char chat[chat_len]; - memset(chat, 0, sizeof(chat)); - - if (System_InputBoxGetString("Enter Quick Chat 2", g_Config.sQuickChat1.c_str(), chat, chat_len)) { - g_Config.sQuickChat1 = chat; - } -#elif defined(__ANDROID__) - System_SendMessage("inputbox", ("quickchat1:" + g_Config.sQuickChat1).c_str()); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + auto n = GetI18NCategory("Networking"); + System_InputBoxGetString(n->T("Enter Quick Chat 2"), g_Config.sQuickChat1, [](bool result, const std::string &value) { + if (result) { + g_Config.sQuickChat1 = value; + } + }); #endif return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) { -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) - const size_t chat_len = 64; - - char chat[chat_len]; - memset(chat, 0, sizeof(chat)); - - if (System_InputBoxGetString("Enter Quick Chat 3", g_Config.sQuickChat2.c_str(), chat, chat_len)) { - g_Config.sQuickChat2 = chat; - } -#elif defined(__ANDROID__) - System_SendMessage("inputbox", ("quickchat2:" + g_Config.sQuickChat2).c_str()); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + auto n = GetI18NCategory("Networking"); + System_InputBoxGetString(n->T("Enter Quick Chat 3"), g_Config.sQuickChat2, [](bool result, const std::string &value) { + if (result) { + g_Config.sQuickChat2 = value; + } + }); #endif return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) { -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) - const size_t chat_len = 64; - - char chat[chat_len]; - memset(chat, 0, sizeof(chat)); - - if (System_InputBoxGetString("Enter Quick Chat 4", g_Config.sQuickChat3.c_str(), chat, chat_len)) { - g_Config.sQuickChat3 = chat; - } -#elif defined(__ANDROID__) - System_SendMessage("inputbox", ("quickchat3:" + g_Config.sQuickChat3).c_str()); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + auto n = GetI18NCategory("Networking"); + System_InputBoxGetString(n->T("Enter Quick Chat 4"), g_Config.sQuickChat3, [](bool result, const std::string &value) { + if (result) { + g_Config.sQuickChat3 = value; + } + }); #endif return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) { -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) - const size_t chat_len = 64; - - char chat[chat_len]; - memset(chat, 0, sizeof(chat)); - - if (System_InputBoxGetString("Enter Quick Chat 5", g_Config.sQuickChat4.c_str(), chat, chat_len)) { - g_Config.sQuickChat4 = chat; - } -#elif defined(__ANDROID__) - System_SendMessage("inputbox", ("quickchat4:" + g_Config.sQuickChat4).c_str()); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + auto n = GetI18NCategory("Networking"); + System_InputBoxGetString(n->T("Enter Quick Chat 5"), g_Config.sQuickChat4, [](bool result, const std::string &value) { + if (result) { + g_Config.sQuickChat4 = value; + } + }); #endif return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { -#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) - const size_t name_len = 256; - - char name[name_len]; - memset(name, 0, sizeof(name)); - - if (System_InputBoxGetString("Enter a new PSP nickname", g_Config.sNickName.c_str(), name, name_len)) { - g_Config.sNickName = StripSpaces(name); - } -#elif defined(__ANDROID__) - // TODO: The return value is handled in NativeApp::inputbox_completed. This is horrific. - System_SendMessage("inputbox", ("nickname:" + g_Config.sNickName).c_str()); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + auto n = GetI18NCategory("Networking"); + System_InputBoxGetString(n->T("Enter a new PSP nickname"), g_Config.sNickName, [](bool result, const std::string &value) { + if (result) { + g_Config.sNickName = StripSpaces(value); + } + }); #endif return UI::EVENT_DONE; } @@ -1389,7 +1352,12 @@ UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParam auto sy = GetI18NCategory("System"); #if defined(__ANDROID__) - System_SendMessage("inputbox", ("IP:" + g_Config.proAdhocServer).c_str()); + auto n = GetI18NCategory("Networking"); + System_InputBoxGetString(n->T("IP"), g_Config.proAdhocServer, [](bool result, const std::string &value) { + if (result) { + g_Config.proAdhocServer = value; + } + }); #else screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, sy->T("proAdhocServer Address:"))); #endif diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 6f56b2fbcb..7a82b7f463 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -34,7 +34,6 @@ public: void update() override; void onFinish(DialogResult result) override; - void sendMessage(const char *message, const char *value) override; std::string tag() const override { return "settings"; } protected: diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 3334120685..400cdebc14 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -159,13 +159,19 @@ struct PendingMessage { std::string value; }; +struct PendingInputBox { + std::function cb; + bool result; + std::string value; +}; + static std::mutex pendingMutex; static std::vector pendingMessages; +static std::vector pendingInputBoxes; static Draw::DrawContext *g_draw; static Draw::Pipeline *colorPipeline; static Draw::Pipeline *texColorPipeline; static UIContext *uiContext; -static std::vector inputboxValue; #ifdef _WIN32 WindowsAudioBackend *winAudioBackend; @@ -445,6 +451,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch setlocale( LC_ALL, "C" ); std::string user_data_path = savegame_dir; pendingMessages.clear(); + pendingInputBoxes.clear(); #ifdef IOS user_data_path += "/"; #endif @@ -1094,42 +1101,6 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { if (msg == "inputDeviceConnected") { KeyMap::NotifyPadConnected(value); } - if (msg == "inputbox_completed") { - SplitString(value, ':', inputboxValue); - std::string setString = inputboxValue.size() > 1 ? inputboxValue[1] : ""; - if (inputboxValue[0] == "IP") - g_Config.proAdhocServer = setString; - else if (inputboxValue[0] == "nickname") - g_Config.sNickName = setString; - else if (inputboxValue[0] == "remoteiso_subdir") - g_Config.sRemoteISOSubdir = setString; - else if (inputboxValue[0] == "remoteiso_server") - g_Config.sLastRemoteISOServer = setString; - - if (inputboxValue[0] == "quickchat0") - g_Config.sQuickChat0 = setString; - if (inputboxValue[0] == "quickchat1") - g_Config.sQuickChat1 = setString; - if (inputboxValue[0] == "quickchat2") - g_Config.sQuickChat2 = setString; - if (inputboxValue[0] == "quickchat3") - g_Config.sQuickChat3 = setString; - if (inputboxValue[0] == "quickchat4") - g_Config.sQuickChat4 = setString; - if (inputboxValue[0] == "nickname") - g_Config.sNickName = setString; - if (inputboxValue[0] == "Chat") { - if (inputboxValue.size() > 2) - { - std::string chatString = value; - chatString.erase(0, 5); - sendChat(chatString); - } else { - sendChat(setString); - } - } - inputboxValue.clear(); - } if (msg == "bgImage_updated") { if (!value.empty()) { std::string dest = GetSysDirectory(DIRECTORY_SYSTEM) + (endsWithNoCase(value, ".jpg") ? "background.jpg" : "background.png"); @@ -1184,15 +1155,21 @@ void NativeUpdate() { PROFILE_END_FRAME(); std::vector toProcess; + std::vector inputToProcess; { std::lock_guard lock(pendingMutex); toProcess = std::move(pendingMessages); + inputToProcess = std::move(pendingInputBoxes); pendingMessages.clear(); + pendingInputBoxes.clear(); } - for (size_t i = 0; i < toProcess.size(); i++) { - HandleGlobalMessage(toProcess[i].msg, toProcess[i].value); - screenManager->sendMessage(toProcess[i].msg.c_str(), toProcess[i].value.c_str()); + for (const auto &item : toProcess) { + HandleGlobalMessage(item.msg, item.value); + screenManager->sendMessage(item.msg.c_str(), item.value.c_str()); + } + for (const auto &item : inputToProcess) { + item.cb(item.result, item.value); } g_DownloadManager.Update(); @@ -1339,6 +1316,15 @@ void NativeMessageReceived(const char *message, const char *value) { pendingMessages.push_back(pendingMessage); } +void NativeInputBoxReceived(std::function cb, bool result, const std::string &value) { + std::lock_guard lock(pendingMutex); + PendingInputBox pendingMessage; + pendingMessage.cb = cb; + pendingMessage.result = result; + pendingMessage.value = value; + pendingInputBoxes.push_back(pendingMessage); +} + void NativeResized() { // NativeResized can come from any thread so we just set a flag, then process it later. ILOG("NativeResized - setting flag"); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 4f9955f678..654be8d6e7 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -520,12 +520,22 @@ void RemoteISOSettingsScreen::CreateViews() { } UI::EventReturn RemoteISOSettingsScreen::OnClickRemoteServer(UI::EventParams &e) { - System_SendMessage("inputbox", ("remoteiso_server:" + g_Config.sLastRemoteISOServer).c_str()); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + auto ri = GetI18NCategory("RemoteISO"); + System_InputBoxGetString(ri->T("Remote Server"), g_Config.sLastRemoteISOServer, [](bool result, const std::string &value) { + g_Config.sLastRemoteISOServer = value; + }); +#endif return UI::EVENT_DONE; } UI::EventReturn RemoteISOSettingsScreen::OnClickRemoteISOSubdir(UI::EventParams &e) { - System_SendMessage("inputbox", ("remoteiso_subdir:" + g_Config.sRemoteISOSubdir).c_str()); +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || defined(__ANDROID__) + auto ri = GetI18NCategory("RemoteISO"); + System_InputBoxGetString(ri->T("Remote Subdirectory"), g_Config.sRemoteISOSubdir, [](bool result, const std::string &value) { + g_Config.sRemoteISOSubdir = value; + }); +#endif return UI::EVENT_DONE; } diff --git a/Windows/main.cpp b/Windows/main.cpp index 7acdd73713..8a8d4e616a 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -18,6 +18,7 @@ #include "stdafx.h" #include #include +#include #include "Common/CommonWindows.h" #include "Common/OSVersion.h" @@ -337,23 +338,12 @@ void EnableCrashingOnCrashes() { FreeLibrary(kernel32); } -bool System_InputBoxGetString(const char *title, const char *defaultValue, char *outValue, size_t outLength) -{ +void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb) { std::string out; if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), ConvertUTF8ToWString(title).c_str(), defaultValue, out)) { - strcpy(outValue, out.c_str()); - return true; + cb(true, out); } else { - return false; - } -} - -bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultvalue, std::wstring &outvalue) -{ - if (InputBox_GetWString(MainWindow::GetHInstance(), MainWindow::GetHWND(), title, defaultvalue, outvalue)) { - return true; - } else { - return false; + cb(false, ""); } } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 0ceace3732..277fd0776d 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -123,6 +123,8 @@ static jmethodID postCommand; static jobject nativeActivity; static volatile bool exitRenderLoop; static bool renderLoopRunning; +static int inputBoxSequence = 1; +std::map> inputBoxCallbacks; static float dp_xscale = 1.0f; static float dp_yscale = 1.0f; @@ -572,6 +574,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { ILOG("Not shutting down renderer - not initialized"); } + inputBoxCallbacks.clear(); NativeShutdown(); VFSShutdown(); while (frameCommands.size()) @@ -667,6 +670,33 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv } } +void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb) { + int seq = inputBoxSequence++; + inputBoxCallbacks[seq] = cb; + + std::string serialized = StringFromFormat("%d:@:%s:@:%s", seq, title.c_str(), defaultValue.c_str()); + System_SendMessage("inputbox", serialized.c_str()); +} + +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); + std::string value = GetJavaString(env, jvalue); + + int seq = 0; + if (!TryParse(seqID, &seq)) { + ELOG("Invalid inputbox seqID value: %s", seqID.c_str()); + return; + } + + auto entry = inputBoxCallbacks.find(seq); + if (entry == inputBoxCallbacks.end()) { + ELOG("Did not find inputbox callback for %s, shutdown?", seqID.c_str()); + return; + } + + NativeInputBoxReceived(entry->second, result, value); +} + void UpdateRunLoopAndroid(JNIEnv *env) { NativeUpdate(); diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 700318133a..58f6dc21da 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -1169,8 +1169,8 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C return bld; } - // The return value is sent elsewhere. TODO in java, in SendMessage in C++. - public void inputBox(final String title, String defaultText, String defaultAction) { + // The return value is sent to C++ via seqID. + public void inputBox(final String seqID, final String title, String defaultText, String defaultAction) { final FrameLayout fl = new FrameLayout(this); final EditText input = new EditText(this); input.setGravity(Gravity.CENTER); @@ -1202,14 +1202,14 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C .setPositiveButton(defaultAction, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface d, int which) { - NativeApp.sendMessage("inputbox_completed", title + ":" + input.getText().toString()); + NativeApp.sendInputBox(seqID, true, input.getText().toString()); d.dismiss(); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface d, int which) { - NativeApp.sendMessage("inputbox_failed", ""); + NativeApp.sendInputBox(seqID, false, ""); d.cancel(); } }) @@ -1311,15 +1311,17 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C inputMethodManager.toggleSoftInputFromWindow(surfView.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0); return true; } else if (command.equals("inputbox")) { + String seqID = ""; String title = "Input"; String defString = ""; - String[] param = params.split(":"); - if (param[0].length() > 0) - title = param[0]; - if (param.length > 1) - defString = param[1]; - Log.i(TAG, "Launching inputbox: " + title + " " + defString); - inputBox(title, defString, "OK"); + String[] param = params.split(":@:", 3); + seqID = 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"); 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 fec8c5c002..46598c8263 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -48,6 +48,7 @@ public class NativeApp { public static native boolean 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 String queryConfig(String queryName); diff --git a/ext/native/base/NativeApp.h b/ext/native/base/NativeApp.h index f1f348acc7..c5c56206cd 100644 --- a/ext/native/base/NativeApp.h +++ b/ext/native/base/NativeApp.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -34,6 +35,9 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo // Generic host->C++ messaging, used for functionality like system-native popup input boxes. void NativeMessageReceived(const char *message, const char *value); +// This is used to communicate back and thread requested input box strings. +void NativeInputBoxReceived(std::function cb, bool result, const std::string &value); + // Easy way for the Java side to ask the C++ side for configuration options, such as // the rotation lock which must be controlled from Java on Android. // It is currently not called on non-Android platforms. @@ -119,8 +123,7 @@ void OpenDirectory(const char *path); void LaunchBrowser(const char *url); void LaunchMarket(const char *url); void LaunchEmail(const char *email_address); -bool System_InputBoxGetString(const char *title, const char *defaultValue, char *outValue, size_t outlength); -bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultValue, std::wstring &outValue); +void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb); void System_SendMessage(const char *command, const char *parameter); PermissionStatus System_GetPermissionStatus(SystemPermission permission); void System_AskForPermission(SystemPermission permission); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index b92f36d6ba..c0494eaf70 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -81,7 +81,7 @@ bool System_GetPropertyBool(SystemProperty prop) { return false; } void System_SendMessage(const char *command, const char *parameter) {} -bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultvalue, std::wstring &outvalue) { 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; } diff --git a/unittest/JitHarness.cpp b/unittest/JitHarness.cpp index a34f3f4952..1569048b18 100644 --- a/unittest/JitHarness.cpp +++ b/unittest/JitHarness.cpp @@ -37,7 +37,7 @@ void NativeRender(GraphicsContext *graphicsContext) { } void NativeResized() { } void System_SendMessage(const char *command, const char *parameter) {} -bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultvalue, std::wstring &outvalue) { 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; }