From fed6ab14c37f196f2a2fd378b46d7e5bd0118224 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 11 Nov 2018 16:41:31 -0500 Subject: [PATCH] am: Implement text check software keyboard mode Allows the game to verify and send a message to the frontend. --- .../frontend/applets/software_keyboard.cpp | 6 ++ src/core/frontend/applets/software_keyboard.h | 2 + src/core/hle/service/am/am.cpp | 37 ++++++++-- src/core/hle/service/am/applets/applets.h | 5 ++ .../service/am/applets/software_keyboard.cpp | 67 ++++++++++++++++--- src/yuzu/main.cpp | 17 +++++ 6 files changed, 120 insertions(+), 14 deletions(-) diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 41d81c293..05e2dc6b7 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -18,4 +18,10 @@ bool DefaultSoftwareKeyboardApplet::GetText(SoftwareKeyboardParameters parameter return true; } +void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(std::u16string error_message) const { + LOG_WARNING(Service_AM, + "(STUBBED) called - Default fallback software keyboard does not support text " + "check! (error_message={})", + Common::UTF16ToUTF8(error_message)); +} } // namespace Core::Frontend diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 2ea9db889..0a82aac0d 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -36,11 +36,13 @@ public: virtual ~SoftwareKeyboardApplet(); virtual bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const = 0; + virtual void SendTextCheckDialog(std::u16string error_message) const = 0; }; class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { public: bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const override; + void SendTextCheckDialog(std::u16string error_message) const override; }; } // namespace Core::Frontend diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index fc464270e..ea00c5c72 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -544,7 +544,7 @@ public: {102, nullptr, "PushExtraStorage"}, {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"}, {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"}, - {105, nullptr, "GetPopOutDataEvent"}, + {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"}, {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"}, {110, nullptr, "NeedsToExitProcess"}, {120, nullptr, "GetLibraryAppletInfo"}, @@ -558,6 +558,8 @@ public: auto& kernel = Core::System::GetInstance().Kernel(); state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:StateChangedEvent"); + pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, + "ILibraryAppletAccessor:PopDataOutEvent"); pop_interactive_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); @@ -585,9 +587,16 @@ private: ASSERT(applet != nullptr); applet->Initialize(storage_stack); - interactive_storage_stack.push_back(std::make_shared(applet->Execute())); + const auto data = std::make_shared(applet->Execute()); state_changed_event->Signal(); - pop_interactive_out_data_event->Signal(); + + if (applet->TransactionComplete()) { + storage_stack.push_back(data); + pop_out_data_event->Signal(); + } else { + interactive_storage_stack.push_back(data); + pop_interactive_out_data_event->Signal(); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -617,6 +626,19 @@ private: IPC::RequestParser rp{ctx}; interactive_storage_stack.push_back(rp.PopIpcInterface()); + ASSERT(applet->IsInitialized()); + applet->ReceiveInteractiveData(interactive_storage_stack.back()); + const auto data = std::make_shared(applet->Execute()); + state_changed_event->Signal(); + + if (applet->TransactionComplete()) { + storage_stack.push_back(data); + pop_out_data_event->Signal(); + } else { + interactive_storage_stack.push_back(data); + pop_interactive_out_data_event->Signal(); + } + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -633,9 +655,13 @@ private: LOG_DEBUG(Service_AM, "called"); } - void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { - pop_interactive_out_data_event->Signal(); + void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(pop_out_data_event); + } + void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(pop_interactive_out_data_event); @@ -647,6 +673,7 @@ private: std::vector> storage_stack; std::vector> interactive_storage_stack; Kernel::SharedPtr state_changed_event; + Kernel::SharedPtr pop_out_data_event; Kernel::SharedPtr pop_interactive_out_data_event; }; diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 47db22fb4..6d90eb608 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -8,6 +8,8 @@ #include #include "common/swap.h" +union ResultCode; + namespace Frontend { class SoftwareKeyboardApplet; } @@ -25,6 +27,9 @@ public: virtual void Initialize(std::vector> storage); + virtual bool TransactionComplete() const = 0; + virtual ResultCode GetStatus() const = 0; + virtual void ReceiveInteractiveData(std::shared_ptr storage) = 0; virtual IStorage Execute() = 0; bool IsInitialized() const { diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 556dea3e4..044a16264 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -50,28 +50,77 @@ void SoftwareKeyboard::Initialize(std::vector> storage ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); - ASSERT_MSG(config.text_check == 0, "Text check software keyboard mode is not implemented!"); - const auto& work_buffer = storage_stack[2]->GetData(); - std::memcpy(initial_text.data(), work_buffer.data() + config.initial_string_offset, - config.initial_string_size); + + if (config.initial_string_size == 0) + return; + + std::vector string(config.initial_string_size); + std::memcpy(string.data(), work_buffer.data() + 4, string.size() * 2); + initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()); +} + +bool SoftwareKeyboard::TransactionComplete() const { + return complete; +} + +ResultCode SoftwareKeyboard::GetStatus() const { + return RESULT_SUCCESS; +} + +void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) { + if (complete) + return; + + const auto data = storage->GetData(); + const auto status = static_cast(data[0]); + + if (status == INTERACTIVE_STATUS_OK) { + complete = true; + } else { + const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; + + std::array string; + std::memcpy(string.data(), data.data() + 4, string.size() * 2); + frontend.SendTextCheckDialog( + Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size())); + } } IStorage SoftwareKeyboard::Execute() { - const auto frontend{GetSoftwareKeyboard()}; - ASSERT(frontend != nullptr); + if (complete) + return IStorage{final_data}; + + const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; const auto parameters = ConvertToFrontendParameters(config, initial_text); std::u16string text; - const auto success = frontend->GetText(parameters, text); + const auto success = frontend.GetText(parameters, text); std::vector output(SWKBD_OUTPUT_BUFFER_SIZE); if (success) { - output[0] = 1; + if (config.text_check) { + const auto size = static_cast(text.size() * 2 + 4); + std::memcpy(output.data(), &size, sizeof(u32)); + } else { + output[0] = 1; + } + std::memcpy(output.data() + 4, text.data(), - std::min(text.size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + std::min(text.size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + } else { + complete = true; + final_data = std::move(output); + return IStorage{final_data}; + } + + complete = !config.text_check; + + if (complete) { + final_data = std::move(output); + return IStorage{final_data}; } return IStorage{output}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9b2c09f32..447d9dece 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -61,6 +61,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/file_sys/romfs.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/submission_package.h" +#include "core/frontend/applets/software_keyboard.h" #include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_ldr.h" @@ -206,6 +207,22 @@ GMainWindow::~GMainWindow() { delete render_window; } +bool GMainWindow::SoftwareKeyboardGetText( + const Core::Frontend::SoftwareKeyboardParameters& parameters, std::u16string& text) { + QtSoftwareKeyboardDialog dialog(this, parameters); + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); + dialog.setWindowModality(Qt::WindowModal); + dialog.exec(); + + text = dialog.GetText(); + return dialog.GetStatus(); +} + +void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) { + QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message)); +} + void GMainWindow::InitializeWidgets() { #ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING ui.action_Report_Compatibility->setVisible(true);