From 7901de2b75bf527790c3c24e6667b275e2ebeb2b Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 17:02:50 -0500 Subject: [PATCH 01/29] svc: Implement svcCreateTransferMemory Seems to be used and created identically to SharedMemory, so just reuse that. --- src/core/hle/kernel/svc.cpp | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2e7c9d0943..20af65ee79 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1194,9 +1194,39 @@ static ResultCode ResetSignal(Handle handle) { /// Creates a TransferMemory object static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, - permissions); - *handle = 0; + LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, + permissions); + + if (!Common::Is4KBAligned(addr)) { + LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(size) || size == 0) { + LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size); + return ERR_INVALID_ADDRESS; + } + + if (addr + size <= addr) { + LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})", + addr, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (permissions > static_cast(MemoryPermission::ReadWrite) || + permissions == static_cast(MemoryPermission::Write)) { + LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})", + permissions); + return ERR_INVALID_MEMORY_PERMISSIONS; + } + + auto& kernel = Core::System::GetInstance().Kernel(); + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto perms = static_cast(permissions); + const auto shared_mem_handle = SharedMemory::Create( + kernel, handle_table.Get(CurrentProcess), size, perms, perms, addr); + + CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); return RESULT_SUCCESS; } From c70529c1ec1092d3317316534155344e498fa593 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 17:03:54 -0500 Subject: [PATCH 02/29] string_util: Implement buffer to UTF-16 string helper function Needed as most all software keyboard functions use fixed-length UTF16 string buffers. --- src/common/string_util.cpp | 9 +++++++++ src/common/string_util.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 14f7037d85..a1360dd268 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -214,6 +214,15 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t return std::string(buffer, len); } +std::u16string UTF16StringFromFixedZeroTerminatedBuffer(const char16_t* buffer, + std::size_t max_len) { + std::size_t len = 0; + while (len < max_len && buffer[len] != '\0') + ++len; + + return std::u16string(buffer, len); +} + const char* TrimSourcePath(const char* path, const char* root) { const char* p = path; diff --git a/src/common/string_util.h b/src/common/string_util.h index 08f96533b6..95b7badaf3 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -66,6 +66,14 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) { */ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); +/** + * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't + * NUL-terminated, then the string ends at the greatest multiple of two less then or equal to + * max_len_bytes. + */ +std::u16string UTF16StringFromFixedZeroTerminatedBuffer(const char16_t* buffer, + std::size_t max_len); + /** * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's * intended to be used to strip a system-specific build directory from the `__FILE__` macro, From 76d515327b14c54036065167cb5dedcc5bf29eb2 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:01:20 -0500 Subject: [PATCH 03/29] am: Implement CreateTransferMemoryStorage Creates an AM::IStorage object with the contents of the transfer memory located at the handle provided. --- src/core/hle/service/am/am.cpp | 25 +++++++++++++++++++++++++ src/core/hle/service/am/am.h | 1 + 2 files changed, 26 insertions(+) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3758ecae13..a7716d80b5 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -704,6 +704,31 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called, size={}", size); } +void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + rp.SetCurrentOffset(3); + const auto handle{rp.Pop()}; + + const auto shared_mem = + Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get( + handle); + + if (shared_mem == nullptr) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); + return; + } + + std::vector memory(shared_mem->size); + std::memcpy(memory.data(), shared_mem->backing_block->data() + shared_mem->backing_block_offset, + memory.size()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::make_shared(std::move(memory))); +} + IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { // clang-format off static const FunctionInfo functions[] = { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 5a3fcba8f6..50b2775ea5 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -163,6 +163,7 @@ public: private: void CreateLibraryApplet(Kernel::HLERequestContext& ctx); void CreateStorage(Kernel::HLERequestContext& ctx); + void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); }; class IApplicationFunctions final : public ServiceFramework { From c7b6c9de9c4004399edf8a010b74fee1127c7682 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:04:07 -0500 Subject: [PATCH 04/29] am: Move IStorageAccessor to header and update backing buffer Writes to an AM::IStorage object through an IStorageAccessor will now be preserved once the accessor is destroyed. --- src/core/hle/service/am/am.cpp | 100 ++++++++++++--------------------- src/core/hle/service/am/am.h | 26 +++++++++ 2 files changed, 62 insertions(+), 64 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a7716d80b5..c7344608c9 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -587,87 +587,59 @@ private: } }; -class ILibraryAppletAccessor final : public ServiceFramework { -public: - explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { - // clang-format off +IStorageAccessor::IStorageAccessor(IStorage& storage) + : ServiceFramework("IStorageAccessor"), backing(storage) { + // clang-format off static const FunctionInfo functions[] = { - {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, - {1, nullptr, "IsCompleted"}, - {10, &ILibraryAppletAccessor::Start, "Start"}, - {20, nullptr, "RequestExit"}, - {25, nullptr, "Terminate"}, - {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, - {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, - {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, - {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, - {102, nullptr, "PushExtraStorage"}, - {103, nullptr, "PushInteractiveInData"}, - {104, nullptr, "PopInteractiveOutData"}, - {105, nullptr, "GetPopOutDataEvent"}, - {106, nullptr, "GetPopInteractiveOutDataEvent"}, - {110, nullptr, "NeedsToExitProcess"}, - {120, nullptr, "GetLibraryAppletInfo"}, - {150, nullptr, "RequestForAppletToGetForeground"}, - {160, nullptr, "GetIndirectLayerConsumerHandle"}, + {0, &IStorageAccessor::GetSize, "GetSize"}, + {10, &IStorageAccessor::Write, "Write"}, + {11, &IStorageAccessor::Read, "Read"}, }; - // clang-format on + // clang-format on - RegisterHandlers(functions); + RegisterHandlers(functions); +} - auto& kernel = Core::System::GetInstance().Kernel(); - state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "ILibraryAppletAccessor:StateChangedEvent"); - } +void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; -private: - void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { - state_changed_event->Signal(); + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast(backing.buffer.size())); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(state_changed_event); + LOG_DEBUG(Service_AM, "called"); +} - LOG_WARNING(Service_AM, "(STUBBED) called"); - } +void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; - void GetResult(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + const u64 offset{rp.Pop()}; + const std::vector data{ctx.ReadBuffer()}; - LOG_WARNING(Service_AM, "(STUBBED) called"); - } + const auto size = std::min(data.size(), backing.buffer.size() - offset); - void Start(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + std::memcpy(&backing.buffer[offset], data.data(), size); - LOG_WARNING(Service_AM, "(STUBBED) called"); - } + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); - void PushInData(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - storage_stack.push(rp.PopIpcInterface()); + LOG_DEBUG(Service_AM, "called, offset={}", offset); +} - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); +void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; - LOG_DEBUG(Service_AM, "called"); - } + const u64 offset{rp.Pop()}; + std::size_t size{ctx.GetWriteBufferSize()}; - void PopOutData(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(std::move(storage_stack.top())); + size = std::min(size, backing.buffer.size() - offset); - storage_stack.pop(); + ctx.WriteBuffer(backing.buffer.data() + offset, size); - LOG_DEBUG(Service_AM, "called"); - } + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); - std::stack> storage_stack; - Kernel::SharedPtr state_changed_event; -}; + LOG_DEBUG(Service_AM, "called, offset={}", offset); +} ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { static const FunctionInfo functions[] = { @@ -675,7 +647,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple {1, nullptr, "TerminateAllLibraryApplets"}, {2, nullptr, "AreAnyLibraryAppletsLeft"}, {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, - {11, nullptr, "CreateTransferMemoryStorage"}, + {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, {12, nullptr, "CreateHandleStorage"}, }; RegisterHandlers(functions); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 50b2775ea5..640901e4af 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -155,6 +155,32 @@ private: std::shared_ptr msg_queue; }; +class IStorage final : public ServiceFramework { +public: + explicit IStorage(std::vector buffer); + + const std::vector& GetData() const; + +private: + std::vector buffer; + + void Open(Kernel::HLERequestContext& ctx); + + friend class IStorageAccessor; +}; + +class IStorageAccessor final : public ServiceFramework { +public: + explicit IStorageAccessor(IStorage& backing); + +private: + IStorage& backing; + + void GetSize(Kernel::HLERequestContext& ctx); + void Write(Kernel::HLERequestContext& ctx); + void Read(Kernel::HLERequestContext& ctx); +}; + class ILibraryAppletCreator final : public ServiceFramework { public: ILibraryAppletCreator(); From 0682a908c0ab79a5137f408cafc3aff07c7c7be5 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:04:43 -0500 Subject: [PATCH 05/29] am: Move AM::IStorage to header Needs to be accessible by applet files. --- src/core/hle/service/am/am.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index c7344608c9..35481b2014 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -481,6 +481,22 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& LOG_DEBUG(Service_AM, "called"); } +IStorage::IStorage(std::vector buffer) + : ServiceFramework("IStorage"), buffer(std::move(buffer)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IStorage::Open, "Open"}, + {1, nullptr, "OpenTransferStorage"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +const std::vector& IStorage::GetData() const { + return buffer; +} + void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { const bool use_docked_mode{Settings::values.use_docked_mode}; IPC::ResponseBuilder rb{ctx, 3}; From 5ce6b8fea7122fb8267252d09d4416769637702d Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:06:50 -0500 Subject: [PATCH 06/29] am: Convert storage stack to vector std::stack was no longer suitable for non-trivial operations --- src/core/hle/service/am/am.cpp | 92 +++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 30 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 35481b2014..a872bea0cc 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -516,31 +516,56 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); } -class IStorageAccessor final : public ServiceFramework { +class ILibraryAppletAccessor final : public ServiceFramework { public: - explicit IStorageAccessor(std::vector buffer) - : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { + explicit ILibraryAppletAccessor(std::shared_ptr applet) + : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) { // clang-format off static const FunctionInfo functions[] = { - {0, &IStorageAccessor::GetSize, "GetSize"}, - {10, &IStorageAccessor::Write, "Write"}, - {11, &IStorageAccessor::Read, "Read"}, + {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, + {1, nullptr, "IsCompleted"}, + {10, &ILibraryAppletAccessor::Start, "Start"}, + {20, nullptr, "RequestExit"}, + {25, nullptr, "Terminate"}, + {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, + {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, + {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, + {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, + {102, nullptr, "PushExtraStorage"}, + {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"}, + {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"}, + {105, nullptr, "GetPopOutDataEvent"}, + {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"}, + {110, nullptr, "NeedsToExitProcess"}, + {120, nullptr, "GetLibraryAppletInfo"}, + {150, nullptr, "RequestForAppletToGetForeground"}, + {160, nullptr, "GetIndirectLayerConsumerHandle"}, }; // clang-format on RegisterHandlers(functions); + + auto& kernel = Core::System::GetInstance().Kernel(); + state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, + "ILibraryAppletAccessor:StateChangedEvent"); } private: - std::vector buffer; - - void GetSize(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 4}; + void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { + state_changed_event->Signal(); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast(buffer.size())); + rb.PushCopyObjects(state_changed_event); - LOG_DEBUG(Service_AM, "called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); + } + + void GetResult(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); } void Write(Kernel::HLERequestContext& ctx) { @@ -551,21 +576,25 @@ private: ASSERT(offset + data.size() <= buffer.size()); - std::memcpy(&buffer[offset], data.data(), data.size()); + void PushInData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + storage_stack.push_back(rp.PopIpcInterface()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_AM, "called, offset={}", offset); + LOG_DEBUG(Service_AM, "called"); } - void Read(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; + void PopOutData(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::move(storage_stack.back())); - const u64 offset{rp.Pop()}; - const std::size_t size{ctx.GetWriteBufferSize()}; + storage_stack.pop_back(); - ASSERT(offset + size <= buffer.size()); + LOG_DEBUG(Service_AM, "called"); + } ctx.WriteBuffer(buffer.data() + offset, size); @@ -590,19 +619,22 @@ public: RegisterHandlers(functions); } -private: - std::vector buffer; - - void Open(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(buffer); - - LOG_DEBUG(Service_AM, "called"); - } + std::shared_ptr applet; + std::vector> storage_stack; + std::vector> interactive_storage_stack; + Kernel::SharedPtr state_changed_event; + Kernel::SharedPtr pop_interactive_out_data_event; }; +void IStorage::Open(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(*this); + + LOG_DEBUG(Service_AM, "called"); +} + IStorageAccessor::IStorageAccessor(IStorage& storage) : ServiceFramework("IStorageAccessor"), backing(storage) { // clang-format off From ba03bfa4309664d55d585c89271d9656f8385a78 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:07:42 -0500 Subject: [PATCH 07/29] am: Implement PopInteractiveOutData and PushInteractiveInData Used by software keyboard applet for data transfer. --- src/core/hle/service/am/am.cpp | 38 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a872bea0cc..b78489df7a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -548,6 +548,9 @@ public: auto& kernel = Core::System::GetInstance().Kernel(); state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:StateChangedEvent"); + pop_interactive_out_data_event = + Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, + "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); } private: @@ -596,27 +599,34 @@ private: LOG_DEBUG(Service_AM, "called"); } - ctx.WriteBuffer(buffer.data() + offset, size); + void PushInteractiveInData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + interactive_storage_stack.push_back(rp.PopIpcInterface()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_AM, "called, offset={}", offset); + LOG_DEBUG(Service_AM, "called"); } -}; -class IStorage final : public ServiceFramework { -public: - explicit IStorage(std::vector buffer) - : ServiceFramework("IStorage"), buffer(std::move(buffer)) { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IStorage::Open, "Open"}, - {1, nullptr, "OpenTransferStorage"}, - }; - // clang-format on + void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::move(interactive_storage_stack.back())); - RegisterHandlers(functions); + interactive_storage_stack.pop_back(); + + LOG_DEBUG(Service_AM, "called"); + } + + void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { + pop_interactive_out_data_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(pop_interactive_out_data_event); + + LOG_WARNING(Service_AM, "(STUBBED) called"); } std::shared_ptr applet; From 731b4bd69185e741282415b66cc39fd482c039b9 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:08:25 -0500 Subject: [PATCH 08/29] am: Unstub ILibraryAppletAccessor::Start Now starts the applet provided in constructor. --- src/core/hle/service/am/am.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index b78489df7a..4b7aacbace 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -6,10 +6,14 @@ #include #include #include +#include "applets/applets.h" +#include "applets/software_keyboard.h" +#include "audio_core/audio_renderer.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" @@ -28,6 +32,10 @@ namespace Service::AM { +enum class AppletId : u32 { + SoftwareKeyboard = 0x11, +}; + constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; struct LaunchParameters { @@ -571,13 +579,17 @@ private: LOG_WARNING(Service_AM, "(STUBBED) called"); } - void Write(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; + void Start(Kernel::HLERequestContext& ctx) { + ASSERT(applet != nullptr); - const u64 offset{rp.Pop()}; - const std::vector data{ctx.ReadBuffer()}; + applet->Initialize(storage_stack); + interactive_storage_stack.push_back(std::make_shared(applet->Execute())); + state_changed_event->Signal(); + pop_interactive_out_data_event->Signal(); - ASSERT(offset + data.size() <= buffer.size()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } void PushInData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; From 5b95de0c9cbe5185283e36426035798e05555c21 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:09:37 -0500 Subject: [PATCH 09/29] am/applets: Add Applet superclass to describe a generic applet Adds an Initialize and Execute methods which are used by the ILibraryAppletAccessor to start and control the applet. --- src/core/CMakeLists.txt | 2 + src/core/hle/service/am/applets/applets.cpp | 29 +++++++++++++ src/core/hle/service/am/applets/applets.h | 46 +++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 src/core/hle/service/am/applets/applets.cpp create mode 100644 src/core/hle/service/am/applets/applets.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 64fdf38cd6..698f21a397 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -150,6 +150,8 @@ add_library(core STATIC hle/service/am/applet_ae.h hle/service/am/applet_oe.cpp hle/service/am/applet_oe.h + hle/service/am/applets/applets.cpp + hle/service/am/applets/applets.h hle/service/am/idle.cpp hle/service/am/idle.h hle/service/am/omm.cpp diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp new file mode 100644 index 0000000000..8cc4b0f1ae --- /dev/null +++ b/src/core/hle/service/am/applets/applets.cpp @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +std::shared_ptr software_keyboard = + std::make_shared(); + +void Applet::Initialize(std::vector> storage) { + storage_stack = std::move(storage); + initialized = true; +} + +void RegisterSoftwareKeyboard(std::shared_ptr applet) { + if (applet == nullptr) + return; + + software_keyboard = std::move(applet); +} + +std::shared_ptr GetSoftwareKeyboard() { + return software_keyboard; +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h new file mode 100644 index 0000000000..1f91392b47 --- /dev/null +++ b/src/core/hle/service/am/applets/applets.h @@ -0,0 +1,46 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/swap.h" + +namespace Frontend { +class SoftwareKeyboardApplet; +} + +namespace Service::AM { + +class IStorage; + +namespace Applets { + +class Applet { +public: + virtual void Initialize(std::vector> storage); + + virtual IStorage Execute() = 0; + +protected: + struct CommonArguments { + u32_le arguments_version; + u32_le size; + u32_le library_version; + u32_le theme_color; + u8 play_startup_sound; + u64_le system_tick; + }; + static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); + + std::vector> storage_stack; + bool initialized = false; +}; + +void RegisterSoftwareKeyboard(std::shared_ptr applet); +std::shared_ptr GetSoftwareKeyboard(); + +} // namespace Applets +} // namespace Service::AM From ae53b84efd5523e2aba3255f9f713d4a1df563b3 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:10:58 -0500 Subject: [PATCH 10/29] frontend/applets: Add frontend software keyboard provider and default Default implementation will return "yuzu" for any string. GUI clients (or CLI) can implement the Frontend::SoftwareKeyboardApplet class and register an instance to provide functionality. --- src/core/CMakeLists.txt | 2 + .../frontend/applets/software_keyboard.cpp | 17 +++++++ src/core/frontend/applets/software_keyboard.h | 44 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 src/core/frontend/applets/software_keyboard.cpp create mode 100644 src/core/frontend/applets/software_keyboard.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 698f21a397..03ecb2c8cd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -77,6 +77,8 @@ add_library(core STATIC file_sys/vfs_vector.h file_sys/xts_archive.cpp file_sys/xts_archive.h + frontend/applets/software_keyboard.cpp + frontend/applets/software_keyboard.h frontend/emu_window.cpp frontend/emu_window.h frontend/framebuffer_layout.cpp diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp new file mode 100644 index 0000000000..8cb888a629 --- /dev/null +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -0,0 +1,17 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/backend.h" +#include "core/frontend/applets/software_keyboard.h" + +namespace Frontend { +bool DefaultSoftwareKeyboardApplet::GetText(Parameters parameters, std::u16string& text) { + if (parameters.initial_text.empty()) + text = Common::UTF8ToUTF16("yuzu"); + else + text = parameters.initial_text; + + return true; +} +} // namespace Frontend diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h new file mode 100644 index 0000000000..d368385d24 --- /dev/null +++ b/src/core/frontend/applets/software_keyboard.h @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/bit_field.h" +#include "common/common_types.h" + +namespace Frontend { +class SoftwareKeyboardApplet { +public: + struct Parameters { + std::u16string submit_text; + std::u16string header_text; + std::u16string sub_text; + std::u16string guide_text; + std::u16string initial_text; + std::size_t max_length; + bool password; + bool cursor_at_beginning; + + union { + u8 value; + + BitField<1, 1, u8> disable_space; + BitField<2, 1, u8> disable_address; + BitField<3, 1, u8> disable_percent; + BitField<4, 1, u8> disable_slash; + BitField<6, 1, u8> disable_number; + BitField<7, 1, u8> disable_download_code; + }; + }; + + virtual bool GetText(Parameters parameters, std::u16string& text) = 0; + virtual ~SoftwareKeyboardApplet() = default; +}; + +class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { + bool GetText(Parameters parameters, std::u16string& text) override; +}; + +} // namespace Frontend From de16c1e45326a5bb587a2c270b9b39042b245f7c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:12:12 -0500 Subject: [PATCH 11/29] am/applets: Add connector between frontend and AM applet classes Provides a middleman between the Frontend provider class and the expected AM::Applets::Applet class needed by ILibraryAppletAccessor --- src/core/CMakeLists.txt | 2 + .../service/am/applets/software_keyboard.cpp | 71 +++++++++++++++++++ .../service/am/applets/software_keyboard.h | 57 +++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 src/core/hle/service/am/applets/software_keyboard.cpp create mode 100644 src/core/hle/service/am/applets/software_keyboard.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 03ecb2c8cd..a355eaca64 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -154,6 +154,8 @@ add_library(core STATIC hle/service/am/applet_oe.h hle/service/am/applets/applets.cpp hle/service/am/applets/applets.h + hle/service/am/applets/software_keyboard.cpp + hle/service/am/applets/software_keyboard.h hle/service/am/idle.cpp hle/service/am/idle.h hle/service/am/omm.cpp diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp new file mode 100644 index 0000000000..ad1797ef18 --- /dev/null +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -0,0 +1,71 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/string_util.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/software_keyboard.h" + +namespace Service::AM::Applets { + +constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; +constexpr std::size_t DEFAULT_MAX_LENGTH = 500; + +static Frontend::SoftwareKeyboardApplet::Parameters ConvertToFrontendParameters( + KeyboardConfig config, std::u16string initial_text) { + Frontend::SoftwareKeyboardApplet::Parameters params{}; + + params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + config.submit_text.data(), config.submit_text.size()); + params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + config.header_text.data(), config.header_text.size()); + params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(), + config.sub_text.size()); + params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), + config.guide_text.size()); + params.initial_text = initial_text; + params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; + params.password = static_cast(config.is_password); + params.cursor_at_beginning = static_cast(config.initial_cursor_position); + params.value = static_cast(config.keyset_disable_bitmask); + + return params; +} + +void SoftwareKeyboard::Initialize(std::vector> storage_) { + Applet::Initialize(std::move(storage_)); + + ASSERT(storage_stack.size() >= 2); + const auto& keyboard_config = storage_stack[1]->GetData(); + 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); +} + +IStorage SoftwareKeyboard::Execute() { + const auto frontend{GetSoftwareKeyboard()}; + ASSERT(frontend != nullptr); + + const auto parameters = ConvertToFrontendParameters(config, initial_text); + + std::u16string text; + const auto success = frontend->GetText(parameters, text); + + std::vector output(SWKBD_OUTPUT_BUFFER_SIZE); + + if (success) { + output[0] = 1; + std::memcpy(output.data() + 4, text.data(), + std::min(text.size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + } + + return IStorage{output}; +} +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h new file mode 100644 index 0000000000..9a37ba45fd --- /dev/null +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -0,0 +1,57 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_funcs.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +enum class KeysetDisable : u32 { + Space = 0x02, + Address = 0x04, + Percent = 0x08, + Slashes = 0x10, + Numbers = 0x40, + DownloadCode = 0x80, +}; + +struct KeyboardConfig { + INSERT_PADDING_BYTES(4); + std::array submit_text; + u16_le left_symbol_key; + u16_le right_symbol_key; + INSERT_PADDING_BYTES(1); + KeysetDisable keyset_disable_bitmask; + u32_le initial_cursor_position; + std::array header_text; + std::array sub_text; + std::array guide_text; + u32_le length_limit; + INSERT_PADDING_BYTES(4); + u32_le is_password; + INSERT_PADDING_BYTES(6); + bool draw_background; + u32_le initial_string_offset; + u32_le initial_string_size; + u32_le user_dictionary_offset; + u32_le user_dictionary_size; + bool text_check; + u64_le text_check_callback; +}; +static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size."); + +class SoftwareKeyboard final : public Applet { +public: + void Initialize(std::vector> storage) override; + + IStorage Execute() override; + +private: + KeyboardConfig config; + std::u16string initial_text; +}; + +} // namespace Service::AM::Applets From 5454494adbfad3148e75b45653a255004ca989b3 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:13:15 -0500 Subject: [PATCH 12/29] qt/applets: Provide Qt frontend implementation of software keyboard Implements all of the features of the keyboard, including length, default text, character validation, and UTF-16 character support. --- src/yuzu/CMakeLists.txt | 2 + src/yuzu/applets/software_keyboard.cpp | 107 +++++++++++++++++++++++++ src/yuzu/applets/software_keyboard.h | 62 ++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 src/yuzu/applets/software_keyboard.cpp create mode 100644 src/yuzu/applets/software_keyboard.h diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index f9ca2948ee..53c7f00d44 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -7,6 +7,8 @@ add_executable(yuzu Info.plist about_dialog.cpp about_dialog.h + applets/software_keyboard.cpp + applets/software_keyboard.h bootmanager.cpp bootmanager.h compatibility_list.cpp diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp new file mode 100644 index 0000000000..9e1c59626b --- /dev/null +++ b/src/yuzu/applets/software_keyboard.cpp @@ -0,0 +1,107 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include "common/logging/backend.h" +#include "yuzu/applets/software_keyboard.h" + +QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( + Frontend::SoftwareKeyboardApplet::Parameters parameters) + : parameters(std::move(parameters)) {} + +QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int&) const { + if (input.size() > parameters.max_length) + return Invalid; + if (parameters.disable_space && input.contains(' ')) + return Invalid; + if (parameters.disable_address && input.contains('@')) + return Invalid; + if (parameters.disable_percent && input.contains('%')) + return Invalid; + if (parameters.disable_slash && (input.contains('/') || input.contains('\\'))) + return Invalid; + if (parameters.disable_number && + std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) + return Invalid; + + if (parameters.disable_download_code && + std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) + return Invalid; + + return Acceptable; +} + +QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( + QWidget* parent, Frontend::SoftwareKeyboardApplet::Parameters parameters_) + : QDialog(parent), parameters(std::move(parameters_)) { + layout = new QVBoxLayout; + + header_label = new QLabel(QString::fromStdU16String(parameters.header_text)); + header_label->setFont({header_label->font().family(), 12, QFont::Bold}); + if (header_label->text().isEmpty()) + header_label->setText(tr("Enter text:")); + + sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text)); + sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(), + sub_label->font().weight(), true}); + sub_label->setHidden(parameters.sub_text.empty()); + + guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text)); + guide_label->setHidden(parameters.guide_text.empty()); + + line_edit = new QLineEdit; + line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters)); + line_edit->setMaxLength(static_cast(parameters.max_length)); + line_edit->setText(QString::fromStdU16String(parameters.initial_text)); + line_edit->setCursorPosition( + parameters.cursor_at_beginning ? 0 : static_cast(parameters.initial_text.size())); + line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal); + + buttons = new QDialogButtonBox; + buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); + buttons->addButton(parameters.submit_text.empty() + ? tr("OK") + : QString::fromStdU16String(parameters.submit_text), + QDialogButtonBox::AcceptRole); + + connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::Submit); + connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::Reject); + layout->addWidget(header_label); + layout->addWidget(sub_label); + layout->addWidget(guide_label); + layout->addWidget(line_edit); + layout->addWidget(buttons); + setLayout(layout); + setWindowTitle("Software Keyboard"); +} + +void QtSoftwareKeyboardDialog::Submit() { + ok = true; + text = line_edit->text().toStdU16String(); + accept(); +} + +void QtSoftwareKeyboardDialog::Reject() { + ok = false; + text = Common::UTF8ToUTF16(""); + accept(); +} + +QtSoftwareKeyboard::QtSoftwareKeyboard(QWidget& parent) : parent(parent) {} + +bool QtSoftwareKeyboard::GetText(Parameters parameters, std::u16string& text) { + QtSoftwareKeyboardDialog dialog(&parent, parameters); + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); + dialog.setWindowModality(Qt::WindowModal); + dialog.exec(); + + text = dialog.text; + return dialog.ok; +} diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h new file mode 100644 index 0000000000..2a18419db4 --- /dev/null +++ b/src/yuzu/applets/software_keyboard.h @@ -0,0 +1,62 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once +#include +#include +#include "common/assert.h" +#include "core/frontend/applets/software_keyboard.h" + +class QDialogButtonBox; +class QLabel; +class QLineEdit; +class QVBoxLayout; +class QtSoftwareKeyboard; + +class QtSoftwareKeyboardValidator final : public QValidator { +public: + explicit QtSoftwareKeyboardValidator(Frontend::SoftwareKeyboardApplet::Parameters parameters); + State validate(QString&, int&) const override; + +private: + Frontend::SoftwareKeyboardApplet::Parameters parameters; +}; + +class QtSoftwareKeyboardDialog final : public QDialog { + Q_OBJECT + +public: + QtSoftwareKeyboardDialog(QWidget* parent, + Frontend::SoftwareKeyboardApplet::Parameters parameters); + void Submit(); + void Reject(); + +private: + bool ok = false; + std::u16string text; + + QDialogButtonBox* buttons; + QLabel* header_label; + QLabel* sub_label; + QLabel* guide_label; + QLineEdit* line_edit; + QVBoxLayout* layout; + + Frontend::SoftwareKeyboardApplet::Parameters parameters; + + friend class QtSoftwareKeyboard; +}; + +class QtSoftwareKeyboard final : public QObject, public Frontend::SoftwareKeyboardApplet { +public: + explicit QtSoftwareKeyboard(QWidget& parent); + bool GetText(Parameters parameters, std::u16string& text) override; + + ~QtSoftwareKeyboard() { + UNREACHABLE(); + } + +private: + QWidget& parent; +}; From 48fcb43585132e52acaf9d859dd5d39fb308cb14 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:14:01 -0500 Subject: [PATCH 13/29] am: Construct and use proper applets with ILibraryAppletAccessor Allows use of software keyboard applet and future applets to be easily added by adding enum ID and a switch case. --- src/core/hle/service/am/am.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4b7aacbace..53580d673a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -725,11 +725,36 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple ILibraryAppletCreator::~ILibraryAppletCreator() = default; +static std::shared_ptr GetAppletFromId(AppletId id) { + switch (id) { + case AppletId::SoftwareKeyboard: + return std::make_shared(); + default: + UNREACHABLE_MSG("Unimplemented AppletId [{:08X}]!", static_cast(id)); + return nullptr; + } +} + void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_id = rp.PopRaw(); + const auto applet_mode = rp.PopRaw(); + + LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", + static_cast(applet_id), applet_mode); + + const auto applet = GetAppletFromId(applet_id); + + if (applet == nullptr) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); + return; + } + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(); + rb.PushIpcInterface(applet); LOG_DEBUG(Service_AM, "called"); } From a81645400f9ba40b272163ef6f751ba3428b85a1 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 9 Nov 2018 20:38:11 -0500 Subject: [PATCH 14/29] qt/main: Register Qt Software Keyboard frontend with AM Allows using Qt provider over default. --- src/core/frontend/applets/software_keyboard.cpp | 1 + src/yuzu/applets/software_keyboard.cpp | 1 + src/yuzu/main.cpp | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 8cb888a629..c1bacefef3 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/logging/backend.h" +#include "common/string_util.h" #include "core/frontend/applets/software_keyboard.h" namespace Frontend { diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 9e1c59626b..bd8bd0dd01 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -9,6 +9,7 @@ #include #include #include "common/logging/backend.h" +#include "common/string_util.h" #include "yuzu/applets/software_keyboard.h" QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 999086e7f2..7b2a01169d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -8,9 +8,11 @@ #include // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. +#include "applets/software_keyboard.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" #include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/applets/applets.h" // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows // defines. @@ -559,6 +561,8 @@ bool GMainWindow::LoadROM(const QString& filename) { system.SetGPUDebugContext(debug_context); + Service::AM::Applets::RegisterSoftwareKeyboard(std::make_shared(*this)); + const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; const auto drd_callout = From e696ed1f4d20f28f8b26c637498962938df7d96f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 11 Nov 2018 16:39:25 -0500 Subject: [PATCH 15/29] am: Deglobalize software keyboard applet --- src/common/string_util.cpp | 4 +- src/common/string_util.h | 4 +- src/core/core.cpp | 17 ++++++ src/core/core.h | 5 ++ .../frontend/applets/software_keyboard.cpp | 13 ++-- src/core/frontend/applets/software_keyboard.h | 54 +++++++++-------- src/core/hle/kernel/svc.cpp | 8 +-- src/core/hle/service/am/am.cpp | 14 +++-- src/core/hle/service/am/am.h | 10 ++-- src/core/hle/service/am/applets/applets.cpp | 16 +---- src/core/hle/service/am/applets/applets.h | 10 +++- .../service/am/applets/software_keyboard.cpp | 12 +++- .../service/am/applets/software_keyboard.h | 9 +++ src/yuzu/applets/software_keyboard.cpp | 59 ++++++++++++------- src/yuzu/applets/software_keyboard.h | 33 ++++++----- src/yuzu/main.cpp | 7 ++- src/yuzu/main.h | 9 +++ 17 files changed, 182 insertions(+), 102 deletions(-) diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index a1360dd268..959f278aaf 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -214,13 +214,13 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t return std::string(buffer, len); } -std::u16string UTF16StringFromFixedZeroTerminatedBuffer(const char16_t* buffer, +std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, std::size_t max_len) { std::size_t len = 0; while (len < max_len && buffer[len] != '\0') ++len; - return std::u16string(buffer, len); + return std::u16string(buffer.begin(), buffer.begin() + len); } const char* TrimSourcePath(const char* path, const char* root) { diff --git a/src/common/string_util.h b/src/common/string_util.h index 95b7badaf3..583fd05e68 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -68,10 +68,10 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t /** * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't - * NUL-terminated, then the string ends at the greatest multiple of two less then or equal to + * null-terminated, then the string ends at the greatest multiple of two less then or equal to * max_len_bytes. */ -std::u16string UTF16StringFromFixedZeroTerminatedBuffer(const char16_t* buffer, +std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, std::size_t max_len); /** diff --git a/src/core/core.cpp b/src/core/core.cpp index 6d5b5a2d01..6c72fdf4a0 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -23,12 +23,14 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" +#include "core/hle/service/am/applets/software_keyboard.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" #include "core/perf_stats.h" #include "core/settings.h" #include "core/telemetry_session.h" +#include "frontend/applets/software_keyboard.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/gpu.h" #include "video_core/renderer_base.h" @@ -136,6 +138,10 @@ struct System::Impl { if (virtual_filesystem == nullptr) virtual_filesystem = std::make_shared(); + /// Create default implementations of applets if one is not provided. + if (software_keyboard == nullptr) + software_keyboard = std::make_unique(); + auto main_process = Kernel::Process::Create(kernel, "main"); kernel.MakeCurrentProcess(main_process.get()); @@ -289,6 +295,9 @@ struct System::Impl { std::array, NUM_CPU_CORES - 1> cpu_core_threads; std::size_t active_core{}; ///< Active core, only used in single thread mode + /// Frontend applets + std::unique_ptr software_keyboard; + /// Service manager std::shared_ptr service_manager; @@ -488,6 +497,14 @@ std::shared_ptr System::GetFilesystem() const { return impl->virtual_filesystem; } +void System::SetSoftwareKeyboard(std::unique_ptr applet) { + impl->software_keyboard = std::move(applet); +} + +const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const { + return *impl->software_keyboard; +} + System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { return impl->Init(emu_window); } diff --git a/src/core/core.h b/src/core/core.h index cfacceb81c..be71bd4378 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -13,6 +13,7 @@ namespace Core::Frontend { class EmuWindow; +class SoftwareKeyboardApplet; } // namespace Core::Frontend namespace FileSys { @@ -236,6 +237,10 @@ public: std::shared_ptr GetFilesystem() const; + void SetSoftwareKeyboard(std::unique_ptr applet); + + const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; + private: System(); diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index c1bacefef3..41d81c2934 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -3,16 +3,19 @@ // Refer to the license.txt file included. #include "common/logging/backend.h" -#include "common/string_util.h" #include "core/frontend/applets/software_keyboard.h" -namespace Frontend { -bool DefaultSoftwareKeyboardApplet::GetText(Parameters parameters, std::u16string& text) { +namespace Core::Frontend { +SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default; + +bool DefaultSoftwareKeyboardApplet::GetText(SoftwareKeyboardParameters parameters, + std::u16string& text) const { if (parameters.initial_text.empty()) - text = Common::UTF8ToUTF16("yuzu"); + text = u"yuzu"; else text = parameters.initial_text; return true; } -} // namespace Frontend + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index d368385d24..2ea9db889b 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -8,37 +8,39 @@ #include "common/bit_field.h" #include "common/common_types.h" -namespace Frontend { +namespace Core::Frontend { +struct SoftwareKeyboardParameters { + std::u16string submit_text; + std::u16string header_text; + std::u16string sub_text; + std::u16string guide_text; + std::u16string initial_text; + std::size_t max_length; + bool password; + bool cursor_at_beginning; + + union { + u8 value; + + BitField<1, 1, u8> disable_space; + BitField<2, 1, u8> disable_address; + BitField<3, 1, u8> disable_percent; + BitField<4, 1, u8> disable_slash; + BitField<6, 1, u8> disable_number; + BitField<7, 1, u8> disable_download_code; + }; +}; + class SoftwareKeyboardApplet { public: - struct Parameters { - std::u16string submit_text; - std::u16string header_text; - std::u16string sub_text; - std::u16string guide_text; - std::u16string initial_text; - std::size_t max_length; - bool password; - bool cursor_at_beginning; + virtual ~SoftwareKeyboardApplet(); - union { - u8 value; - - BitField<1, 1, u8> disable_space; - BitField<2, 1, u8> disable_address; - BitField<3, 1, u8> disable_percent; - BitField<4, 1, u8> disable_slash; - BitField<6, 1, u8> disable_number; - BitField<7, 1, u8> disable_download_code; - }; - }; - - virtual bool GetText(Parameters parameters, std::u16string& text) = 0; - virtual ~SoftwareKeyboardApplet() = default; + virtual bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const = 0; }; class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { - bool GetText(Parameters parameters, std::u16string& text) override; +public: + bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const override; }; -} // namespace Frontend +} // namespace Core::Frontend diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 20af65ee79..f84b00a009 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1207,14 +1207,15 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 return ERR_INVALID_ADDRESS; } - if (addr + size <= addr) { + if (!IsValidAddressRange(addr, size)) { LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})", addr, size); return ERR_INVALID_ADDRESS_STATE; } - if (permissions > static_cast(MemoryPermission::ReadWrite) || - permissions == static_cast(MemoryPermission::Write)) { + const auto perms = static_cast(permissions); + if (perms != MemoryPermission::None && perms != MemoryPermission::Read && + perms != MemoryPermission::ReadWrite) { LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})", permissions); return ERR_INVALID_MEMORY_PERMISSIONS; @@ -1222,7 +1223,6 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 auto& kernel = Core::System::GetInstance().Kernel(); auto& handle_table = Core::CurrentProcess()->GetHandleTable(); - const auto perms = static_cast(permissions); const auto shared_mem_handle = SharedMemory::Create( kernel, handle_table.Get(CurrentProcess), size, perms, perms, addr); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 53580d673a..fc464270e8 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -501,6 +501,8 @@ IStorage::IStorage(std::vector buffer) RegisterHandlers(functions); } +IStorage::~IStorage() = default; + const std::vector& IStorage::GetData() const { return buffer; } @@ -670,6 +672,8 @@ IStorageAccessor::IStorageAccessor(IStorage& storage) RegisterHandlers(functions); } +IStorageAccessor::~IStorageAccessor() = default; + void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; @@ -685,7 +689,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop()}; const std::vector data{ctx.ReadBuffer()}; - const auto size = std::min(data.size(), backing.buffer.size() - offset); + const auto size = std::min(data.size(), backing.buffer.size() - offset); std::memcpy(&backing.buffer[offset], data.data(), size); @@ -701,7 +705,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop()}; std::size_t size{ctx.GetWriteBufferSize()}; - size = std::min(size, backing.buffer.size() - offset); + size = std::min(size, backing.buffer.size() - offset); ctx.WriteBuffer(backing.buffer.data() + offset, size); @@ -787,9 +791,9 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex return; } - std::vector memory(shared_mem->size); - std::memcpy(memory.data(), shared_mem->backing_block->data() + shared_mem->backing_block_offset, - memory.size()); + const auto mem_begin = shared_mem->backing_block->begin() + shared_mem->backing_block_offset; + const auto mem_end = mem_begin + shared_mem->size; + std::vector memory{mem_begin, mem_end}; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 640901e4af..44c1bcde5d 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -158,27 +158,29 @@ private: class IStorage final : public ServiceFramework { public: explicit IStorage(std::vector buffer); + ~IStorage() override; const std::vector& GetData() const; private: - std::vector buffer; - void Open(Kernel::HLERequestContext& ctx); + std::vector buffer; + friend class IStorageAccessor; }; class IStorageAccessor final : public ServiceFramework { public: explicit IStorageAccessor(IStorage& backing); + ~IStorageAccessor() override; private: - IStorage& backing; - void GetSize(Kernel::HLERequestContext& ctx); void Write(Kernel::HLERequestContext& ctx); void Read(Kernel::HLERequestContext& ctx); + + IStorage& backing; }; class ILibraryAppletCreator final : public ServiceFramework { diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 8cc4b0f1ae..03b9d83e75 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -7,23 +7,13 @@ namespace Service::AM::Applets { -std::shared_ptr software_keyboard = - std::make_shared(); +Applet::Applet() = default; + +Applet::~Applet() = default; void Applet::Initialize(std::vector> storage) { storage_stack = std::move(storage); initialized = true; } -void RegisterSoftwareKeyboard(std::shared_ptr applet) { - if (applet == nullptr) - return; - - software_keyboard = std::move(applet); -} - -std::shared_ptr GetSoftwareKeyboard() { - return software_keyboard; -} - } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 1f91392b47..47db22fb4d 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -20,10 +20,17 @@ namespace Applets { class Applet { public: + Applet(); + virtual ~Applet(); + virtual void Initialize(std::vector> storage); virtual IStorage Execute() = 0; + bool IsInitialized() const { + return initialized; + } + protected: struct CommonArguments { u32_le arguments_version; @@ -39,8 +46,5 @@ protected: bool initialized = false; }; -void RegisterSoftwareKeyboard(std::shared_ptr applet); -std::shared_ptr GetSoftwareKeyboard(); - } // namespace Applets } // namespace Service::AM diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index ad1797ef18..556dea3e46 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "common/assert.h" #include "common/string_util.h" +#include "core/core.h" #include "core/frontend/applets/software_keyboard.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/software_keyboard.h" @@ -11,11 +13,13 @@ namespace Service::AM::Applets { constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; +constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; constexpr std::size_t DEFAULT_MAX_LENGTH = 500; +constexpr bool INTERACTIVE_STATUS_OK = false; -static Frontend::SoftwareKeyboardApplet::Parameters ConvertToFrontendParameters( +static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( KeyboardConfig config, std::u16string initial_text) { - Frontend::SoftwareKeyboardApplet::Parameters params{}; + Core::Frontend::SoftwareKeyboardParameters params{}; params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( config.submit_text.data(), config.submit_text.size()); @@ -34,6 +38,10 @@ static Frontend::SoftwareKeyboardApplet::Parameters ConvertToFrontendParameters( return params; } +SoftwareKeyboard::SoftwareKeyboard() = default; + +SoftwareKeyboard::~SoftwareKeyboard() = default; + void SoftwareKeyboard::Initialize(std::vector> storage_) { Applet::Initialize(std::move(storage_)); diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 9a37ba45fd..9d77f5802b 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -5,6 +5,7 @@ #pragma once #include "common/common_funcs.h" +#include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" namespace Service::AM::Applets { @@ -45,13 +46,21 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz class SoftwareKeyboard final : public Applet { public: + SoftwareKeyboard(); + ~SoftwareKeyboard() override; + void Initialize(std::vector> storage) override; + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ReceiveInteractiveData(std::shared_ptr storage) override; IStorage Execute() override; private: KeyboardConfig config; std::u16string initial_text; + bool complete = false; + std::vector final_data; }; } // namespace Service::AM::Applets diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index bd8bd0dd01..fad150ec18 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -2,21 +2,20 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include +#include #include #include #include #include #include -#include "common/logging/backend.h" -#include "common/string_util.h" #include "yuzu/applets/software_keyboard.h" +#include "yuzu/main.h" QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( - Frontend::SoftwareKeyboardApplet::Parameters parameters) + Core::Frontend::SoftwareKeyboardParameters parameters) : parameters(std::move(parameters)) {} -QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int&) const { +QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { if (input.size() > parameters.max_length) return Invalid; if (parameters.disable_space && input.contains(' ')) @@ -28,18 +27,20 @@ QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int&) co if (parameters.disable_slash && (input.contains('/') || input.contains('\\'))) return Invalid; if (parameters.disable_number && - std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) + std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { return Invalid; + } if (parameters.disable_download_code && - std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) + std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) { return Invalid; + } return Acceptable; } QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( - QWidget* parent, Frontend::SoftwareKeyboardApplet::Parameters parameters_) + QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_) : QDialog(parent), parameters(std::move(parameters_)) { layout = new QVBoxLayout; @@ -79,9 +80,11 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( layout->addWidget(line_edit); layout->addWidget(buttons); setLayout(layout); - setWindowTitle("Software Keyboard"); + setWindowTitle(tr("Software Keyboard")); } +QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; + void QtSoftwareKeyboardDialog::Submit() { ok = true; text = line_edit->text().toStdU16String(); @@ -90,19 +93,33 @@ void QtSoftwareKeyboardDialog::Submit() { void QtSoftwareKeyboardDialog::Reject() { ok = false; - text = Common::UTF8ToUTF16(""); + text.clear(); accept(); } -QtSoftwareKeyboard::QtSoftwareKeyboard(QWidget& parent) : parent(parent) {} - -bool QtSoftwareKeyboard::GetText(Parameters parameters, std::u16string& text) { - QtSoftwareKeyboardDialog dialog(&parent, parameters); - dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | - Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); - dialog.setWindowModality(Qt::WindowModal); - dialog.exec(); - - text = dialog.text; - return dialog.ok; +std::u16string QtSoftwareKeyboardDialog::GetText() { + return text; +} + +bool QtSoftwareKeyboardDialog::GetStatus() { + return ok; +} + +QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& parent) : main_window(parent) {} + +QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; + +bool QtSoftwareKeyboard::GetText(Core::Frontend::SoftwareKeyboardParameters parameters, + std::u16string& text) const { + bool success; + QMetaObject::invokeMethod(&main_window, "SoftwareKeyboardGetText", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(Core::Frontend::SoftwareKeyboardParameters, parameters), + Q_ARG(std::u16string&, text)); + return success; +} + +void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message) const { + QMetaObject::invokeMethod(&main_window, "SoftwareKeyboardInvokeCheckDialog", + Qt::BlockingQueuedConnection, Q_ARG(std::u16string, error_message)); } diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 2a18419db4..1069c10ec8 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -3,11 +3,13 @@ // Refer to the license.txt file included. #pragma once + #include #include #include "common/assert.h" #include "core/frontend/applets/software_keyboard.h" +class GMainWindow; class QDialogButtonBox; class QLabel; class QLineEdit; @@ -16,11 +18,11 @@ class QtSoftwareKeyboard; class QtSoftwareKeyboardValidator final : public QValidator { public: - explicit QtSoftwareKeyboardValidator(Frontend::SoftwareKeyboardApplet::Parameters parameters); - State validate(QString&, int&) const override; + explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters); + State validate(QString& input, int& pos) const override; private: - Frontend::SoftwareKeyboardApplet::Parameters parameters; + Core::Frontend::SoftwareKeyboardParameters parameters; }; class QtSoftwareKeyboardDialog final : public QDialog { @@ -28,10 +30,15 @@ class QtSoftwareKeyboardDialog final : public QDialog { public: QtSoftwareKeyboardDialog(QWidget* parent, - Frontend::SoftwareKeyboardApplet::Parameters parameters); + Core::Frontend::SoftwareKeyboardParameters parameters); + ~QtSoftwareKeyboardDialog() override; + void Submit(); void Reject(); + std::u16string GetText(); + bool GetStatus(); + private: bool ok = false; std::u16string text; @@ -43,20 +50,18 @@ private: QLineEdit* line_edit; QVBoxLayout* layout; - Frontend::SoftwareKeyboardApplet::Parameters parameters; - - friend class QtSoftwareKeyboard; + Core::Frontend::SoftwareKeyboardParameters parameters; }; -class QtSoftwareKeyboard final : public QObject, public Frontend::SoftwareKeyboardApplet { +class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet { public: - explicit QtSoftwareKeyboard(QWidget& parent); - bool GetText(Parameters parameters, std::u16string& text) override; + explicit QtSoftwareKeyboard(GMainWindow& parent); + ~QtSoftwareKeyboard() override; - ~QtSoftwareKeyboard() { - UNREACHABLE(); - } + bool GetText(Core::Frontend::SoftwareKeyboardParameters parameters, + std::u16string& text) const override; + void SendTextCheckDialog(std::u16string error_message) const override; private: - QWidget& parent; + GMainWindow& main_window; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 7b2a01169d..9b2c09f324 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -561,7 +561,7 @@ bool GMainWindow::LoadROM(const QString& filename) { system.SetGPUDebugContext(debug_context); - Service::AM::Applets::RegisterSoftwareKeyboard(std::make_shared(*this)); + system.SetSoftwareKeyboard(std::make_unique(*this)); const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; @@ -1232,8 +1232,13 @@ void GMainWindow::OnMenuRecentFile() { void GMainWindow::OnStartGame() { emu_thread->SetRunning(true); + + qRegisterMetaType( + "core::Frontend::SoftwareKeyboardParameters"); qRegisterMetaType("Core::System::ResultStatus"); qRegisterMetaType("std::string"); + qRegisterMetaType("std::u16string"); + connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); ui.action_Start->setEnabled(false); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 929250e8cd..38074e3f0e 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -29,6 +29,10 @@ class ProfilerWidget; class WaitTreeWidget; enum class GameListOpenTarget; +namespace Core::Frontend { +struct SoftwareKeyboardParameters; +} // namespace Core::Frontend + namespace FileSys { class RegisteredCacheUnion; class VfsFilesystem; @@ -95,6 +99,11 @@ signals: // Signal that tells widgets to update icons to use the current theme void UpdateThemedIcons(); +public slots: + bool SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters, + std::u16string& text); + void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); + private: void InitializeWidgets(); void InitializeDebugWidgets(); From fed6ab14c37f196f2a2fd378b46d7e5bd0118224 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 11 Nov 2018 16:41:31 -0500 Subject: [PATCH 16/29] 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 41d81c2934..05e2dc6b78 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 2ea9db889b..0a82aac0d1 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 fc464270e8..ea00c5c720 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 47db22fb4d..6d90eb608f 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 556dea3e46..044a162648 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 9b2c09f324..447d9dece8 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); From 3cf7246e376445320e2b55165265228736f65214 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 11 Nov 2018 16:57:59 -0500 Subject: [PATCH 17/29] am: Implement ILibraryAppletAccessor IsCompleted and GetResult --- src/core/frontend/applets/software_keyboard.cpp | 1 + src/core/hle/service/am/am.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 05e2dc6b78..cf5e2ea31c 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/logging/backend.h" +#include "common/string_util.h" #include "core/frontend/applets/software_keyboard.h" namespace Core::Frontend { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index ea00c5c720..3201e2d4bc 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -533,7 +533,7 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, - {1, nullptr, "IsCompleted"}, + {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"}, {10, &ILibraryAppletAccessor::Start, "Start"}, {20, nullptr, "RequestExit"}, {25, nullptr, "Terminate"}, @@ -576,11 +576,15 @@ private: LOG_WARNING(Service_AM, "(STUBBED) called"); } + void IsCompleted(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(applet->TransactionComplete()); + } + void GetResult(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); + rb.Push(applet->GetStatus()); } void Start(Kernel::HLERequestContext& ctx) { From 7cfb29de23836aa1873bbb108e3d25a0e9dcfa6d Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 11 Nov 2018 20:16:38 -0500 Subject: [PATCH 18/29] am: Allow applets to push multiple and different channels of data --- .../frontend/applets/software_keyboard.cpp | 10 +++--- src/core/frontend/applets/software_keyboard.h | 5 +-- src/core/hle/service/am/am.cpp | 34 +++++++++---------- src/core/hle/service/am/applets/applets.h | 6 +++- .../service/am/applets/software_keyboard.cpp | 27 ++++++--------- .../service/am/applets/software_keyboard.h | 3 +- src/yuzu/applets/software_keyboard.cpp | 15 ++++---- src/yuzu/applets/software_keyboard.h | 8 ++--- src/yuzu/main.cpp | 14 ++++---- src/yuzu/main.h | 4 +-- 10 files changed, 62 insertions(+), 64 deletions(-) diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index cf5e2ea31c..2445a980d1 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -9,14 +9,12 @@ namespace Core::Frontend { SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default; -bool DefaultSoftwareKeyboardApplet::GetText(SoftwareKeyboardParameters parameters, - std::u16string& text) const { +std::optional DefaultSoftwareKeyboardApplet::GetText( + SoftwareKeyboardParameters parameters) const { if (parameters.initial_text.empty()) - text = u"yuzu"; - else - text = parameters.initial_text; + return u"yuzu"; - return true; + return parameters.initial_text; } void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(std::u16string error_message) const { diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 0a82aac0d1..7387ee8d70 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "common/bit_field.h" #include "common/common_types.h" @@ -35,13 +36,13 @@ class SoftwareKeyboardApplet { public: virtual ~SoftwareKeyboardApplet(); - virtual bool GetText(SoftwareKeyboardParameters parameters, std::u16string& text) const = 0; + virtual std::optional GetText(SoftwareKeyboardParameters parameters) 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; + std::optional GetText(SoftwareKeyboardParameters parameters) const override; void SendTextCheckDialog(std::u16string error_message) const override; }; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3201e2d4bc..3f8d97d31b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -566,6 +566,16 @@ public: } private: + void AppletStorageProxyOutData(IStorage storage) { + storage_stack.push_back(std::make_shared(storage)); + pop_out_data_event->Signal(); + } + + void AppletStorageProxyOutInteractiveData(IStorage storage) { + interactive_storage_stack.push_back(std::make_shared(storage)); + pop_interactive_out_data_event->Signal(); + } + void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { state_changed_event->Signal(); @@ -591,17 +601,11 @@ private: ASSERT(applet != nullptr); applet->Initialize(storage_stack); - const auto data = std::make_shared(applet->Execute()); + applet->Execute( + [this](IStorage storage) { AppletStorageProxyOutData(storage); }, + [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }); 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); } @@ -632,17 +636,11 @@ private: ASSERT(applet->IsInitialized()); applet->ReceiveInteractiveData(interactive_storage_stack.back()); - const auto data = std::make_shared(applet->Execute()); + applet->Execute( + [this](IStorage storage) { AppletStorageProxyOutData(storage); }, + [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }); 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); diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 6d90eb608f..7fbaaf2f36 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include "common/swap.h" @@ -20,6 +21,8 @@ class IStorage; namespace Applets { +using AppletStorageProxyFunction = std::function; + class Applet { public: Applet(); @@ -30,7 +33,8 @@ public: virtual bool TransactionComplete() const = 0; virtual ResultCode GetStatus() const = 0; virtual void ReceiveInteractiveData(std::shared_ptr storage) = 0; - virtual IStorage Execute() = 0; + virtual void Execute(AppletStorageProxyFunction out_data, + AppletStorageProxyFunction out_interactive_data) = 0; bool IsInitialized() const { return initialized; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 044a162648..7352f3bdf6 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -87,42 +87,37 @@ void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) } } -IStorage SoftwareKeyboard::Execute() { +void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, + AppletStorageProxyFunction out_interactive_data) { if (complete) - return IStorage{final_data}; + return; 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 res = frontend.GetText(parameters); std::vector output(SWKBD_OUTPUT_BUFFER_SIZE); - if (success) { + if (res.has_value()) { if (config.text_check) { - const auto size = static_cast(text.size() * 2 + 4); + const auto size = static_cast(res->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::memcpy(output.data() + 4, res->data(), + std::min(res->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); } else { complete = true; - final_data = std::move(output); - return IStorage{final_data}; + out_data(IStorage{output}); + return; } complete = !config.text_check; - if (complete) { - final_data = std::move(output); - return IStorage{final_data}; - } - - return IStorage{output}; + (complete ? out_data : out_interactive_data)(IStorage{output}); } } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 9d77f5802b..66de4bc592 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -54,7 +54,8 @@ public: bool TransactionComplete() const override; ResultCode GetStatus() const override; void ReceiveInteractiveData(std::shared_ptr storage) override; - IStorage Execute() override; + void Execute(AppletStorageProxyFunction out_data, + AppletStorageProxyFunction out_interactive_data) override; private: KeyboardConfig config; diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index fad150ec18..92992ef872 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -97,11 +97,11 @@ void QtSoftwareKeyboardDialog::Reject() { accept(); } -std::u16string QtSoftwareKeyboardDialog::GetText() { +std::u16string QtSoftwareKeyboardDialog::GetText() const { return text; } -bool QtSoftwareKeyboardDialog::GetStatus() { +bool QtSoftwareKeyboardDialog::GetStatus() const { return ok; } @@ -109,13 +109,12 @@ QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& parent) : main_window(parent QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; -bool QtSoftwareKeyboard::GetText(Core::Frontend::SoftwareKeyboardParameters parameters, - std::u16string& text) const { - bool success; +std::optional QtSoftwareKeyboard::GetText( + Core::Frontend::SoftwareKeyboardParameters parameters) const { + std::optional success; QMetaObject::invokeMethod(&main_window, "SoftwareKeyboardGetText", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, success), - Q_ARG(Core::Frontend::SoftwareKeyboardParameters, parameters), - Q_ARG(std::u16string&, text)); + Q_RETURN_ARG(std::optional, success), + Q_ARG(Core::Frontend::SoftwareKeyboardParameters, parameters)); return success; } diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 1069c10ec8..8d95ca511f 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -36,8 +36,8 @@ public: void Submit(); void Reject(); - std::u16string GetText(); - bool GetStatus(); + std::u16string GetText() const; + bool GetStatus() const; private: bool ok = false; @@ -58,8 +58,8 @@ public: explicit QtSoftwareKeyboard(GMainWindow& parent); ~QtSoftwareKeyboard() override; - bool GetText(Core::Frontend::SoftwareKeyboardParameters parameters, - std::u16string& text) const override; + std::optional GetText( + Core::Frontend::SoftwareKeyboardParameters parameters) const override; void SendTextCheckDialog(std::u16string error_message) const override; private: diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 447d9dece8..a11eb7f865 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -207,16 +207,18 @@ GMainWindow::~GMainWindow() { delete render_window; } -bool GMainWindow::SoftwareKeyboardGetText( - const Core::Frontend::SoftwareKeyboardParameters& parameters, std::u16string& text) { +std::optional GMainWindow::SoftwareKeyboardGetText( + const Core::Frontend::SoftwareKeyboardParameters& parameters) { 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(); + if (!dialog.GetStatus()) + return std::nullopt; + + return dialog.GetText(); } void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) { @@ -1251,10 +1253,10 @@ void GMainWindow::OnStartGame() { emu_thread->SetRunning(true); qRegisterMetaType( - "core::Frontend::SoftwareKeyboardParameters"); + "Core::Frontend::SoftwareKeyboardParameters"); qRegisterMetaType("Core::System::ResultStatus"); qRegisterMetaType("std::string"); - qRegisterMetaType("std::u16string"); + qRegisterMetaType>("std::optional"); connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 38074e3f0e..8fbe998eaf 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -100,8 +100,8 @@ signals: void UpdateThemedIcons(); public slots: - bool SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters, - std::u16string& text); + std::optional SoftwareKeyboardGetText( + const Core::Frontend::SoftwareKeyboardParameters& parameters); void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); private: From 8b433beff34c382e50334bb59c4f71394845558c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 12 Nov 2018 11:08:09 -0500 Subject: [PATCH 19/29] software_keyboard: Make GetText asynchronous a --- .../frontend/applets/software_keyboard.cpp | 7 ++--- src/core/frontend/applets/software_keyboard.h | 7 +++-- src/core/hle/service/am/am.cpp | 2 +- .../service/am/applets/software_keyboard.cpp | 19 +++++++++---- .../service/am/applets/software_keyboard.h | 5 ++++ src/yuzu/applets/software_keyboard.cpp | 27 ++++++++++++------- src/yuzu/applets/software_keyboard.h | 15 ++++++++--- src/yuzu/main.cpp | 6 ++--- src/yuzu/main.h | 5 ++-- 9 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 2445a980d1..4105101b36 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -9,12 +9,13 @@ namespace Core::Frontend { SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default; -std::optional DefaultSoftwareKeyboardApplet::GetText( +void DefaultSoftwareKeyboardApplet::RequestText( + std::function)> out, SoftwareKeyboardParameters parameters) const { if (parameters.initial_text.empty()) - return u"yuzu"; + out(u"yuzu"); - return parameters.initial_text; + out(parameters.initial_text); } void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(std::u16string error_message) const { diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 7387ee8d70..5420ea8833 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include "common/bit_field.h" @@ -36,13 +37,15 @@ class SoftwareKeyboardApplet { public: virtual ~SoftwareKeyboardApplet(); - virtual std::optional GetText(SoftwareKeyboardParameters parameters) const = 0; + virtual void RequestText(std::function)> out, + SoftwareKeyboardParameters parameters) const = 0; virtual void SendTextCheckDialog(std::u16string error_message) const = 0; }; class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { public: - std::optional GetText(SoftwareKeyboardParameters parameters) const override; + void RequestText(std::function)> out, + SoftwareKeyboardParameters parameters) const override; void SendTextCheckDialog(std::u16string error_message) const override; }; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3f8d97d31b..d040d4776c 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -718,7 +718,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop()}; const std::vector data{ctx.ReadBuffer()}; - const auto size = std::min(data.size(), backing.buffer.size() - offset); + const auto size = std::min(data.size(), backing.buffer.size() - offset); std::memcpy(&backing.buffer[offset], data.data(), size); diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 7352f3bdf6..66b34d5ac8 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -43,6 +43,10 @@ SoftwareKeyboard::SoftwareKeyboard() = default; SoftwareKeyboard::~SoftwareKeyboard() = default; void SoftwareKeyboard::Initialize(std::vector> storage_) { + complete = false; + initial_text.clear(); + final_data.clear(); + Applet::Initialize(std::move(storage_)); ASSERT(storage_stack.size() >= 2); @@ -96,20 +100,25 @@ void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, const auto parameters = ConvertToFrontendParameters(config, initial_text); - const auto res = frontend.GetText(parameters); + this->out_data = out_data; + this->out_interactive_data = out_interactive_data; + frontend.RequestText([this](std::optional text) { WriteText(text); }, + parameters); +} +void SoftwareKeyboard::WriteText(std::optional text) { std::vector output(SWKBD_OUTPUT_BUFFER_SIZE); - if (res.has_value()) { + if (text.has_value()) { if (config.text_check) { - const auto size = static_cast(res->size() * 2 + 4); + 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, res->data(), - std::min(res->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + std::memcpy(output.data() + 4, text->data(), + std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); } else { complete = true; out_data(IStorage{output}); diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 66de4bc592..b08bff3d74 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -57,11 +57,16 @@ public: void Execute(AppletStorageProxyFunction out_data, AppletStorageProxyFunction out_interactive_data) override; + void WriteText(std::optional text); + private: KeyboardConfig config; std::u16string initial_text; bool complete = false; std::vector final_data; + + AppletStorageProxyFunction out_data; + AppletStorageProxyFunction out_interactive_data; }; } // namespace Service::AM::Applets diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 92992ef872..9fb179f5c0 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -105,20 +105,27 @@ bool QtSoftwareKeyboardDialog::GetStatus() const { return ok; } -QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& parent) : main_window(parent) {} +QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { + connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window, + &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection); + connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window, + &GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection); + connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this, + &QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection); +} QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; -std::optional QtSoftwareKeyboard::GetText( - Core::Frontend::SoftwareKeyboardParameters parameters) const { - std::optional success; - QMetaObject::invokeMethod(&main_window, "SoftwareKeyboardGetText", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(std::optional, success), - Q_ARG(Core::Frontend::SoftwareKeyboardParameters, parameters)); - return success; +void QtSoftwareKeyboard::RequestText(std::function)> out, + Core::Frontend::SoftwareKeyboardParameters parameters) const { + text_output = out; + emit MainWindowGetText(parameters); } void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message) const { - QMetaObject::invokeMethod(&main_window, "SoftwareKeyboardInvokeCheckDialog", - Qt::BlockingQueuedConnection, Q_ARG(std::u16string, error_message)); + emit MainWindowTextCheckDialog(error_message); +} + +void QtSoftwareKeyboard::MainWindowFinishedText(std::optional text) { + text_output(text); } diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 8d95ca511f..670b05dc98 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -54,14 +54,23 @@ private: }; class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet { + Q_OBJECT + public: explicit QtSoftwareKeyboard(GMainWindow& parent); ~QtSoftwareKeyboard() override; - std::optional GetText( - Core::Frontend::SoftwareKeyboardParameters parameters) const override; + void RequestText(std::function)> out, + Core::Frontend::SoftwareKeyboardParameters parameters) const override; void SendTextCheckDialog(std::u16string error_message) const override; +signals: + void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; + void MainWindowTextCheckDialog(std::u16string error_message) const; + +public slots: + void MainWindowFinishedText(std::optional text); + private: - GMainWindow& main_window; + mutable std::function)> text_output; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a11eb7f865..28c53cc876 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -207,7 +207,7 @@ GMainWindow::~GMainWindow() { delete render_window; } -std::optional GMainWindow::SoftwareKeyboardGetText( +void GMainWindow::SoftwareKeyboardGetText( const Core::Frontend::SoftwareKeyboardParameters& parameters) { QtSoftwareKeyboardDialog dialog(this, parameters); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | @@ -216,9 +216,9 @@ std::optional GMainWindow::SoftwareKeyboardGetText( dialog.exec(); if (!dialog.GetStatus()) - return std::nullopt; + emit SoftwareKeyboardFinishedText(std::nullopt); - return dialog.GetText(); + emit SoftwareKeyboardFinishedText(dialog.GetText()); } void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8fbe998eaf..d83169805e 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -99,9 +99,10 @@ signals: // Signal that tells widgets to update icons to use the current theme void UpdateThemedIcons(); + void SoftwareKeyboardFinishedText(std::optional text); + public slots: - std::optional SoftwareKeyboardGetText( - const Core::Frontend::SoftwareKeyboardParameters& parameters); + void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); private: From 6209fe0c27a5557c20ff6350a94f6e074e0285dc Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 16 Nov 2018 22:20:09 -0500 Subject: [PATCH 20/29] software_keyboard: Push buffer size to offset 0x4 in output data --- src/core/hle/service/am/am.cpp | 30 ++++++++++++------- src/core/hle/service/am/applets/applets.h | 4 ++- .../service/am/applets/software_keyboard.cpp | 18 +++++++---- .../service/am/applets/software_keyboard.h | 5 +++- 4 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d040d4776c..470253ef1d 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -583,31 +583,38 @@ private: rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(state_changed_event); - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); } void IsCompleted(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(applet->TransactionComplete()); + + LOG_DEBUG(Service_AM, "called"); } void GetResult(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(applet->GetStatus()); + + LOG_DEBUG(Service_AM, "called"); } void Start(Kernel::HLERequestContext& ctx) { ASSERT(applet != nullptr); applet->Initialize(storage_stack); - applet->Execute( - [this](IStorage storage) { AppletStorageProxyOutData(storage); }, - [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }); - state_changed_event->Signal(); + storage_stack.clear(); + interactive_storage_stack.clear(); + applet->Execute([this](IStorage storage) { AppletStorageProxyOutData(storage); }, + [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }, + [this] { state_changed_event->Signal(); }); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); + + LOG_DEBUG(Service_AM, "called"); } void PushInData(Kernel::HLERequestContext& ctx) { @@ -636,10 +643,9 @@ private: ASSERT(applet->IsInitialized()); applet->ReceiveInteractiveData(interactive_storage_stack.back()); - applet->Execute( - [this](IStorage storage) { AppletStorageProxyOutData(storage); }, - [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }); - state_changed_event->Signal(); + applet->Execute([this](IStorage storage) { AppletStorageProxyOutData(storage); }, + [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }, + [this] { state_changed_event->Signal(); }); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -661,6 +667,8 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(pop_out_data_event); + + LOG_DEBUG(Service_AM, "called"); } void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { @@ -668,7 +676,7 @@ private: rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(pop_interactive_out_data_event); - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); } std::shared_ptr applet; @@ -734,7 +742,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop()}; std::size_t size{ctx.GetWriteBufferSize()}; - size = std::min(size, backing.buffer.size() - offset); + size = std::min(size, backing.buffer.size() - offset); ctx.WriteBuffer(backing.buffer.data() + offset, size); diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 7fbaaf2f36..1ffa094208 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -22,6 +22,7 @@ class IStorage; namespace Applets { using AppletStorageProxyFunction = std::function; +using AppletStateProxyFunction = std::function; class Applet { public: @@ -34,7 +35,8 @@ public: virtual ResultCode GetStatus() const = 0; virtual void ReceiveInteractiveData(std::shared_ptr storage) = 0; virtual void Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data) = 0; + AppletStorageProxyFunction out_interactive_data, + AppletStateProxyFunction state) = 0; bool IsInitialized() const { return initialized; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 66b34d5ac8..bb28a2e8db 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -69,7 +69,7 @@ bool SoftwareKeyboard::TransactionComplete() const { } ResultCode SoftwareKeyboard::GetStatus() const { - return RESULT_SUCCESS; + return status; } void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) { @@ -92,7 +92,8 @@ void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) } void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data) { + AppletStorageProxyFunction out_interactive_data, + AppletStateProxyFunction state) { if (complete) return; @@ -102,6 +103,7 @@ void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, this->out_data = out_data; this->out_interactive_data = out_interactive_data; + this->state = state; frontend.RequestText([this](std::optional text) { WriteText(text); }, parameters); } @@ -110,6 +112,7 @@ void SoftwareKeyboard::WriteText(std::optional text) { std::vector output(SWKBD_OUTPUT_BUFFER_SIZE); if (text.has_value()) { + status = RESULT_SUCCESS; if (config.text_check) { const auto size = static_cast(text->size() * 2 + 4); std::memcpy(output.data(), &size, sizeof(u32)); @@ -117,9 +120,12 @@ void SoftwareKeyboard::WriteText(std::optional text) { output[0] = 1; } - std::memcpy(output.data() + 4, text->data(), - std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + const auto size = static_cast(text->size()); + std::memcpy(output.data() + 4, &size, sizeof(u32)); + std::memcpy(output.data() + 8, text->data(), + std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); } else { + status = ResultCode(-1); complete = true; out_data(IStorage{output}); return; @@ -127,6 +133,8 @@ void SoftwareKeyboard::WriteText(std::optional text) { complete = !config.text_check; - (complete ? out_data : out_interactive_data)(IStorage{output}); + out_data(IStorage{output}); + out_interactive_data(IStorage{output}); + state(); } } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index b08bff3d74..9629f64085 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -55,7 +55,8 @@ public: ResultCode GetStatus() const override; void ReceiveInteractiveData(std::shared_ptr storage) override; void Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data) override; + AppletStorageProxyFunction out_interactive_data, + AppletStateProxyFunction state) override; void WriteText(std::optional text); @@ -64,9 +65,11 @@ private: std::u16string initial_text; bool complete = false; std::vector final_data; + ResultCode status = ResultCode(-1); AppletStorageProxyFunction out_data; AppletStorageProxyFunction out_interactive_data; + AppletStateProxyFunction state; }; } // namespace Service::AM::Applets From 19b2571aecfff680c7a414c505eafc26264b6f2f Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 17 Nov 2018 12:18:03 -0500 Subject: [PATCH 21/29] applet: Add operation completed callback --- src/core/frontend/applets/software_keyboard.cpp | 4 +++- src/core/frontend/applets/software_keyboard.h | 6 ++++-- src/core/hle/service/am/am.cpp | 6 ++++-- .../hle/service/am/applets/software_keyboard.cpp | 2 +- src/yuzu/applets/software_keyboard.cpp | 14 +++++++++++++- src/yuzu/applets/software_keyboard.h | 5 ++++- src/yuzu/main.cpp | 5 ++++- src/yuzu/main.h | 1 + 8 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 4105101b36..856ed33da5 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -18,10 +18,12 @@ void DefaultSoftwareKeyboardApplet::RequestText( out(parameters.initial_text); } -void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(std::u16string error_message) const { +void DefaultSoftwareKeyboardApplet::SendTextCheckDialog( + std::u16string error_message, std::function finished_check) const { LOG_WARNING(Service_AM, "(STUBBED) called - Default fallback software keyboard does not support text " "check! (error_message={})", Common::UTF16ToUTF8(error_message)); + finished_check(); } } // namespace Core::Frontend diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 5420ea8833..f9b2026642 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -39,14 +39,16 @@ public: virtual void RequestText(std::function)> out, SoftwareKeyboardParameters parameters) const = 0; - virtual void SendTextCheckDialog(std::u16string error_message) const = 0; + virtual void SendTextCheckDialog(std::u16string error_message, + std::function finished_check) const = 0; }; class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { public: void RequestText(std::function)> out, SoftwareKeyboardParameters parameters) const override; - void SendTextCheckDialog(std::u16string error_message) const override; + void SendTextCheckDialog(std::u16string error_message, + std::function finished_check) const override; }; } // namespace Core::Frontend diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 470253ef1d..5cbcb8d91b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -605,8 +605,10 @@ private: ASSERT(applet != nullptr); applet->Initialize(storage_stack); - storage_stack.clear(); - interactive_storage_stack.clear(); + while (!storage_stack.empty()) + storage_stack.pop(); + while (!interactive_storage_stack.empty()) + interactive_storage_stack.pop(); applet->Execute([this](IStorage storage) { AppletStorageProxyOutData(storage); }, [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }, [this] { state_changed_event->Signal(); }); diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index bb28a2e8db..039bfcc0f0 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -87,7 +87,7 @@ void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) std::array string; std::memcpy(string.data(), data.data() + 4, string.size() * 2); frontend.SendTextCheckDialog( - Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size())); + Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), state); } } diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 9fb179f5c0..83b9c320be 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -3,11 +3,13 @@ // Refer to the license.txt file included. #include +#include #include #include #include #include #include +#include "core/hle/lock.h" #include "yuzu/applets/software_keyboard.h" #include "yuzu/main.h" @@ -122,10 +124,20 @@ void QtSoftwareKeyboard::RequestText(std::function finished_check) const { + this->finished_check = finished_check; emit MainWindowTextCheckDialog(error_message); } void QtSoftwareKeyboard::MainWindowFinishedText(std::optional text) { + // Acquire the HLE mutex + std::lock_guard lock(HLE::g_hle_lock); text_output(text); } + +void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { + // Acquire the HLE mutex + std::lock_guard lock(HLE::g_hle_lock); + finished_check(); +} diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 670b05dc98..8d56f5db2e 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -62,7 +62,8 @@ public: void RequestText(std::function)> out, Core::Frontend::SoftwareKeyboardParameters parameters) const override; - void SendTextCheckDialog(std::u16string error_message) const override; + void SendTextCheckDialog(std::u16string error_message, + std::function finished_check) const override; signals: void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; @@ -70,7 +71,9 @@ signals: public slots: void MainWindowFinishedText(std::optional text); + void MainWindowFinishedCheckDialog(); private: mutable std::function)> text_output; + mutable std::function finished_check; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 28c53cc876..4262bd0eb9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -215,14 +215,17 @@ void GMainWindow::SoftwareKeyboardGetText( dialog.setWindowModality(Qt::WindowModal); dialog.exec(); - if (!dialog.GetStatus()) + if (!dialog.GetStatus()) { emit SoftwareKeyboardFinishedText(std::nullopt); + return; + } emit SoftwareKeyboardFinishedText(dialog.GetText()); } void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) { QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message)); + emit SoftwareKeyboardFinishedCheckDialog(); } void GMainWindow::InitializeWidgets() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index d83169805e..674e734125 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -100,6 +100,7 @@ signals: void UpdateThemedIcons(); void SoftwareKeyboardFinishedText(std::optional text); + void SoftwareKeyboardFinishedCheckDialog(); public slots: void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); From 4ee087fb3ca2b1e064422b73195a8ff7698721d9 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 17 Nov 2018 12:19:06 -0500 Subject: [PATCH 22/29] applet: Use std::queue instead of std::vector for storage stack --- src/core/hle/service/am/am.cpp | 36 ++++++++++++------- src/core/hle/service/am/applets/applets.cpp | 12 ++++++- src/core/hle/service/am/applets/applets.h | 7 ++-- .../service/am/applets/software_keyboard.cpp | 9 +++-- .../service/am/applets/software_keyboard.h | 2 +- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 5cbcb8d91b..d92a46b00f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -567,12 +567,12 @@ public: private: void AppletStorageProxyOutData(IStorage storage) { - storage_stack.push_back(std::make_shared(storage)); + storage_stack.push(std::make_shared(storage)); pop_out_data_event->Signal(); } void AppletStorageProxyOutInteractiveData(IStorage storage) { - interactive_storage_stack.push_back(std::make_shared(storage)); + interactive_storage_stack.push(std::make_shared(storage)); pop_interactive_out_data_event->Signal(); } @@ -621,7 +621,7 @@ private: void PushInData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - storage_stack.push_back(rp.PopIpcInterface()); + storage_stack.push(rp.PopIpcInterface()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -631,17 +631,23 @@ private: void PopOutData(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(std::move(storage_stack.back())); - storage_stack.pop_back(); + if (storage_stack.empty()) { + rb.Push(ResultCode(-1)); + return; + } + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::move(storage_stack.front())); + + storage_stack.pop(); LOG_DEBUG(Service_AM, "called"); } void PushInteractiveInData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - interactive_storage_stack.push_back(rp.PopIpcInterface()); + interactive_storage_stack.push(rp.PopIpcInterface()); ASSERT(applet->IsInitialized()); applet->ReceiveInteractiveData(interactive_storage_stack.back()); @@ -657,10 +663,16 @@ private: void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(std::move(interactive_storage_stack.back())); - interactive_storage_stack.pop_back(); + if (interactive_storage_stack.empty()) { + rb.Push(ResultCode(-1)); + return; + } + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::move(interactive_storage_stack.front())); + + interactive_storage_stack.pop(); LOG_DEBUG(Service_AM, "called"); } @@ -682,8 +694,8 @@ private: } std::shared_ptr applet; - std::vector> storage_stack; - std::vector> interactive_storage_stack; + std::queue> storage_stack; + std::queue> 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.cpp b/src/core/hle/service/am/applets/applets.cpp index 03b9d83e75..be950d320c 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -2,7 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include "common/assert.h" #include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" namespace Service::AM::Applets { @@ -11,8 +14,15 @@ Applet::Applet() = default; Applet::~Applet() = default; -void Applet::Initialize(std::vector> storage) { +void Applet::Initialize(std::queue> storage) { storage_stack = std::move(storage); + + const auto common_data = storage_stack.front()->GetData(); + storage_stack.pop(); + + ASSERT(common_data.size() >= sizeof(CommonArguments)); + std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments)); + initialized = true; } diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 1ffa094208..a6a9bf77b2 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -6,7 +6,7 @@ #include #include -#include +#include #include "common/swap.h" union ResultCode; @@ -29,7 +29,7 @@ public: Applet(); virtual ~Applet(); - virtual void Initialize(std::vector> storage); + virtual void Initialize(std::queue> storage); virtual bool TransactionComplete() const = 0; virtual ResultCode GetStatus() const = 0; @@ -53,7 +53,8 @@ protected: }; static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); - std::vector> storage_stack; + CommonArguments common_args; + std::queue> storage_stack; bool initialized = false; }; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 039bfcc0f0..a5ffa1f31a 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -42,7 +42,7 @@ SoftwareKeyboard::SoftwareKeyboard() = default; SoftwareKeyboard::~SoftwareKeyboard() = default; -void SoftwareKeyboard::Initialize(std::vector> storage_) { +void SoftwareKeyboard::Initialize(std::queue> storage_) { complete = false; initial_text.clear(); final_data.clear(); @@ -50,11 +50,14 @@ void SoftwareKeyboard::Initialize(std::vector> storage Applet::Initialize(std::move(storage_)); ASSERT(storage_stack.size() >= 2); - const auto& keyboard_config = storage_stack[1]->GetData(); + const auto& keyboard_config = storage_stack.front()->GetData(); + storage_stack.pop(); + ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); - const auto& work_buffer = storage_stack[2]->GetData(); + const auto& work_buffer = storage_stack.front()->GetData(); + storage_stack.pop(); if (config.initial_string_size == 0) return; diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 9629f64085..9544d6b1b5 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -49,7 +49,7 @@ public: SoftwareKeyboard(); ~SoftwareKeyboard() override; - void Initialize(std::vector> storage) override; + void Initialize(std::queue> storage) override; bool TransactionComplete() const override; ResultCode GetStatus() const override; From 02e6602baaf36d7c148739eae922fa91ba4818fd Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 17 Nov 2018 12:20:16 -0500 Subject: [PATCH 23/29] software_keyboard: Push all data over all channels on dialog completion --- .../service/am/applets/software_keyboard.cpp | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index a5ffa1f31a..5661cc98db 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -97,8 +97,10 @@ void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, AppletStorageProxyFunction out_interactive_data, AppletStateProxyFunction state) { - if (complete) + if (complete) { + out_data(IStorage{final_data}); return; + } const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; @@ -112,32 +114,38 @@ void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, } void SoftwareKeyboard::WriteText(std::optional text) { - std::vector output(SWKBD_OUTPUT_BUFFER_SIZE); + std::vector output_main(SWKBD_OUTPUT_BUFFER_SIZE); if (text.has_value()) { + std::vector output_sub(SWKBD_OUTPUT_BUFFER_SIZE); status = RESULT_SUCCESS; - if (config.text_check) { - const auto size = static_cast(text->size() * 2 + 4); - std::memcpy(output.data(), &size, sizeof(u32)); + + const u64 size = text->size() * 2 + 8; + std::memcpy(output_sub.data(), &size, sizeof(u64)); + std::memcpy(output_sub.data() + 8, text->data(), + std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); + + output_main[0] = config.text_check; + std::memcpy(output_main.data() + 4, text->data(), + std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + + complete = !config.text_check; + final_data = output_main; + + if (complete) { + out_data(IStorage{output_main}); } else { - output[0] = 1; + out_data(IStorage{output_main}); + out_interactive_data(IStorage{output_sub}); } - const auto size = static_cast(text->size()); - std::memcpy(output.data() + 4, &size, sizeof(u32)); - std::memcpy(output.data() + 8, text->data(), - std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); + state(); } else { status = ResultCode(-1); + output_main[0] = 1; complete = true; - out_data(IStorage{output}); - return; + out_data(IStorage{output_main}); + state(); } - - complete = !config.text_check; - - out_data(IStorage{output}); - out_interactive_data(IStorage{output}); - state(); } } // namespace Service::AM::Applets From 56cf5b7b17cd87f4a23bd45ae661762ce1fda5d1 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 17 Nov 2018 14:44:16 -0500 Subject: [PATCH 24/29] software_keyboard: Add max and current length display to dialog --- src/yuzu/applets/software_keyboard.cpp | 11 ++++++++++- src/yuzu/applets/software_keyboard.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 83b9c320be..efefb1f99f 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -47,7 +47,7 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( layout = new QVBoxLayout; header_label = new QLabel(QString::fromStdU16String(parameters.header_text)); - header_label->setFont({header_label->font().family(), 12, QFont::Bold}); + header_label->setFont({header_label->font().family(), 11, QFont::Bold}); if (header_label->text().isEmpty()) header_label->setText(tr("Enter text:")); @@ -59,6 +59,10 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text)); guide_label->setHidden(parameters.guide_text.empty()); + length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length)); + length_label->setAlignment(Qt::AlignRight); + length_label->setFont({length_label->font().family(), 8}); + line_edit = new QLineEdit; line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters)); line_edit->setMaxLength(static_cast(parameters.max_length)); @@ -67,6 +71,10 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( parameters.cursor_at_beginning ? 0 : static_cast(parameters.initial_text.size())); line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal); + connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) { + length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); + }); + buttons = new QDialogButtonBox; buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); buttons->addButton(parameters.submit_text.empty() @@ -79,6 +87,7 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( layout->addWidget(header_label); layout->addWidget(sub_label); layout->addWidget(guide_label); + layout->addWidget(length_label); layout->addWidget(line_edit); layout->addWidget(buttons); setLayout(layout); diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index 8d56f5db2e..73f56714f9 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -47,6 +47,7 @@ private: QLabel* header_label; QLabel* sub_label; QLabel* guide_label; + QLabel* length_label; QLineEdit* line_edit; QVBoxLayout* layout; From ea680bea60ac772a80c797365edaa7c86ab8459a Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 18 Nov 2018 23:14:48 -0500 Subject: [PATCH 25/29] software_keyboard: Check for UTF-8 config flag --- .../service/am/applets/software_keyboard.cpp | 27 ++++++++++++++----- .../service/am/applets/software_keyboard.h | 3 ++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 5661cc98db..0ef052be69 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -120,14 +120,27 @@ void SoftwareKeyboard::WriteText(std::optional text) { std::vector output_sub(SWKBD_OUTPUT_BUFFER_SIZE); status = RESULT_SUCCESS; - const u64 size = text->size() * 2 + 8; - std::memcpy(output_sub.data(), &size, sizeof(u64)); - std::memcpy(output_sub.data() + 8, text->data(), - std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); + if (config.utf_8) { + const u64 size = text->size() + 8; + const auto new_text = Common::UTF16ToUTF8(*text); - output_main[0] = config.text_check; - std::memcpy(output_main.data() + 4, text->data(), - std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + std::memcpy(output_sub.data(), &size, sizeof(u64)); + std::memcpy(output_sub.data() + 8, new_text.data(), + std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8)); + + output_main[0] = config.text_check; + std::memcpy(output_main.data() + 4, new_text.data(), + std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); + } else { + const u64 size = text->size() * 2 + 8; + std::memcpy(output_sub.data(), &size, sizeof(u64)); + std::memcpy(output_sub.data() + 8, text->data(), + std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); + + output_main[0] = config.text_check; + std::memcpy(output_main.data() + 4, text->data(), + std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + } complete = !config.text_check; final_data = output_main; diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 9544d6b1b5..e0a9479c2f 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -33,7 +33,8 @@ struct KeyboardConfig { u32_le length_limit; INSERT_PADDING_BYTES(4); u32_le is_password; - INSERT_PADDING_BYTES(6); + INSERT_PADDING_BYTES(5); + bool utf_8; bool draw_background; u32_le initial_string_offset; u32_le initial_string_size; From 96535c13a51a8ccece9965185250ae5d2a89dabf Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 19 Nov 2018 11:22:04 -0500 Subject: [PATCH 26/29] software_keyboard: Use correct offset for inital text string --- src/core/hle/service/am/applets/software_keyboard.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 0ef052be69..816b5fb5fa 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -63,7 +63,8 @@ void SoftwareKeyboard::Initialize(std::queue> storage_ return; std::vector string(config.initial_string_size); - std::memcpy(string.data(), work_buffer.data() + 4, string.size() * 2); + std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset, + string.size() * 2); initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()); } From 32775125b7af14cf488fdcbc4a61c00507c2d4a5 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 19 Nov 2018 14:24:36 -0500 Subject: [PATCH 27/29] applet: Add AppletDataBroker to manage HLE to AM service interaction This cleans up most of the callbacks and such in the Applets::Applet interface, while also properly implementing all four data channels. --- src/core/hle/service/am/am.cpp | 92 +++++++----------- src/core/hle/service/am/applets/applets.cpp | 95 ++++++++++++++++++- src/core/hle/service/am/applets/applets.h | 56 ++++++++--- .../service/am/applets/software_keyboard.cpp | 43 ++++----- .../service/am/applets/software_keyboard.h | 12 +-- 5 files changed, 194 insertions(+), 104 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d92a46b00f..fd14af1e7e 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -32,6 +32,9 @@ namespace Service::AM { +constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; +constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; + enum class AppletId : u32 { SoftwareKeyboard = 0x11, }; @@ -529,7 +532,8 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { class ILibraryAppletAccessor final : public ServiceFramework { public: explicit ILibraryAppletAccessor(std::shared_ptr applet) - : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) { + : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)), + broker(std::make_shared()) { // clang-format off static const FunctionInfo functions[] = { {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, @@ -554,34 +558,16 @@ public: // clang-format on RegisterHandlers(functions); - - 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"); } private: - void AppletStorageProxyOutData(IStorage storage) { - storage_stack.push(std::make_shared(storage)); - pop_out_data_event->Signal(); - } - - void AppletStorageProxyOutInteractiveData(IStorage storage) { - interactive_storage_stack.push(std::make_shared(storage)); - pop_interactive_out_data_event->Signal(); - } - void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { - state_changed_event->Signal(); + const auto event = broker->GetStateChangedEvent(); + event->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(state_changed_event); + rb.PushCopyObjects(event); LOG_DEBUG(Service_AM, "called"); } @@ -604,14 +590,8 @@ private: void Start(Kernel::HLERequestContext& ctx) { ASSERT(applet != nullptr); - applet->Initialize(storage_stack); - while (!storage_stack.empty()) - storage_stack.pop(); - while (!interactive_storage_stack.empty()) - interactive_storage_stack.pop(); - applet->Execute([this](IStorage storage) { AppletStorageProxyOutData(storage); }, - [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }, - [this] { state_changed_event->Signal(); }); + applet->Initialize(broker); + applet->Execute(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -621,7 +601,7 @@ private: void PushInData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - storage_stack.push(rp.PopIpcInterface()); + broker->PushNormalDataFromGame(*rp.PopIpcInterface()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -632,28 +612,25 @@ private: void PopOutData(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - if (storage_stack.empty()) { - rb.Push(ResultCode(-1)); + const auto storage = broker->PopNormalDataToGame(); + if (storage == nullptr) { + rb.Push(ERR_NO_DATA_IN_CHANNEL); return; } rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(std::move(storage_stack.front())); - - storage_stack.pop(); + rb.PushIpcInterface(std::move(*storage)); LOG_DEBUG(Service_AM, "called"); } void PushInteractiveInData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - interactive_storage_stack.push(rp.PopIpcInterface()); + broker->PushInteractiveDataFromGame(*rp.PopIpcInterface()); ASSERT(applet->IsInitialized()); - applet->ReceiveInteractiveData(interactive_storage_stack.back()); - applet->Execute([this](IStorage storage) { AppletStorageProxyOutData(storage); }, - [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }, - [this] { state_changed_event->Signal(); }); + applet->ExecuteInteractive(); + applet->Execute(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -664,15 +641,14 @@ private: void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - if (interactive_storage_stack.empty()) { - rb.Push(ResultCode(-1)); + const auto storage = broker->PopInteractiveDataToGame(); + if (storage == nullptr) { + rb.Push(ERR_NO_DATA_IN_CHANNEL); return; } rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(std::move(interactive_storage_stack.front())); - - interactive_storage_stack.pop(); + rb.PushIpcInterface(std::move(*storage)); LOG_DEBUG(Service_AM, "called"); } @@ -680,7 +656,7 @@ private: void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(pop_out_data_event); + rb.PushCopyObjects(broker->GetNormalDataEvent()); LOG_DEBUG(Service_AM, "called"); } @@ -688,17 +664,13 @@ private: void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(pop_interactive_out_data_event); + rb.PushCopyObjects(broker->GetInteractiveDataEvent()); LOG_DEBUG(Service_AM, "called"); } std::shared_ptr applet; - std::queue> storage_stack; - std::queue> interactive_storage_stack; - Kernel::SharedPtr state_changed_event; - Kernel::SharedPtr pop_out_data_event; - Kernel::SharedPtr pop_interactive_out_data_event; + std::shared_ptr broker; }; void IStorage::Open(Kernel::HLERequestContext& ctx) { @@ -740,9 +712,12 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop()}; const std::vector data{ctx.ReadBuffer()}; - const auto size = std::min(data.size(), backing.buffer.size() - offset); + if (data.size() > backing.buffer.size() - offset) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_SIZE_OUT_OF_BOUNDS); + } - std::memcpy(&backing.buffer[offset], data.data(), size); + std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -754,9 +729,12 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 offset{rp.Pop()}; - std::size_t size{ctx.GetWriteBufferSize()}; + const std::size_t size{ctx.GetWriteBufferSize()}; - size = std::min(size, backing.buffer.size() - offset); + if (size > backing.buffer.size() - offset) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_SIZE_OUT_OF_BOUNDS); + } ctx.WriteBuffer(backing.buffer.data() + offset, size); diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index be950d320c..c81bd59b28 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -4,21 +4,108 @@ #include #include "common/assert.h" +#include "core/core.h" #include "core/frontend/applets/software_keyboard.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/server_port.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" namespace Service::AM::Applets { +AppletDataBroker::AppletDataBroker() { + 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"); +} + +AppletDataBroker::~AppletDataBroker() = default; + +std::unique_ptr AppletDataBroker::PopNormalDataToGame() { + if (out_channel.empty()) + return nullptr; + + auto out = std::move(out_channel.front()); + out_channel.pop(); + return out; +} + +std::unique_ptr AppletDataBroker::PopNormalDataToApplet() { + if (in_channel.empty()) + return nullptr; + + auto out = std::move(in_channel.front()); + in_channel.pop(); + return out; +} + +std::unique_ptr AppletDataBroker::PopInteractiveDataToGame() { + if (out_interactive_channel.empty()) + return nullptr; + + auto out = std::move(out_interactive_channel.front()); + out_interactive_channel.pop(); + return out; +} + +std::unique_ptr AppletDataBroker::PopInteractiveDataToApplet() { + if (in_interactive_channel.empty()) + return nullptr; + + auto out = std::move(in_interactive_channel.front()); + in_interactive_channel.pop(); + return out; +} + +void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { + in_channel.push(std::make_unique(storage)); +} + +void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { + out_channel.push(std::make_unique(storage)); + pop_out_data_event->Signal(); +} + +void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { + in_interactive_channel.push(std::make_unique(storage)); +} + +void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { + out_interactive_channel.push(std::make_unique(storage)); + pop_interactive_out_data_event->Signal(); +} + +void AppletDataBroker::SignalStateChanged() const { + state_changed_event->Signal(); +} + +Kernel::SharedPtr AppletDataBroker::GetNormalDataEvent() const { + return pop_out_data_event; +} + +Kernel::SharedPtr AppletDataBroker::GetInteractiveDataEvent() const { + return pop_interactive_out_data_event; +} + +Kernel::SharedPtr AppletDataBroker::GetStateChangedEvent() const { + return state_changed_event; +} + Applet::Applet() = default; Applet::~Applet() = default; -void Applet::Initialize(std::queue> storage) { - storage_stack = std::move(storage); +void Applet::Initialize(std::shared_ptr broker_) { + broker = std::move(broker_); - const auto common_data = storage_stack.front()->GetData(); - storage_stack.pop(); + const auto common = broker->PopNormalDataToApplet(); + ASSERT(common != nullptr); + + const auto common_data = common->GetData(); ASSERT(common_data.size() >= sizeof(CommonArguments)); std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments)); diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index a6a9bf77b2..1364456496 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -8,35 +8,67 @@ #include #include #include "common/swap.h" +#include "core/hle/kernel/event.h" union ResultCode; -namespace Frontend { -class SoftwareKeyboardApplet; -} - namespace Service::AM { class IStorage; namespace Applets { -using AppletStorageProxyFunction = std::function; -using AppletStateProxyFunction = std::function; +class AppletDataBroker final { +public: + AppletDataBroker(); + ~AppletDataBroker(); + + std::unique_ptr PopNormalDataToGame(); + std::unique_ptr PopNormalDataToApplet(); + + std::unique_ptr PopInteractiveDataToGame(); + std::unique_ptr PopInteractiveDataToApplet(); + + void PushNormalDataFromGame(IStorage storage); + void PushNormalDataFromApplet(IStorage storage); + + void PushInteractiveDataFromGame(IStorage storage); + void PushInteractiveDataFromApplet(IStorage storage); + + void SignalStateChanged() const; + + Kernel::SharedPtr GetNormalDataEvent() const; + Kernel::SharedPtr GetInteractiveDataEvent() const; + Kernel::SharedPtr GetStateChangedEvent() const; + +private: + // Queues are named from applet's perspective + std::queue> + in_channel; // PopNormalDataToApplet and PushNormalDataFromGame + std::queue> + out_channel; // PopNormalDataToGame and PushNormalDataFromApplet + std::queue> + in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame + std::queue> + out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet + + Kernel::SharedPtr state_changed_event; + Kernel::SharedPtr pop_out_data_event; // Signaled on PushNormalDataFromApplet + Kernel::SharedPtr + pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet +}; class Applet { public: Applet(); virtual ~Applet(); - virtual void Initialize(std::queue> storage); + virtual void Initialize(std::shared_ptr broker); virtual bool TransactionComplete() const = 0; virtual ResultCode GetStatus() const = 0; - virtual void ReceiveInteractiveData(std::shared_ptr storage) = 0; - virtual void Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data, - AppletStateProxyFunction state) = 0; + virtual void ExecuteInteractive() = 0; + virtual void Execute() = 0; bool IsInitialized() const { return initialized; @@ -54,7 +86,7 @@ protected: static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); CommonArguments common_args; - std::queue> storage_stack; + std::shared_ptr broker; bool initialized = false; }; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 816b5fb5fa..ca9ef7e7d4 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -42,22 +42,23 @@ SoftwareKeyboard::SoftwareKeyboard() = default; SoftwareKeyboard::~SoftwareKeyboard() = default; -void SoftwareKeyboard::Initialize(std::queue> storage_) { +void SoftwareKeyboard::Initialize(std::shared_ptr broker_) { complete = false; initial_text.clear(); final_data.clear(); - Applet::Initialize(std::move(storage_)); + Applet::Initialize(std::move(broker_)); - ASSERT(storage_stack.size() >= 2); - const auto& keyboard_config = storage_stack.front()->GetData(); - storage_stack.pop(); + const auto keyboard_config_storage = broker->PopNormalDataToApplet(); + ASSERT(keyboard_config_storage != nullptr); + const auto& keyboard_config = keyboard_config_storage->GetData(); ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); - const auto& work_buffer = storage_stack.front()->GetData(); - storage_stack.pop(); + const auto work_buffer_storage = broker->PopNormalDataToApplet(); + ASSERT(work_buffer_storage != nullptr); + const auto& work_buffer = work_buffer_storage->GetData(); if (config.initial_string_size == 0) return; @@ -76,10 +77,12 @@ ResultCode SoftwareKeyboard::GetStatus() const { return status; } -void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) { +void SoftwareKeyboard::ExecuteInteractive() { if (complete) return; + const auto storage = broker->PopInteractiveDataToApplet(); + ASSERT(storage != nullptr); const auto data = storage->GetData(); const auto status = static_cast(data[0]); @@ -91,15 +94,14 @@ void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) std::array string; std::memcpy(string.data(), data.data() + 4, string.size() * 2); frontend.SendTextCheckDialog( - Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), state); + Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), + [this] { broker->SignalStateChanged(); }); } } -void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data, - AppletStateProxyFunction state) { +void SoftwareKeyboard::Execute() { if (complete) { - out_data(IStorage{final_data}); + broker->PushNormalDataFromApplet(IStorage{final_data}); return; } @@ -107,9 +109,6 @@ void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, const auto parameters = ConvertToFrontendParameters(config, initial_text); - this->out_data = out_data; - this->out_interactive_data = out_interactive_data; - this->state = state; frontend.RequestText([this](std::optional text) { WriteText(text); }, parameters); } @@ -147,19 +146,19 @@ void SoftwareKeyboard::WriteText(std::optional text) { final_data = output_main; if (complete) { - out_data(IStorage{output_main}); + broker->PushNormalDataFromApplet(IStorage{output_main}); } else { - out_data(IStorage{output_main}); - out_interactive_data(IStorage{output_sub}); + broker->PushNormalDataFromApplet(IStorage{output_main}); + broker->PushInteractiveDataFromApplet(IStorage{output_sub}); } - state(); + broker->SignalStateChanged(); } else { status = ResultCode(-1); output_main[0] = 1; complete = true; - out_data(IStorage{output_main}); - state(); + broker->PushNormalDataFromApplet(IStorage{output_main}); + broker->SignalStateChanged(); } } } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index e0a9479c2f..405c58851f 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -50,14 +50,12 @@ public: SoftwareKeyboard(); ~SoftwareKeyboard() override; - void Initialize(std::queue> storage) override; + void Initialize(std::shared_ptr broker) override; bool TransactionComplete() const override; ResultCode GetStatus() const override; - void ReceiveInteractiveData(std::shared_ptr storage) override; - void Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data, - AppletStateProxyFunction state) override; + void ExecuteInteractive() override; + void Execute() override; void WriteText(std::optional text); @@ -67,10 +65,6 @@ private: bool complete = false; std::vector final_data; ResultCode status = ResultCode(-1); - - AppletStorageProxyFunction out_data; - AppletStorageProxyFunction out_interactive_data; - AppletStateProxyFunction state; }; } // namespace Service::AM::Applets From d68795c665fe73da55f0427d1ff05281d46c3de9 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 19 Nov 2018 15:10:01 -0500 Subject: [PATCH 28/29] software_keyboard: Return correct result code on user cancel operation --- src/core/hle/service/am/applets/applets.cpp | 1 - src/core/hle/service/am/applets/software_keyboard.cpp | 4 +--- src/core/hle/service/am/applets/software_keyboard.h | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index c81bd59b28..8adb818230 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -5,7 +5,6 @@ #include #include "common/assert.h" #include "core/core.h" -#include "core/frontend/applets/software_keyboard.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/server_port.h" #include "core/hle/service/am/am.h" diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index ca9ef7e7d4..aec6bf2a71 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -74,7 +74,7 @@ bool SoftwareKeyboard::TransactionComplete() const { } ResultCode SoftwareKeyboard::GetStatus() const { - return status; + return RESULT_SUCCESS; } void SoftwareKeyboard::ExecuteInteractive() { @@ -118,7 +118,6 @@ void SoftwareKeyboard::WriteText(std::optional text) { if (text.has_value()) { std::vector output_sub(SWKBD_OUTPUT_BUFFER_SIZE); - status = RESULT_SUCCESS; if (config.utf_8) { const u64 size = text->size() + 8; @@ -154,7 +153,6 @@ void SoftwareKeyboard::WriteText(std::optional text) { broker->SignalStateChanged(); } else { - status = ResultCode(-1); output_main[0] = 1; complete = true; broker->PushNormalDataFromApplet(IStorage{output_main}); diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 405c58851f..16e1fff665 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -64,7 +64,6 @@ private: std::u16string initial_text; bool complete = false; std::vector final_data; - ResultCode status = ResultCode(-1); }; } // namespace Service::AM::Applets From a9fa890f14afc84307884aa802b6255c906054d9 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 19 Nov 2018 16:30:17 -0500 Subject: [PATCH 29/29] software_keyboard: Fix erroneous extra PushNormalData --- src/core/hle/service/am/applets/software_keyboard.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index aec6bf2a71..c4b76a5154 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -127,7 +127,7 @@ void SoftwareKeyboard::WriteText(std::optional text) { std::memcpy(output_sub.data() + 8, new_text.data(), std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8)); - output_main[0] = config.text_check; + output_main[0] = INTERACTIVE_STATUS_OK; std::memcpy(output_main.data() + 4, new_text.data(), std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); } else { @@ -136,7 +136,7 @@ void SoftwareKeyboard::WriteText(std::optional text) { std::memcpy(output_sub.data() + 8, text->data(), std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); - output_main[0] = config.text_check; + output_main[0] = INTERACTIVE_STATUS_OK; std::memcpy(output_main.data() + 4, text->data(), std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); } @@ -147,7 +147,6 @@ void SoftwareKeyboard::WriteText(std::optional text) { if (complete) { broker->PushNormalDataFromApplet(IStorage{output_main}); } else { - broker->PushNormalDataFromApplet(IStorage{output_main}); broker->PushInteractiveDataFromApplet(IStorage{output_sub}); }