am: Implement text check software keyboard mode
Allows the game to verify and send a message to the frontend.
This commit is contained in:
parent
e696ed1f4d
commit
fed6ab14c3
6 changed files with 120 additions and 14 deletions
|
@ -18,4 +18,10 @@ bool DefaultSoftwareKeyboardApplet::GetText(SoftwareKeyboardParameters parameter
|
||||||
return true;
|
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
|
} // namespace Core::Frontend
|
||||||
|
|
|
@ -36,11 +36,13 @@ public:
|
||||||
virtual ~SoftwareKeyboardApplet();
|
virtual ~SoftwareKeyboardApplet();
|
||||||
|
|
||||||
virtual bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const = 0;
|
virtual bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const = 0;
|
||||||
|
virtual void SendTextCheckDialog(std::u16string error_message) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
|
class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
|
||||||
public:
|
public:
|
||||||
bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const override;
|
bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const override;
|
||||||
|
void SendTextCheckDialog(std::u16string error_message) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Core::Frontend
|
} // namespace Core::Frontend
|
||||||
|
|
|
@ -544,7 +544,7 @@ public:
|
||||||
{102, nullptr, "PushExtraStorage"},
|
{102, nullptr, "PushExtraStorage"},
|
||||||
{103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
|
{103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
|
||||||
{104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
|
{104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
|
||||||
{105, nullptr, "GetPopOutDataEvent"},
|
{105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
|
||||||
{106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
|
{106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
|
||||||
{110, nullptr, "NeedsToExitProcess"},
|
{110, nullptr, "NeedsToExitProcess"},
|
||||||
{120, nullptr, "GetLibraryAppletInfo"},
|
{120, nullptr, "GetLibraryAppletInfo"},
|
||||||
|
@ -558,6 +558,8 @@ public:
|
||||||
auto& kernel = Core::System::GetInstance().Kernel();
|
auto& kernel = Core::System::GetInstance().Kernel();
|
||||||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||||
"ILibraryAppletAccessor:StateChangedEvent");
|
"ILibraryAppletAccessor:StateChangedEvent");
|
||||||
|
pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||||
|
"ILibraryAppletAccessor:PopDataOutEvent");
|
||||||
pop_interactive_out_data_event =
|
pop_interactive_out_data_event =
|
||||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||||
"ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
"ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||||
|
@ -585,9 +587,16 @@ private:
|
||||||
ASSERT(applet != nullptr);
|
ASSERT(applet != nullptr);
|
||||||
|
|
||||||
applet->Initialize(storage_stack);
|
applet->Initialize(storage_stack);
|
||||||
interactive_storage_stack.push_back(std::make_shared<IStorage>(applet->Execute()));
|
const auto data = std::make_shared<IStorage>(applet->Execute());
|
||||||
state_changed_event->Signal();
|
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};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
@ -617,6 +626,19 @@ private:
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
interactive_storage_stack.push_back(rp.PopIpcInterface<IStorage>());
|
interactive_storage_stack.push_back(rp.PopIpcInterface<IStorage>());
|
||||||
|
|
||||||
|
ASSERT(applet->IsInitialized());
|
||||||
|
applet->ReceiveInteractiveData(interactive_storage_stack.back());
|
||||||
|
const auto data = std::make_shared<IStorage>(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};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
|
@ -633,9 +655,13 @@ private:
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
|
void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||||
pop_interactive_out_data_event->Signal();
|
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};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushCopyObjects(pop_interactive_out_data_event);
|
rb.PushCopyObjects(pop_interactive_out_data_event);
|
||||||
|
@ -647,6 +673,7 @@ private:
|
||||||
std::vector<std::shared_ptr<IStorage>> storage_stack;
|
std::vector<std::shared_ptr<IStorage>> storage_stack;
|
||||||
std::vector<std::shared_ptr<IStorage>> interactive_storage_stack;
|
std::vector<std::shared_ptr<IStorage>> interactive_storage_stack;
|
||||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||||
|
Kernel::SharedPtr<Kernel::Event> pop_out_data_event;
|
||||||
Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
|
Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
|
||||||
|
union ResultCode;
|
||||||
|
|
||||||
namespace Frontend {
|
namespace Frontend {
|
||||||
class SoftwareKeyboardApplet;
|
class SoftwareKeyboardApplet;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +27,9 @@ public:
|
||||||
|
|
||||||
virtual void Initialize(std::vector<std::shared_ptr<IStorage>> storage);
|
virtual void Initialize(std::vector<std::shared_ptr<IStorage>> storage);
|
||||||
|
|
||||||
|
virtual bool TransactionComplete() const = 0;
|
||||||
|
virtual ResultCode GetStatus() const = 0;
|
||||||
|
virtual void ReceiveInteractiveData(std::shared_ptr<IStorage> storage) = 0;
|
||||||
virtual IStorage Execute() = 0;
|
virtual IStorage Execute() = 0;
|
||||||
|
|
||||||
bool IsInitialized() const {
|
bool IsInitialized() const {
|
||||||
|
|
|
@ -50,28 +50,77 @@ void SoftwareKeyboard::Initialize(std::vector<std::shared_ptr<IStorage>> storage
|
||||||
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
|
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
|
||||||
std::memcpy(&config, keyboard_config.data(), 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();
|
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<char16_t> 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<IStorage> storage) {
|
||||||
|
if (complete)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto data = storage->GetData();
|
||||||
|
const auto status = static_cast<bool>(data[0]);
|
||||||
|
|
||||||
|
if (status == INTERACTIVE_STATUS_OK) {
|
||||||
|
complete = true;
|
||||||
|
} else {
|
||||||
|
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
|
||||||
|
|
||||||
|
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
|
||||||
|
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
|
||||||
|
frontend.SendTextCheckDialog(
|
||||||
|
Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IStorage SoftwareKeyboard::Execute() {
|
IStorage SoftwareKeyboard::Execute() {
|
||||||
const auto frontend{GetSoftwareKeyboard()};
|
if (complete)
|
||||||
ASSERT(frontend != nullptr);
|
return IStorage{final_data};
|
||||||
|
|
||||||
|
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
|
||||||
|
|
||||||
const auto parameters = ConvertToFrontendParameters(config, initial_text);
|
const auto parameters = ConvertToFrontendParameters(config, initial_text);
|
||||||
|
|
||||||
std::u16string text;
|
std::u16string text;
|
||||||
const auto success = frontend->GetText(parameters, text);
|
const auto success = frontend.GetText(parameters, text);
|
||||||
|
|
||||||
std::vector<u8> output(SWKBD_OUTPUT_BUFFER_SIZE);
|
std::vector<u8> output(SWKBD_OUTPUT_BUFFER_SIZE);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
output[0] = 1;
|
if (config.text_check) {
|
||||||
|
const auto size = static_cast<u32>(text.size() * 2 + 4);
|
||||||
|
std::memcpy(output.data(), &size, sizeof(u32));
|
||||||
|
} else {
|
||||||
|
output[0] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::memcpy(output.data() + 4, text.data(),
|
std::memcpy(output.data() + 4, text.data(),
|
||||||
std::min<std::size_t>(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};
|
return IStorage{output};
|
||||||
|
|
|
@ -61,6 +61,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||||
#include "core/file_sys/romfs.h"
|
#include "core/file_sys/romfs.h"
|
||||||
#include "core/file_sys/savedata_factory.h"
|
#include "core/file_sys/savedata_factory.h"
|
||||||
#include "core/file_sys/submission_package.h"
|
#include "core/file_sys/submission_package.h"
|
||||||
|
#include "core/frontend/applets/software_keyboard.h"
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/hle/service/filesystem/fsp_ldr.h"
|
#include "core/hle/service/filesystem/fsp_ldr.h"
|
||||||
|
@ -206,6 +207,22 @@ GMainWindow::~GMainWindow() {
|
||||||
delete render_window;
|
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() {
|
void GMainWindow::InitializeWidgets() {
|
||||||
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
|
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
|
||||||
ui.action_Report_Compatibility->setVisible(true);
|
ui.action_Report_Compatibility->setVisible(true);
|
||||||
|
|
Loading…
Reference in a new issue