diff options
| -rw-r--r-- | src/common/string_util.cpp | 9 | ||||
| -rw-r--r-- | src/common/string_util.h | 8 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/core/core.cpp | 17 | ||||
| -rw-r--r-- | src/core/core.h | 5 | ||||
| -rw-r--r-- | src/core/frontend/applets/software_keyboard.cpp | 29 | ||||
| -rw-r--r-- | src/core/frontend/applets/software_keyboard.h | 54 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 36 | ||||
| -rw-r--r-- | src/core/hle/service/am/am.cpp | 331 | ||||
| -rw-r--r-- | src/core/hle/service/am/am.h | 29 | ||||
| -rw-r--r-- | src/core/hle/service/am/applets/applets.cpp | 115 | ||||
| -rw-r--r-- | src/core/hle/service/am/applets/applets.h | 94 | ||||
| -rw-r--r-- | src/core/hle/service/am/applets/software_keyboard.cpp | 161 | ||||
| -rw-r--r-- | src/core/hle/service/am/applets/software_keyboard.h | 69 | ||||
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/applets/software_keyboard.cpp | 152 | ||||
| -rw-r--r-- | src/yuzu/applets/software_keyboard.h | 80 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 31 | ||||
| -rw-r--r-- | src/yuzu/main.h | 11 |
19 files changed, 1133 insertions, 106 deletions
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 14f7037d8..959f278aa 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 | |||
| 214 | return std::string(buffer, len); | 214 | return std::string(buffer, len); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, | ||
| 218 | std::size_t max_len) { | ||
| 219 | std::size_t len = 0; | ||
| 220 | while (len < max_len && buffer[len] != '\0') | ||
| 221 | ++len; | ||
| 222 | |||
| 223 | return std::u16string(buffer.begin(), buffer.begin() + len); | ||
| 224 | } | ||
| 225 | |||
| 217 | const char* TrimSourcePath(const char* path, const char* root) { | 226 | const char* TrimSourcePath(const char* path, const char* root) { |
| 218 | const char* p = path; | 227 | const char* p = path; |
| 219 | 228 | ||
diff --git a/src/common/string_util.h b/src/common/string_util.h index 08f96533b..583fd05e6 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -67,6 +67,14 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) { | |||
| 67 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); | 67 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); |
| 68 | 68 | ||
| 69 | /** | 69 | /** |
| 70 | * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't | ||
| 71 | * null-terminated, then the string ends at the greatest multiple of two less then or equal to | ||
| 72 | * max_len_bytes. | ||
| 73 | */ | ||
| 74 | std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, | ||
| 75 | std::size_t max_len); | ||
| 76 | |||
| 77 | /** | ||
| 70 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's | 78 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's |
| 71 | * intended to be used to strip a system-specific build directory from the `__FILE__` macro, | 79 | * intended to be used to strip a system-specific build directory from the `__FILE__` macro, |
| 72 | * leaving only the path relative to the sources root. | 80 | * leaving only the path relative to the sources root. |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 64fdf38cd..a355eaca6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -77,6 +77,8 @@ add_library(core STATIC | |||
| 77 | file_sys/vfs_vector.h | 77 | file_sys/vfs_vector.h |
| 78 | file_sys/xts_archive.cpp | 78 | file_sys/xts_archive.cpp |
| 79 | file_sys/xts_archive.h | 79 | file_sys/xts_archive.h |
| 80 | frontend/applets/software_keyboard.cpp | ||
| 81 | frontend/applets/software_keyboard.h | ||
| 80 | frontend/emu_window.cpp | 82 | frontend/emu_window.cpp |
| 81 | frontend/emu_window.h | 83 | frontend/emu_window.h |
| 82 | frontend/framebuffer_layout.cpp | 84 | frontend/framebuffer_layout.cpp |
| @@ -150,6 +152,10 @@ add_library(core STATIC | |||
| 150 | hle/service/am/applet_ae.h | 152 | hle/service/am/applet_ae.h |
| 151 | hle/service/am/applet_oe.cpp | 153 | hle/service/am/applet_oe.cpp |
| 152 | hle/service/am/applet_oe.h | 154 | hle/service/am/applet_oe.h |
| 155 | hle/service/am/applets/applets.cpp | ||
| 156 | hle/service/am/applets/applets.h | ||
| 157 | hle/service/am/applets/software_keyboard.cpp | ||
| 158 | hle/service/am/applets/software_keyboard.h | ||
| 153 | hle/service/am/idle.cpp | 159 | hle/service/am/idle.cpp |
| 154 | hle/service/am/idle.h | 160 | hle/service/am/idle.h |
| 155 | hle/service/am/omm.cpp | 161 | hle/service/am/omm.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 6d5b5a2d0..6c72fdf4a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -23,12 +23,14 @@ | |||
| 23 | #include "core/hle/kernel/process.h" | 23 | #include "core/hle/kernel/process.h" |
| 24 | #include "core/hle/kernel/scheduler.h" | 24 | #include "core/hle/kernel/scheduler.h" |
| 25 | #include "core/hle/kernel/thread.h" | 25 | #include "core/hle/kernel/thread.h" |
| 26 | #include "core/hle/service/am/applets/software_keyboard.h" | ||
| 26 | #include "core/hle/service/service.h" | 27 | #include "core/hle/service/service.h" |
| 27 | #include "core/hle/service/sm/sm.h" | 28 | #include "core/hle/service/sm/sm.h" |
| 28 | #include "core/loader/loader.h" | 29 | #include "core/loader/loader.h" |
| 29 | #include "core/perf_stats.h" | 30 | #include "core/perf_stats.h" |
| 30 | #include "core/settings.h" | 31 | #include "core/settings.h" |
| 31 | #include "core/telemetry_session.h" | 32 | #include "core/telemetry_session.h" |
| 33 | #include "frontend/applets/software_keyboard.h" | ||
| 32 | #include "video_core/debug_utils/debug_utils.h" | 34 | #include "video_core/debug_utils/debug_utils.h" |
| 33 | #include "video_core/gpu.h" | 35 | #include "video_core/gpu.h" |
| 34 | #include "video_core/renderer_base.h" | 36 | #include "video_core/renderer_base.h" |
| @@ -136,6 +138,10 @@ struct System::Impl { | |||
| 136 | if (virtual_filesystem == nullptr) | 138 | if (virtual_filesystem == nullptr) |
| 137 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); | 139 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); |
| 138 | 140 | ||
| 141 | /// Create default implementations of applets if one is not provided. | ||
| 142 | if (software_keyboard == nullptr) | ||
| 143 | software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); | ||
| 144 | |||
| 139 | auto main_process = Kernel::Process::Create(kernel, "main"); | 145 | auto main_process = Kernel::Process::Create(kernel, "main"); |
| 140 | kernel.MakeCurrentProcess(main_process.get()); | 146 | kernel.MakeCurrentProcess(main_process.get()); |
| 141 | 147 | ||
| @@ -289,6 +295,9 @@ struct System::Impl { | |||
| 289 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; | 295 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; |
| 290 | std::size_t active_core{}; ///< Active core, only used in single thread mode | 296 | std::size_t active_core{}; ///< Active core, only used in single thread mode |
| 291 | 297 | ||
| 298 | /// Frontend applets | ||
| 299 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; | ||
| 300 | |||
| 292 | /// Service manager | 301 | /// Service manager |
| 293 | std::shared_ptr<Service::SM::ServiceManager> service_manager; | 302 | std::shared_ptr<Service::SM::ServiceManager> service_manager; |
| 294 | 303 | ||
| @@ -488,6 +497,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { | |||
| 488 | return impl->virtual_filesystem; | 497 | return impl->virtual_filesystem; |
| 489 | } | 498 | } |
| 490 | 499 | ||
| 500 | void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) { | ||
| 501 | impl->software_keyboard = std::move(applet); | ||
| 502 | } | ||
| 503 | |||
| 504 | const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const { | ||
| 505 | return *impl->software_keyboard; | ||
| 506 | } | ||
| 507 | |||
| 491 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | 508 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { |
| 492 | return impl->Init(emu_window); | 509 | return impl->Init(emu_window); |
| 493 | } | 510 | } |
diff --git a/src/core/core.h b/src/core/core.h index cfacceb81..be71bd437 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | namespace Core::Frontend { | 14 | namespace Core::Frontend { |
| 15 | class EmuWindow; | 15 | class EmuWindow; |
| 16 | class SoftwareKeyboardApplet; | ||
| 16 | } // namespace Core::Frontend | 17 | } // namespace Core::Frontend |
| 17 | 18 | ||
| 18 | namespace FileSys { | 19 | namespace FileSys { |
| @@ -236,6 +237,10 @@ public: | |||
| 236 | 237 | ||
| 237 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; | 238 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; |
| 238 | 239 | ||
| 240 | void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet); | ||
| 241 | |||
| 242 | const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; | ||
| 243 | |||
| 239 | private: | 244 | private: |
| 240 | System(); | 245 | System(); |
| 241 | 246 | ||
diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp new file mode 100644 index 000000000..856ed33da --- /dev/null +++ b/src/core/frontend/applets/software_keyboard.cpp | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/backend.h" | ||
| 6 | #include "common/string_util.h" | ||
| 7 | #include "core/frontend/applets/software_keyboard.h" | ||
| 8 | |||
| 9 | namespace Core::Frontend { | ||
| 10 | SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default; | ||
| 11 | |||
| 12 | void DefaultSoftwareKeyboardApplet::RequestText( | ||
| 13 | std::function<void(std::optional<std::u16string>)> out, | ||
| 14 | SoftwareKeyboardParameters parameters) const { | ||
| 15 | if (parameters.initial_text.empty()) | ||
| 16 | out(u"yuzu"); | ||
| 17 | |||
| 18 | out(parameters.initial_text); | ||
| 19 | } | ||
| 20 | |||
| 21 | void DefaultSoftwareKeyboardApplet::SendTextCheckDialog( | ||
| 22 | std::u16string error_message, std::function<void()> finished_check) const { | ||
| 23 | LOG_WARNING(Service_AM, | ||
| 24 | "(STUBBED) called - Default fallback software keyboard does not support text " | ||
| 25 | "check! (error_message={})", | ||
| 26 | Common::UTF16ToUTF8(error_message)); | ||
| 27 | finished_check(); | ||
| 28 | } | ||
| 29 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h new file mode 100644 index 000000000..f9b202664 --- /dev/null +++ b/src/core/frontend/applets/software_keyboard.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <optional> | ||
| 9 | #include <string> | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Core::Frontend { | ||
| 14 | struct SoftwareKeyboardParameters { | ||
| 15 | std::u16string submit_text; | ||
| 16 | std::u16string header_text; | ||
| 17 | std::u16string sub_text; | ||
| 18 | std::u16string guide_text; | ||
| 19 | std::u16string initial_text; | ||
| 20 | std::size_t max_length; | ||
| 21 | bool password; | ||
| 22 | bool cursor_at_beginning; | ||
| 23 | |||
| 24 | union { | ||
| 25 | u8 value; | ||
| 26 | |||
| 27 | BitField<1, 1, u8> disable_space; | ||
| 28 | BitField<2, 1, u8> disable_address; | ||
| 29 | BitField<3, 1, u8> disable_percent; | ||
| 30 | BitField<4, 1, u8> disable_slash; | ||
| 31 | BitField<6, 1, u8> disable_number; | ||
| 32 | BitField<7, 1, u8> disable_download_code; | ||
| 33 | }; | ||
| 34 | }; | ||
| 35 | |||
| 36 | class SoftwareKeyboardApplet { | ||
| 37 | public: | ||
| 38 | virtual ~SoftwareKeyboardApplet(); | ||
| 39 | |||
| 40 | virtual void RequestText(std::function<void(std::optional<std::u16string>)> out, | ||
| 41 | SoftwareKeyboardParameters parameters) const = 0; | ||
| 42 | virtual void SendTextCheckDialog(std::u16string error_message, | ||
| 43 | std::function<void()> finished_check) const = 0; | ||
| 44 | }; | ||
| 45 | |||
| 46 | class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { | ||
| 47 | public: | ||
| 48 | void RequestText(std::function<void(std::optional<std::u16string>)> out, | ||
| 49 | SoftwareKeyboardParameters parameters) const override; | ||
| 50 | void SendTextCheckDialog(std::u16string error_message, | ||
| 51 | std::function<void()> finished_check) const override; | ||
| 52 | }; | ||
| 53 | |||
| 54 | } // namespace Core::Frontend | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9904605cd..51c367de7 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1183,9 +1183,39 @@ static ResultCode ResetSignal(Handle handle) { | |||
| 1183 | 1183 | ||
| 1184 | /// Creates a TransferMemory object | 1184 | /// Creates a TransferMemory object |
| 1185 | static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { | 1185 | static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { |
| 1186 | LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, | 1186 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, |
| 1187 | permissions); | 1187 | permissions); |
| 1188 | *handle = 0; | 1188 | |
| 1189 | if (!Common::Is4KBAligned(addr)) { | ||
| 1190 | LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr); | ||
| 1191 | return ERR_INVALID_ADDRESS; | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | if (!Common::Is4KBAligned(size) || size == 0) { | ||
| 1195 | LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size); | ||
| 1196 | return ERR_INVALID_ADDRESS; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | if (!IsValidAddressRange(addr, size)) { | ||
| 1200 | LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})", | ||
| 1201 | addr, size); | ||
| 1202 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | const auto perms = static_cast<MemoryPermission>(permissions); | ||
| 1206 | if (perms != MemoryPermission::None && perms != MemoryPermission::Read && | ||
| 1207 | perms != MemoryPermission::ReadWrite) { | ||
| 1208 | LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})", | ||
| 1209 | permissions); | ||
| 1210 | return ERR_INVALID_MEMORY_PERMISSIONS; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 1214 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | ||
| 1215 | const auto shared_mem_handle = SharedMemory::Create( | ||
| 1216 | kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr); | ||
| 1217 | |||
| 1218 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); | ||
| 1189 | return RESULT_SUCCESS; | 1219 | return RESULT_SUCCESS; |
| 1190 | } | 1220 | } |
| 1191 | 1221 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3758ecae1..fd14af1e7 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -6,10 +6,14 @@ | |||
| 6 | #include <cinttypes> | 6 | #include <cinttypes> |
| 7 | #include <cstring> | 7 | #include <cstring> |
| 8 | #include <stack> | 8 | #include <stack> |
| 9 | #include "applets/applets.h" | ||
| 10 | #include "applets/software_keyboard.h" | ||
| 11 | #include "audio_core/audio_renderer.h" | ||
| 9 | #include "core/core.h" | 12 | #include "core/core.h" |
| 10 | #include "core/hle/ipc_helpers.h" | 13 | #include "core/hle/ipc_helpers.h" |
| 11 | #include "core/hle/kernel/event.h" | 14 | #include "core/hle/kernel/event.h" |
| 12 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| 16 | #include "core/hle/kernel/shared_memory.h" | ||
| 13 | #include "core/hle/service/acc/profile_manager.h" | 17 | #include "core/hle/service/acc/profile_manager.h" |
| 14 | #include "core/hle/service/am/am.h" | 18 | #include "core/hle/service/am/am.h" |
| 15 | #include "core/hle/service/am/applet_ae.h" | 19 | #include "core/hle/service/am/applet_ae.h" |
| @@ -28,6 +32,13 @@ | |||
| 28 | 32 | ||
| 29 | namespace Service::AM { | 33 | namespace Service::AM { |
| 30 | 34 | ||
| 35 | constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; | ||
| 36 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; | ||
| 37 | |||
| 38 | enum class AppletId : u32 { | ||
| 39 | SoftwareKeyboard = 0x11, | ||
| 40 | }; | ||
| 41 | |||
| 31 | constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; | 42 | constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; |
| 32 | 43 | ||
| 33 | struct LaunchParameters { | 44 | struct LaunchParameters { |
| @@ -481,6 +492,24 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& | |||
| 481 | LOG_DEBUG(Service_AM, "called"); | 492 | LOG_DEBUG(Service_AM, "called"); |
| 482 | } | 493 | } |
| 483 | 494 | ||
| 495 | IStorage::IStorage(std::vector<u8> buffer) | ||
| 496 | : ServiceFramework("IStorage"), buffer(std::move(buffer)) { | ||
| 497 | // clang-format off | ||
| 498 | static const FunctionInfo functions[] = { | ||
| 499 | {0, &IStorage::Open, "Open"}, | ||
| 500 | {1, nullptr, "OpenTransferStorage"}, | ||
| 501 | }; | ||
| 502 | // clang-format on | ||
| 503 | |||
| 504 | RegisterHandlers(functions); | ||
| 505 | } | ||
| 506 | |||
| 507 | IStorage::~IStorage() = default; | ||
| 508 | |||
| 509 | const std::vector<u8>& IStorage::GetData() const { | ||
| 510 | return buffer; | ||
| 511 | } | ||
| 512 | |||
| 484 | void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { | 513 | void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { |
| 485 | const bool use_docked_mode{Settings::values.use_docked_mode}; | 514 | const bool use_docked_mode{Settings::values.use_docked_mode}; |
| 486 | IPC::ResponseBuilder rb{ctx, 3}; | 515 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -500,15 +529,31 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { | |||
| 500 | LOG_DEBUG(Service_AM, "called"); | 529 | LOG_DEBUG(Service_AM, "called"); |
| 501 | } | 530 | } |
| 502 | 531 | ||
| 503 | class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { | 532 | class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { |
| 504 | public: | 533 | public: |
| 505 | explicit IStorageAccessor(std::vector<u8> buffer) | 534 | explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet) |
| 506 | : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { | 535 | : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)), |
| 536 | broker(std::make_shared<Applets::AppletDataBroker>()) { | ||
| 507 | // clang-format off | 537 | // clang-format off |
| 508 | static const FunctionInfo functions[] = { | 538 | static const FunctionInfo functions[] = { |
| 509 | {0, &IStorageAccessor::GetSize, "GetSize"}, | 539 | {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, |
| 510 | {10, &IStorageAccessor::Write, "Write"}, | 540 | {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"}, |
| 511 | {11, &IStorageAccessor::Read, "Read"}, | 541 | {10, &ILibraryAppletAccessor::Start, "Start"}, |
| 542 | {20, nullptr, "RequestExit"}, | ||
| 543 | {25, nullptr, "Terminate"}, | ||
| 544 | {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, | ||
| 545 | {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, | ||
| 546 | {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, | ||
| 547 | {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, | ||
| 548 | {102, nullptr, "PushExtraStorage"}, | ||
| 549 | {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"}, | ||
| 550 | {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"}, | ||
| 551 | {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"}, | ||
| 552 | {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"}, | ||
| 553 | {110, nullptr, "NeedsToExitProcess"}, | ||
| 554 | {120, nullptr, "GetLibraryAppletInfo"}, | ||
| 555 | {150, nullptr, "RequestForAppletToGetForeground"}, | ||
| 556 | {160, nullptr, "GetIndirectLayerConsumerHandle"}, | ||
| 512 | }; | 557 | }; |
| 513 | // clang-format on | 558 | // clang-format on |
| 514 | 559 | ||
| @@ -516,158 +561,188 @@ public: | |||
| 516 | } | 561 | } |
| 517 | 562 | ||
| 518 | private: | 563 | private: |
| 519 | std::vector<u8> buffer; | 564 | void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { |
| 565 | const auto event = broker->GetStateChangedEvent(); | ||
| 566 | event->Signal(); | ||
| 520 | 567 | ||
| 521 | void GetSize(Kernel::HLERequestContext& ctx) { | 568 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 522 | IPC::ResponseBuilder rb{ctx, 4}; | 569 | rb.Push(RESULT_SUCCESS); |
| 570 | rb.PushCopyObjects(event); | ||
| 571 | |||
| 572 | LOG_DEBUG(Service_AM, "called"); | ||
| 573 | } | ||
| 523 | 574 | ||
| 575 | void IsCompleted(Kernel::HLERequestContext& ctx) { | ||
| 576 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 524 | rb.Push(RESULT_SUCCESS); | 577 | rb.Push(RESULT_SUCCESS); |
| 525 | rb.Push(static_cast<u64>(buffer.size())); | 578 | rb.Push<u32>(applet->TransactionComplete()); |
| 526 | 579 | ||
| 527 | LOG_DEBUG(Service_AM, "called"); | 580 | LOG_DEBUG(Service_AM, "called"); |
| 528 | } | 581 | } |
| 529 | 582 | ||
| 530 | void Write(Kernel::HLERequestContext& ctx) { | 583 | void GetResult(Kernel::HLERequestContext& ctx) { |
| 531 | IPC::RequestParser rp{ctx}; | 584 | IPC::ResponseBuilder rb{ctx, 2}; |
| 585 | rb.Push(applet->GetStatus()); | ||
| 532 | 586 | ||
| 533 | const u64 offset{rp.Pop<u64>()}; | 587 | LOG_DEBUG(Service_AM, "called"); |
| 534 | const std::vector<u8> data{ctx.ReadBuffer()}; | 588 | } |
| 535 | 589 | ||
| 536 | ASSERT(offset + data.size() <= buffer.size()); | 590 | void Start(Kernel::HLERequestContext& ctx) { |
| 591 | ASSERT(applet != nullptr); | ||
| 537 | 592 | ||
| 538 | std::memcpy(&buffer[offset], data.data(), data.size()); | 593 | applet->Initialize(broker); |
| 594 | applet->Execute(); | ||
| 539 | 595 | ||
| 540 | IPC::ResponseBuilder rb{ctx, 2}; | 596 | IPC::ResponseBuilder rb{ctx, 2}; |
| 541 | rb.Push(RESULT_SUCCESS); | 597 | rb.Push(RESULT_SUCCESS); |
| 542 | 598 | ||
| 543 | LOG_DEBUG(Service_AM, "called, offset={}", offset); | 599 | LOG_DEBUG(Service_AM, "called"); |
| 544 | } | 600 | } |
| 545 | 601 | ||
| 546 | void Read(Kernel::HLERequestContext& ctx) { | 602 | void PushInData(Kernel::HLERequestContext& ctx) { |
| 547 | IPC::RequestParser rp{ctx}; | 603 | IPC::RequestParser rp{ctx}; |
| 604 | broker->PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>()); | ||
| 605 | |||
| 606 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 607 | rb.Push(RESULT_SUCCESS); | ||
| 608 | |||
| 609 | LOG_DEBUG(Service_AM, "called"); | ||
| 610 | } | ||
| 611 | |||
| 612 | void PopOutData(Kernel::HLERequestContext& ctx) { | ||
| 613 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 548 | 614 | ||
| 549 | const u64 offset{rp.Pop<u64>()}; | 615 | const auto storage = broker->PopNormalDataToGame(); |
| 550 | const std::size_t size{ctx.GetWriteBufferSize()}; | 616 | if (storage == nullptr) { |
| 617 | rb.Push(ERR_NO_DATA_IN_CHANNEL); | ||
| 618 | return; | ||
| 619 | } | ||
| 551 | 620 | ||
| 552 | ASSERT(offset + size <= buffer.size()); | 621 | rb.Push(RESULT_SUCCESS); |
| 622 | rb.PushIpcInterface<IStorage>(std::move(*storage)); | ||
| 623 | |||
| 624 | LOG_DEBUG(Service_AM, "called"); | ||
| 625 | } | ||
| 626 | |||
| 627 | void PushInteractiveInData(Kernel::HLERequestContext& ctx) { | ||
| 628 | IPC::RequestParser rp{ctx}; | ||
| 629 | broker->PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>()); | ||
| 553 | 630 | ||
| 554 | ctx.WriteBuffer(buffer.data() + offset, size); | 631 | ASSERT(applet->IsInitialized()); |
| 632 | applet->ExecuteInteractive(); | ||
| 633 | applet->Execute(); | ||
| 555 | 634 | ||
| 556 | IPC::ResponseBuilder rb{ctx, 2}; | 635 | IPC::ResponseBuilder rb{ctx, 2}; |
| 557 | rb.Push(RESULT_SUCCESS); | 636 | rb.Push(RESULT_SUCCESS); |
| 558 | 637 | ||
| 559 | LOG_DEBUG(Service_AM, "called, offset={}", offset); | 638 | LOG_DEBUG(Service_AM, "called"); |
| 560 | } | 639 | } |
| 561 | }; | ||
| 562 | 640 | ||
| 563 | class IStorage final : public ServiceFramework<IStorage> { | 641 | void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { |
| 564 | public: | 642 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 565 | explicit IStorage(std::vector<u8> buffer) | ||
| 566 | : ServiceFramework("IStorage"), buffer(std::move(buffer)) { | ||
| 567 | // clang-format off | ||
| 568 | static const FunctionInfo functions[] = { | ||
| 569 | {0, &IStorage::Open, "Open"}, | ||
| 570 | {1, nullptr, "OpenTransferStorage"}, | ||
| 571 | }; | ||
| 572 | // clang-format on | ||
| 573 | 643 | ||
| 574 | RegisterHandlers(functions); | 644 | const auto storage = broker->PopInteractiveDataToGame(); |
| 645 | if (storage == nullptr) { | ||
| 646 | rb.Push(ERR_NO_DATA_IN_CHANNEL); | ||
| 647 | return; | ||
| 648 | } | ||
| 649 | |||
| 650 | rb.Push(RESULT_SUCCESS); | ||
| 651 | rb.PushIpcInterface<IStorage>(std::move(*storage)); | ||
| 652 | |||
| 653 | LOG_DEBUG(Service_AM, "called"); | ||
| 575 | } | 654 | } |
| 576 | 655 | ||
| 577 | private: | 656 | void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { |
| 578 | std::vector<u8> buffer; | 657 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 658 | rb.Push(RESULT_SUCCESS); | ||
| 659 | rb.PushCopyObjects(broker->GetNormalDataEvent()); | ||
| 579 | 660 | ||
| 580 | void Open(Kernel::HLERequestContext& ctx) { | 661 | LOG_DEBUG(Service_AM, "called"); |
| 581 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 662 | } |
| 582 | 663 | ||
| 664 | void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { | ||
| 665 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 583 | rb.Push(RESULT_SUCCESS); | 666 | rb.Push(RESULT_SUCCESS); |
| 584 | rb.PushIpcInterface<AM::IStorageAccessor>(buffer); | 667 | rb.PushCopyObjects(broker->GetInteractiveDataEvent()); |
| 585 | 668 | ||
| 586 | LOG_DEBUG(Service_AM, "called"); | 669 | LOG_DEBUG(Service_AM, "called"); |
| 587 | } | 670 | } |
| 671 | |||
| 672 | std::shared_ptr<Applets::Applet> applet; | ||
| 673 | std::shared_ptr<Applets::AppletDataBroker> broker; | ||
| 588 | }; | 674 | }; |
| 589 | 675 | ||
| 590 | class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { | 676 | void IStorage::Open(Kernel::HLERequestContext& ctx) { |
| 591 | public: | 677 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 592 | explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { | 678 | |
| 593 | // clang-format off | 679 | rb.Push(RESULT_SUCCESS); |
| 680 | rb.PushIpcInterface<IStorageAccessor>(*this); | ||
| 681 | |||
| 682 | LOG_DEBUG(Service_AM, "called"); | ||
| 683 | } | ||
| 684 | |||
| 685 | IStorageAccessor::IStorageAccessor(IStorage& storage) | ||
| 686 | : ServiceFramework("IStorageAccessor"), backing(storage) { | ||
| 687 | // clang-format off | ||
| 594 | static const FunctionInfo functions[] = { | 688 | static const FunctionInfo functions[] = { |
| 595 | {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, | 689 | {0, &IStorageAccessor::GetSize, "GetSize"}, |
| 596 | {1, nullptr, "IsCompleted"}, | 690 | {10, &IStorageAccessor::Write, "Write"}, |
| 597 | {10, &ILibraryAppletAccessor::Start, "Start"}, | 691 | {11, &IStorageAccessor::Read, "Read"}, |
| 598 | {20, nullptr, "RequestExit"}, | ||
| 599 | {25, nullptr, "Terminate"}, | ||
| 600 | {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, | ||
| 601 | {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, | ||
| 602 | {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, | ||
| 603 | {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, | ||
| 604 | {102, nullptr, "PushExtraStorage"}, | ||
| 605 | {103, nullptr, "PushInteractiveInData"}, | ||
| 606 | {104, nullptr, "PopInteractiveOutData"}, | ||
| 607 | {105, nullptr, "GetPopOutDataEvent"}, | ||
| 608 | {106, nullptr, "GetPopInteractiveOutDataEvent"}, | ||
| 609 | {110, nullptr, "NeedsToExitProcess"}, | ||
| 610 | {120, nullptr, "GetLibraryAppletInfo"}, | ||
| 611 | {150, nullptr, "RequestForAppletToGetForeground"}, | ||
| 612 | {160, nullptr, "GetIndirectLayerConsumerHandle"}, | ||
| 613 | }; | 692 | }; |
| 614 | // clang-format on | 693 | // clang-format on |
| 615 | 694 | ||
| 616 | RegisterHandlers(functions); | 695 | RegisterHandlers(functions); |
| 696 | } | ||
| 617 | 697 | ||
| 618 | auto& kernel = Core::System::GetInstance().Kernel(); | 698 | IStorageAccessor::~IStorageAccessor() = default; |
| 619 | state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, | ||
| 620 | "ILibraryAppletAccessor:StateChangedEvent"); | ||
| 621 | } | ||
| 622 | 699 | ||
| 623 | private: | 700 | void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) { |
| 624 | void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { | 701 | IPC::ResponseBuilder rb{ctx, 4}; |
| 625 | state_changed_event->Signal(); | ||
| 626 | 702 | ||
| 627 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 703 | rb.Push(RESULT_SUCCESS); |
| 628 | rb.Push(RESULT_SUCCESS); | 704 | rb.Push(static_cast<u64>(backing.buffer.size())); |
| 629 | rb.PushCopyObjects(state_changed_event); | ||
| 630 | 705 | ||
| 631 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 706 | LOG_DEBUG(Service_AM, "called"); |
| 632 | } | 707 | } |
| 633 | 708 | ||
| 634 | void GetResult(Kernel::HLERequestContext& ctx) { | 709 | void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { |
| 635 | IPC::ResponseBuilder rb{ctx, 2}; | 710 | IPC::RequestParser rp{ctx}; |
| 636 | rb.Push(RESULT_SUCCESS); | ||
| 637 | 711 | ||
| 638 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 712 | const u64 offset{rp.Pop<u64>()}; |
| 639 | } | 713 | const std::vector<u8> data{ctx.ReadBuffer()}; |
| 640 | 714 | ||
| 641 | void Start(Kernel::HLERequestContext& ctx) { | 715 | if (data.size() > backing.buffer.size() - offset) { |
| 642 | IPC::ResponseBuilder rb{ctx, 2}; | 716 | IPC::ResponseBuilder rb{ctx, 2}; |
| 643 | rb.Push(RESULT_SUCCESS); | 717 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); |
| 644 | |||
| 645 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 646 | } | 718 | } |
| 647 | 719 | ||
| 648 | void PushInData(Kernel::HLERequestContext& ctx) { | 720 | std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); |
| 649 | IPC::RequestParser rp{ctx}; | ||
| 650 | storage_stack.push(rp.PopIpcInterface<AM::IStorage>()); | ||
| 651 | 721 | ||
| 652 | IPC::ResponseBuilder rb{ctx, 2}; | 722 | IPC::ResponseBuilder rb{ctx, 2}; |
| 653 | rb.Push(RESULT_SUCCESS); | 723 | rb.Push(RESULT_SUCCESS); |
| 654 | 724 | ||
| 655 | LOG_DEBUG(Service_AM, "called"); | 725 | LOG_DEBUG(Service_AM, "called, offset={}", offset); |
| 656 | } | 726 | } |
| 657 | 727 | ||
| 658 | void PopOutData(Kernel::HLERequestContext& ctx) { | 728 | void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { |
| 659 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 729 | IPC::RequestParser rp{ctx}; |
| 660 | rb.Push(RESULT_SUCCESS); | ||
| 661 | rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top())); | ||
| 662 | 730 | ||
| 663 | storage_stack.pop(); | 731 | const u64 offset{rp.Pop<u64>()}; |
| 732 | const std::size_t size{ctx.GetWriteBufferSize()}; | ||
| 664 | 733 | ||
| 665 | LOG_DEBUG(Service_AM, "called"); | 734 | if (size > backing.buffer.size() - offset) { |
| 735 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 736 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); | ||
| 666 | } | 737 | } |
| 667 | 738 | ||
| 668 | std::stack<std::shared_ptr<AM::IStorage>> storage_stack; | 739 | ctx.WriteBuffer(backing.buffer.data() + offset, size); |
| 669 | Kernel::SharedPtr<Kernel::Event> state_changed_event; | 740 | |
| 670 | }; | 741 | IPC::ResponseBuilder rb{ctx, 2}; |
| 742 | rb.Push(RESULT_SUCCESS); | ||
| 743 | |||
| 744 | LOG_DEBUG(Service_AM, "called, offset={}", offset); | ||
| 745 | } | ||
| 671 | 746 | ||
| 672 | ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { | 747 | ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { |
| 673 | static const FunctionInfo functions[] = { | 748 | static const FunctionInfo functions[] = { |
| @@ -675,7 +750,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple | |||
| 675 | {1, nullptr, "TerminateAllLibraryApplets"}, | 750 | {1, nullptr, "TerminateAllLibraryApplets"}, |
| 676 | {2, nullptr, "AreAnyLibraryAppletsLeft"}, | 751 | {2, nullptr, "AreAnyLibraryAppletsLeft"}, |
| 677 | {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, | 752 | {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, |
| 678 | {11, nullptr, "CreateTransferMemoryStorage"}, | 753 | {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, |
| 679 | {12, nullptr, "CreateHandleStorage"}, | 754 | {12, nullptr, "CreateHandleStorage"}, |
| 680 | }; | 755 | }; |
| 681 | RegisterHandlers(functions); | 756 | RegisterHandlers(functions); |
| @@ -683,11 +758,36 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple | |||
| 683 | 758 | ||
| 684 | ILibraryAppletCreator::~ILibraryAppletCreator() = default; | 759 | ILibraryAppletCreator::~ILibraryAppletCreator() = default; |
| 685 | 760 | ||
| 761 | static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { | ||
| 762 | switch (id) { | ||
| 763 | case AppletId::SoftwareKeyboard: | ||
| 764 | return std::make_shared<Applets::SoftwareKeyboard>(); | ||
| 765 | default: | ||
| 766 | UNREACHABLE_MSG("Unimplemented AppletId [{:08X}]!", static_cast<u32>(id)); | ||
| 767 | return nullptr; | ||
| 768 | } | ||
| 769 | } | ||
| 770 | |||
| 686 | void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { | 771 | void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { |
| 772 | IPC::RequestParser rp{ctx}; | ||
| 773 | const auto applet_id = rp.PopRaw<AppletId>(); | ||
| 774 | const auto applet_mode = rp.PopRaw<u32>(); | ||
| 775 | |||
| 776 | LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", | ||
| 777 | static_cast<u32>(applet_id), applet_mode); | ||
| 778 | |||
| 779 | const auto applet = GetAppletFromId(applet_id); | ||
| 780 | |||
| 781 | if (applet == nullptr) { | ||
| 782 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 783 | rb.Push(ResultCode(-1)); | ||
| 784 | return; | ||
| 785 | } | ||
| 786 | |||
| 687 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 787 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 688 | 788 | ||
| 689 | rb.Push(RESULT_SUCCESS); | 789 | rb.Push(RESULT_SUCCESS); |
| 690 | rb.PushIpcInterface<AM::ILibraryAppletAccessor>(); | 790 | rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet); |
| 691 | 791 | ||
| 692 | LOG_DEBUG(Service_AM, "called"); | 792 | LOG_DEBUG(Service_AM, "called"); |
| 693 | } | 793 | } |
| @@ -704,6 +804,31 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { | |||
| 704 | LOG_DEBUG(Service_AM, "called, size={}", size); | 804 | LOG_DEBUG(Service_AM, "called, size={}", size); |
| 705 | } | 805 | } |
| 706 | 806 | ||
| 807 | void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) { | ||
| 808 | IPC::RequestParser rp{ctx}; | ||
| 809 | |||
| 810 | rp.SetCurrentOffset(3); | ||
| 811 | const auto handle{rp.Pop<Kernel::Handle>()}; | ||
| 812 | |||
| 813 | const auto shared_mem = | ||
| 814 | Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>( | ||
| 815 | handle); | ||
| 816 | |||
| 817 | if (shared_mem == nullptr) { | ||
| 818 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 819 | rb.Push(ResultCode(-1)); | ||
| 820 | return; | ||
| 821 | } | ||
| 822 | |||
| 823 | const auto mem_begin = shared_mem->backing_block->begin() + shared_mem->backing_block_offset; | ||
| 824 | const auto mem_end = mem_begin + shared_mem->size; | ||
| 825 | std::vector<u8> memory{mem_begin, mem_end}; | ||
| 826 | |||
| 827 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 828 | rb.Push(RESULT_SUCCESS); | ||
| 829 | rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); | ||
| 830 | } | ||
| 831 | |||
| 707 | IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { | 832 | IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { |
| 708 | // clang-format off | 833 | // clang-format off |
| 709 | static const FunctionInfo functions[] = { | 834 | static const FunctionInfo functions[] = { |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 5a3fcba8f..44c1bcde5 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -155,6 +155,34 @@ private: | |||
| 155 | std::shared_ptr<AppletMessageQueue> msg_queue; | 155 | std::shared_ptr<AppletMessageQueue> msg_queue; |
| 156 | }; | 156 | }; |
| 157 | 157 | ||
| 158 | class IStorage final : public ServiceFramework<IStorage> { | ||
| 159 | public: | ||
| 160 | explicit IStorage(std::vector<u8> buffer); | ||
| 161 | ~IStorage() override; | ||
| 162 | |||
| 163 | const std::vector<u8>& GetData() const; | ||
| 164 | |||
| 165 | private: | ||
| 166 | void Open(Kernel::HLERequestContext& ctx); | ||
| 167 | |||
| 168 | std::vector<u8> buffer; | ||
| 169 | |||
| 170 | friend class IStorageAccessor; | ||
| 171 | }; | ||
| 172 | |||
| 173 | class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { | ||
| 174 | public: | ||
| 175 | explicit IStorageAccessor(IStorage& backing); | ||
| 176 | ~IStorageAccessor() override; | ||
| 177 | |||
| 178 | private: | ||
| 179 | void GetSize(Kernel::HLERequestContext& ctx); | ||
| 180 | void Write(Kernel::HLERequestContext& ctx); | ||
| 181 | void Read(Kernel::HLERequestContext& ctx); | ||
| 182 | |||
| 183 | IStorage& backing; | ||
| 184 | }; | ||
| 185 | |||
| 158 | class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { | 186 | class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { |
| 159 | public: | 187 | public: |
| 160 | ILibraryAppletCreator(); | 188 | ILibraryAppletCreator(); |
| @@ -163,6 +191,7 @@ public: | |||
| 163 | private: | 191 | private: |
| 164 | void CreateLibraryApplet(Kernel::HLERequestContext& ctx); | 192 | void CreateLibraryApplet(Kernel::HLERequestContext& ctx); |
| 165 | void CreateStorage(Kernel::HLERequestContext& ctx); | 193 | void CreateStorage(Kernel::HLERequestContext& ctx); |
| 194 | void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); | ||
| 166 | }; | 195 | }; |
| 167 | 196 | ||
| 168 | class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { | 197 | class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { |
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 000000000..8adb81823 --- /dev/null +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/hle/kernel/event.h" | ||
| 9 | #include "core/hle/kernel/server_port.h" | ||
| 10 | #include "core/hle/service/am/am.h" | ||
| 11 | #include "core/hle/service/am/applets/applets.h" | ||
| 12 | |||
| 13 | namespace Service::AM::Applets { | ||
| 14 | |||
| 15 | AppletDataBroker::AppletDataBroker() { | ||
| 16 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 17 | state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, | ||
| 18 | "ILibraryAppletAccessor:StateChangedEvent"); | ||
| 19 | pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, | ||
| 20 | "ILibraryAppletAccessor:PopDataOutEvent"); | ||
| 21 | pop_interactive_out_data_event = Kernel::Event::Create( | ||
| 22 | kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); | ||
| 23 | } | ||
| 24 | |||
| 25 | AppletDataBroker::~AppletDataBroker() = default; | ||
| 26 | |||
| 27 | std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { | ||
| 28 | if (out_channel.empty()) | ||
| 29 | return nullptr; | ||
| 30 | |||
| 31 | auto out = std::move(out_channel.front()); | ||
| 32 | out_channel.pop(); | ||
| 33 | return out; | ||
| 34 | } | ||
| 35 | |||
| 36 | std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { | ||
| 37 | if (in_channel.empty()) | ||
| 38 | return nullptr; | ||
| 39 | |||
| 40 | auto out = std::move(in_channel.front()); | ||
| 41 | in_channel.pop(); | ||
| 42 | return out; | ||
| 43 | } | ||
| 44 | |||
| 45 | std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { | ||
| 46 | if (out_interactive_channel.empty()) | ||
| 47 | return nullptr; | ||
| 48 | |||
| 49 | auto out = std::move(out_interactive_channel.front()); | ||
| 50 | out_interactive_channel.pop(); | ||
| 51 | return out; | ||
| 52 | } | ||
| 53 | |||
| 54 | std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { | ||
| 55 | if (in_interactive_channel.empty()) | ||
| 56 | return nullptr; | ||
| 57 | |||
| 58 | auto out = std::move(in_interactive_channel.front()); | ||
| 59 | in_interactive_channel.pop(); | ||
| 60 | return out; | ||
| 61 | } | ||
| 62 | |||
| 63 | void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { | ||
| 64 | in_channel.push(std::make_unique<IStorage>(storage)); | ||
| 65 | } | ||
| 66 | |||
| 67 | void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { | ||
| 68 | out_channel.push(std::make_unique<IStorage>(storage)); | ||
| 69 | pop_out_data_event->Signal(); | ||
| 70 | } | ||
| 71 | |||
| 72 | void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { | ||
| 73 | in_interactive_channel.push(std::make_unique<IStorage>(storage)); | ||
| 74 | } | ||
| 75 | |||
| 76 | void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { | ||
| 77 | out_interactive_channel.push(std::make_unique<IStorage>(storage)); | ||
| 78 | pop_interactive_out_data_event->Signal(); | ||
| 79 | } | ||
| 80 | |||
| 81 | void AppletDataBroker::SignalStateChanged() const { | ||
| 82 | state_changed_event->Signal(); | ||
| 83 | } | ||
| 84 | |||
| 85 | Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const { | ||
| 86 | return pop_out_data_event; | ||
| 87 | } | ||
| 88 | |||
| 89 | Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const { | ||
| 90 | return pop_interactive_out_data_event; | ||
| 91 | } | ||
| 92 | |||
| 93 | Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const { | ||
| 94 | return state_changed_event; | ||
| 95 | } | ||
| 96 | |||
| 97 | Applet::Applet() = default; | ||
| 98 | |||
| 99 | Applet::~Applet() = default; | ||
| 100 | |||
| 101 | void Applet::Initialize(std::shared_ptr<AppletDataBroker> broker_) { | ||
| 102 | broker = std::move(broker_); | ||
| 103 | |||
| 104 | const auto common = broker->PopNormalDataToApplet(); | ||
| 105 | ASSERT(common != nullptr); | ||
| 106 | |||
| 107 | const auto common_data = common->GetData(); | ||
| 108 | |||
| 109 | ASSERT(common_data.size() >= sizeof(CommonArguments)); | ||
| 110 | std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments)); | ||
| 111 | |||
| 112 | initialized = true; | ||
| 113 | } | ||
| 114 | |||
| 115 | } // 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 000000000..136445649 --- /dev/null +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <queue> | ||
| 10 | #include "common/swap.h" | ||
| 11 | #include "core/hle/kernel/event.h" | ||
| 12 | |||
| 13 | union ResultCode; | ||
| 14 | |||
| 15 | namespace Service::AM { | ||
| 16 | |||
| 17 | class IStorage; | ||
| 18 | |||
| 19 | namespace Applets { | ||
| 20 | |||
| 21 | class AppletDataBroker final { | ||
| 22 | public: | ||
| 23 | AppletDataBroker(); | ||
| 24 | ~AppletDataBroker(); | ||
| 25 | |||
| 26 | std::unique_ptr<IStorage> PopNormalDataToGame(); | ||
| 27 | std::unique_ptr<IStorage> PopNormalDataToApplet(); | ||
| 28 | |||
| 29 | std::unique_ptr<IStorage> PopInteractiveDataToGame(); | ||
| 30 | std::unique_ptr<IStorage> PopInteractiveDataToApplet(); | ||
| 31 | |||
| 32 | void PushNormalDataFromGame(IStorage storage); | ||
| 33 | void PushNormalDataFromApplet(IStorage storage); | ||
| 34 | |||
| 35 | void PushInteractiveDataFromGame(IStorage storage); | ||
| 36 | void PushInteractiveDataFromApplet(IStorage storage); | ||
| 37 | |||
| 38 | void SignalStateChanged() const; | ||
| 39 | |||
| 40 | Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const; | ||
| 41 | Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const; | ||
| 42 | Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const; | ||
| 43 | |||
| 44 | private: | ||
| 45 | // Queues are named from applet's perspective | ||
| 46 | std::queue<std::unique_ptr<IStorage>> | ||
| 47 | in_channel; // PopNormalDataToApplet and PushNormalDataFromGame | ||
| 48 | std::queue<std::unique_ptr<IStorage>> | ||
| 49 | out_channel; // PopNormalDataToGame and PushNormalDataFromApplet | ||
| 50 | std::queue<std::unique_ptr<IStorage>> | ||
| 51 | in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame | ||
| 52 | std::queue<std::unique_ptr<IStorage>> | ||
| 53 | out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet | ||
| 54 | |||
| 55 | Kernel::SharedPtr<Kernel::Event> state_changed_event; | ||
| 56 | Kernel::SharedPtr<Kernel::Event> pop_out_data_event; // Signaled on PushNormalDataFromApplet | ||
| 57 | Kernel::SharedPtr<Kernel::Event> | ||
| 58 | pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet | ||
| 59 | }; | ||
| 60 | |||
| 61 | class Applet { | ||
| 62 | public: | ||
| 63 | Applet(); | ||
| 64 | virtual ~Applet(); | ||
| 65 | |||
| 66 | virtual void Initialize(std::shared_ptr<AppletDataBroker> broker); | ||
| 67 | |||
| 68 | virtual bool TransactionComplete() const = 0; | ||
| 69 | virtual ResultCode GetStatus() const = 0; | ||
| 70 | virtual void ExecuteInteractive() = 0; | ||
| 71 | virtual void Execute() = 0; | ||
| 72 | |||
| 73 | bool IsInitialized() const { | ||
| 74 | return initialized; | ||
| 75 | } | ||
| 76 | |||
| 77 | protected: | ||
| 78 | struct CommonArguments { | ||
| 79 | u32_le arguments_version; | ||
| 80 | u32_le size; | ||
| 81 | u32_le library_version; | ||
| 82 | u32_le theme_color; | ||
| 83 | u8 play_startup_sound; | ||
| 84 | u64_le system_tick; | ||
| 85 | }; | ||
| 86 | static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); | ||
| 87 | |||
| 88 | CommonArguments common_args; | ||
| 89 | std::shared_ptr<AppletDataBroker> broker; | ||
| 90 | bool initialized = false; | ||
| 91 | }; | ||
| 92 | |||
| 93 | } // namespace Applets | ||
| 94 | } // 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 new file mode 100644 index 000000000..c4b76a515 --- /dev/null +++ b/src/core/hle/service/am/applets/software_keyboard.cpp | |||
| @@ -0,0 +1,161 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/string_util.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/frontend/applets/software_keyboard.h" | ||
| 10 | #include "core/hle/service/am/am.h" | ||
| 11 | #include "core/hle/service/am/applets/software_keyboard.h" | ||
| 12 | |||
| 13 | namespace Service::AM::Applets { | ||
| 14 | |||
| 15 | constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; | ||
| 16 | constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; | ||
| 17 | constexpr std::size_t DEFAULT_MAX_LENGTH = 500; | ||
| 18 | constexpr bool INTERACTIVE_STATUS_OK = false; | ||
| 19 | |||
| 20 | static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( | ||
| 21 | KeyboardConfig config, std::u16string initial_text) { | ||
| 22 | Core::Frontend::SoftwareKeyboardParameters params{}; | ||
| 23 | |||
| 24 | params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( | ||
| 25 | config.submit_text.data(), config.submit_text.size()); | ||
| 26 | params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( | ||
| 27 | config.header_text.data(), config.header_text.size()); | ||
| 28 | params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(), | ||
| 29 | config.sub_text.size()); | ||
| 30 | params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), | ||
| 31 | config.guide_text.size()); | ||
| 32 | params.initial_text = initial_text; | ||
| 33 | params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; | ||
| 34 | params.password = static_cast<bool>(config.is_password); | ||
| 35 | params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); | ||
| 36 | params.value = static_cast<u8>(config.keyset_disable_bitmask); | ||
| 37 | |||
| 38 | return params; | ||
| 39 | } | ||
| 40 | |||
| 41 | SoftwareKeyboard::SoftwareKeyboard() = default; | ||
| 42 | |||
| 43 | SoftwareKeyboard::~SoftwareKeyboard() = default; | ||
| 44 | |||
| 45 | void SoftwareKeyboard::Initialize(std::shared_ptr<AppletDataBroker> broker_) { | ||
| 46 | complete = false; | ||
| 47 | initial_text.clear(); | ||
| 48 | final_data.clear(); | ||
| 49 | |||
| 50 | Applet::Initialize(std::move(broker_)); | ||
| 51 | |||
| 52 | const auto keyboard_config_storage = broker->PopNormalDataToApplet(); | ||
| 53 | ASSERT(keyboard_config_storage != nullptr); | ||
| 54 | const auto& keyboard_config = keyboard_config_storage->GetData(); | ||
| 55 | |||
| 56 | ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); | ||
| 57 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); | ||
| 58 | |||
| 59 | const auto work_buffer_storage = broker->PopNormalDataToApplet(); | ||
| 60 | ASSERT(work_buffer_storage != nullptr); | ||
| 61 | const auto& work_buffer = work_buffer_storage->GetData(); | ||
| 62 | |||
| 63 | if (config.initial_string_size == 0) | ||
| 64 | return; | ||
| 65 | |||
| 66 | std::vector<char16_t> string(config.initial_string_size); | ||
| 67 | std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset, | ||
| 68 | string.size() * 2); | ||
| 69 | initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()); | ||
| 70 | } | ||
| 71 | |||
| 72 | bool SoftwareKeyboard::TransactionComplete() const { | ||
| 73 | return complete; | ||
| 74 | } | ||
| 75 | |||
| 76 | ResultCode SoftwareKeyboard::GetStatus() const { | ||
| 77 | return RESULT_SUCCESS; | ||
| 78 | } | ||
| 79 | |||
| 80 | void SoftwareKeyboard::ExecuteInteractive() { | ||
| 81 | if (complete) | ||
| 82 | return; | ||
| 83 | |||
| 84 | const auto storage = broker->PopInteractiveDataToApplet(); | ||
| 85 | ASSERT(storage != nullptr); | ||
| 86 | const auto data = storage->GetData(); | ||
| 87 | const auto status = static_cast<bool>(data[0]); | ||
| 88 | |||
| 89 | if (status == INTERACTIVE_STATUS_OK) { | ||
| 90 | complete = true; | ||
| 91 | } else { | ||
| 92 | const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; | ||
| 93 | |||
| 94 | std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; | ||
| 95 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); | ||
| 96 | frontend.SendTextCheckDialog( | ||
| 97 | Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), | ||
| 98 | [this] { broker->SignalStateChanged(); }); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | void SoftwareKeyboard::Execute() { | ||
| 103 | if (complete) { | ||
| 104 | broker->PushNormalDataFromApplet(IStorage{final_data}); | ||
| 105 | return; | ||
| 106 | } | ||
| 107 | |||
| 108 | const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; | ||
| 109 | |||
| 110 | const auto parameters = ConvertToFrontendParameters(config, initial_text); | ||
| 111 | |||
| 112 | frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, | ||
| 113 | parameters); | ||
| 114 | } | ||
| 115 | |||
| 116 | void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { | ||
| 117 | std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE); | ||
| 118 | |||
| 119 | if (text.has_value()) { | ||
| 120 | std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE); | ||
| 121 | |||
| 122 | if (config.utf_8) { | ||
| 123 | const u64 size = text->size() + 8; | ||
| 124 | const auto new_text = Common::UTF16ToUTF8(*text); | ||
| 125 | |||
| 126 | std::memcpy(output_sub.data(), &size, sizeof(u64)); | ||
| 127 | std::memcpy(output_sub.data() + 8, new_text.data(), | ||
| 128 | std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8)); | ||
| 129 | |||
| 130 | output_main[0] = INTERACTIVE_STATUS_OK; | ||
| 131 | std::memcpy(output_main.data() + 4, new_text.data(), | ||
| 132 | std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); | ||
| 133 | } else { | ||
| 134 | const u64 size = text->size() * 2 + 8; | ||
| 135 | std::memcpy(output_sub.data(), &size, sizeof(u64)); | ||
| 136 | std::memcpy(output_sub.data() + 8, text->data(), | ||
| 137 | std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); | ||
| 138 | |||
| 139 | output_main[0] = INTERACTIVE_STATUS_OK; | ||
| 140 | std::memcpy(output_main.data() + 4, text->data(), | ||
| 141 | std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); | ||
| 142 | } | ||
| 143 | |||
| 144 | complete = !config.text_check; | ||
| 145 | final_data = output_main; | ||
| 146 | |||
| 147 | if (complete) { | ||
| 148 | broker->PushNormalDataFromApplet(IStorage{output_main}); | ||
| 149 | } else { | ||
| 150 | broker->PushInteractiveDataFromApplet(IStorage{output_sub}); | ||
| 151 | } | ||
| 152 | |||
| 153 | broker->SignalStateChanged(); | ||
| 154 | } else { | ||
| 155 | output_main[0] = 1; | ||
| 156 | complete = true; | ||
| 157 | broker->PushNormalDataFromApplet(IStorage{output_main}); | ||
| 158 | broker->SignalStateChanged(); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } // 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 000000000..16e1fff66 --- /dev/null +++ b/src/core/hle/service/am/applets/software_keyboard.h | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "core/hle/service/am/am.h" | ||
| 9 | #include "core/hle/service/am/applets/applets.h" | ||
| 10 | |||
| 11 | namespace Service::AM::Applets { | ||
| 12 | |||
| 13 | enum class KeysetDisable : u32 { | ||
| 14 | Space = 0x02, | ||
| 15 | Address = 0x04, | ||
| 16 | Percent = 0x08, | ||
| 17 | Slashes = 0x10, | ||
| 18 | Numbers = 0x40, | ||
| 19 | DownloadCode = 0x80, | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct KeyboardConfig { | ||
| 23 | INSERT_PADDING_BYTES(4); | ||
| 24 | std::array<char16_t, 9> submit_text; | ||
| 25 | u16_le left_symbol_key; | ||
| 26 | u16_le right_symbol_key; | ||
| 27 | INSERT_PADDING_BYTES(1); | ||
| 28 | KeysetDisable keyset_disable_bitmask; | ||
| 29 | u32_le initial_cursor_position; | ||
| 30 | std::array<char16_t, 65> header_text; | ||
| 31 | std::array<char16_t, 129> sub_text; | ||
| 32 | std::array<char16_t, 257> guide_text; | ||
| 33 | u32_le length_limit; | ||
| 34 | INSERT_PADDING_BYTES(4); | ||
| 35 | u32_le is_password; | ||
| 36 | INSERT_PADDING_BYTES(5); | ||
| 37 | bool utf_8; | ||
| 38 | bool draw_background; | ||
| 39 | u32_le initial_string_offset; | ||
| 40 | u32_le initial_string_size; | ||
| 41 | u32_le user_dictionary_offset; | ||
| 42 | u32_le user_dictionary_size; | ||
| 43 | bool text_check; | ||
| 44 | u64_le text_check_callback; | ||
| 45 | }; | ||
| 46 | static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size."); | ||
| 47 | |||
| 48 | class SoftwareKeyboard final : public Applet { | ||
| 49 | public: | ||
| 50 | SoftwareKeyboard(); | ||
| 51 | ~SoftwareKeyboard() override; | ||
| 52 | |||
| 53 | void Initialize(std::shared_ptr<AppletDataBroker> broker) override; | ||
| 54 | |||
| 55 | bool TransactionComplete() const override; | ||
| 56 | ResultCode GetStatus() const override; | ||
| 57 | void ExecuteInteractive() override; | ||
| 58 | void Execute() override; | ||
| 59 | |||
| 60 | void WriteText(std::optional<std::u16string> text); | ||
| 61 | |||
| 62 | private: | ||
| 63 | KeyboardConfig config; | ||
| 64 | std::u16string initial_text; | ||
| 65 | bool complete = false; | ||
| 66 | std::vector<u8> final_data; | ||
| 67 | }; | ||
| 68 | |||
| 69 | } // namespace Service::AM::Applets | ||
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 34f36f06b..cfca8f4a8 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -7,6 +7,8 @@ add_executable(yuzu | |||
| 7 | Info.plist | 7 | Info.plist |
| 8 | about_dialog.cpp | 8 | about_dialog.cpp |
| 9 | about_dialog.h | 9 | about_dialog.h |
| 10 | applets/software_keyboard.cpp | ||
| 11 | applets/software_keyboard.h | ||
| 10 | bootmanager.cpp | 12 | bootmanager.cpp |
| 11 | bootmanager.h | 13 | bootmanager.h |
| 12 | compatibility_list.cpp | 14 | compatibility_list.cpp |
diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp new file mode 100644 index 000000000..efefb1f99 --- /dev/null +++ b/src/yuzu/applets/software_keyboard.cpp | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <mutex> | ||
| 7 | #include <QDialogButtonBox> | ||
| 8 | #include <QFont> | ||
| 9 | #include <QLabel> | ||
| 10 | #include <QLineEdit> | ||
| 11 | #include <QVBoxLayout> | ||
| 12 | #include "core/hle/lock.h" | ||
| 13 | #include "yuzu/applets/software_keyboard.h" | ||
| 14 | #include "yuzu/main.h" | ||
| 15 | |||
| 16 | QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( | ||
| 17 | Core::Frontend::SoftwareKeyboardParameters parameters) | ||
| 18 | : parameters(std::move(parameters)) {} | ||
| 19 | |||
| 20 | QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { | ||
| 21 | if (input.size() > parameters.max_length) | ||
| 22 | return Invalid; | ||
| 23 | if (parameters.disable_space && input.contains(' ')) | ||
| 24 | return Invalid; | ||
| 25 | if (parameters.disable_address && input.contains('@')) | ||
| 26 | return Invalid; | ||
| 27 | if (parameters.disable_percent && input.contains('%')) | ||
| 28 | return Invalid; | ||
| 29 | if (parameters.disable_slash && (input.contains('/') || input.contains('\\'))) | ||
| 30 | return Invalid; | ||
| 31 | if (parameters.disable_number && | ||
| 32 | std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { | ||
| 33 | return Invalid; | ||
| 34 | } | ||
| 35 | |||
| 36 | if (parameters.disable_download_code && | ||
| 37 | std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) { | ||
| 38 | return Invalid; | ||
| 39 | } | ||
| 40 | |||
| 41 | return Acceptable; | ||
| 42 | } | ||
| 43 | |||
| 44 | QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( | ||
| 45 | QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_) | ||
| 46 | : QDialog(parent), parameters(std::move(parameters_)) { | ||
| 47 | layout = new QVBoxLayout; | ||
| 48 | |||
| 49 | header_label = new QLabel(QString::fromStdU16String(parameters.header_text)); | ||
| 50 | header_label->setFont({header_label->font().family(), 11, QFont::Bold}); | ||
| 51 | if (header_label->text().isEmpty()) | ||
| 52 | header_label->setText(tr("Enter text:")); | ||
| 53 | |||
| 54 | sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text)); | ||
| 55 | sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(), | ||
| 56 | sub_label->font().weight(), true}); | ||
| 57 | sub_label->setHidden(parameters.sub_text.empty()); | ||
| 58 | |||
| 59 | guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text)); | ||
| 60 | guide_label->setHidden(parameters.guide_text.empty()); | ||
| 61 | |||
| 62 | length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length)); | ||
| 63 | length_label->setAlignment(Qt::AlignRight); | ||
| 64 | length_label->setFont({length_label->font().family(), 8}); | ||
| 65 | |||
| 66 | line_edit = new QLineEdit; | ||
| 67 | line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters)); | ||
| 68 | line_edit->setMaxLength(static_cast<int>(parameters.max_length)); | ||
| 69 | line_edit->setText(QString::fromStdU16String(parameters.initial_text)); | ||
| 70 | line_edit->setCursorPosition( | ||
| 71 | parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size())); | ||
| 72 | line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal); | ||
| 73 | |||
| 74 | connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) { | ||
| 75 | length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); | ||
| 76 | }); | ||
| 77 | |||
| 78 | buttons = new QDialogButtonBox; | ||
| 79 | buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); | ||
| 80 | buttons->addButton(parameters.submit_text.empty() | ||
| 81 | ? tr("OK") | ||
| 82 | : QString::fromStdU16String(parameters.submit_text), | ||
| 83 | QDialogButtonBox::AcceptRole); | ||
| 84 | |||
| 85 | connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::Submit); | ||
| 86 | connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::Reject); | ||
| 87 | layout->addWidget(header_label); | ||
| 88 | layout->addWidget(sub_label); | ||
| 89 | layout->addWidget(guide_label); | ||
| 90 | layout->addWidget(length_label); | ||
| 91 | layout->addWidget(line_edit); | ||
| 92 | layout->addWidget(buttons); | ||
| 93 | setLayout(layout); | ||
| 94 | setWindowTitle(tr("Software Keyboard")); | ||
| 95 | } | ||
| 96 | |||
| 97 | QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; | ||
| 98 | |||
| 99 | void QtSoftwareKeyboardDialog::Submit() { | ||
| 100 | ok = true; | ||
| 101 | text = line_edit->text().toStdU16String(); | ||
| 102 | accept(); | ||
| 103 | } | ||
| 104 | |||
| 105 | void QtSoftwareKeyboardDialog::Reject() { | ||
| 106 | ok = false; | ||
| 107 | text.clear(); | ||
| 108 | accept(); | ||
| 109 | } | ||
| 110 | |||
| 111 | std::u16string QtSoftwareKeyboardDialog::GetText() const { | ||
| 112 | return text; | ||
| 113 | } | ||
| 114 | |||
| 115 | bool QtSoftwareKeyboardDialog::GetStatus() const { | ||
| 116 | return ok; | ||
| 117 | } | ||
| 118 | |||
| 119 | QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { | ||
| 120 | connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window, | ||
| 121 | &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection); | ||
| 122 | connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window, | ||
| 123 | &GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection); | ||
| 124 | connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this, | ||
| 125 | &QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection); | ||
| 126 | } | ||
| 127 | |||
| 128 | QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; | ||
| 129 | |||
| 130 | void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out, | ||
| 131 | Core::Frontend::SoftwareKeyboardParameters parameters) const { | ||
| 132 | text_output = out; | ||
| 133 | emit MainWindowGetText(parameters); | ||
| 134 | } | ||
| 135 | |||
| 136 | void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, | ||
| 137 | std::function<void()> finished_check) const { | ||
| 138 | this->finished_check = finished_check; | ||
| 139 | emit MainWindowTextCheckDialog(error_message); | ||
| 140 | } | ||
| 141 | |||
| 142 | void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { | ||
| 143 | // Acquire the HLE mutex | ||
| 144 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 145 | text_output(text); | ||
| 146 | } | ||
| 147 | |||
| 148 | void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { | ||
| 149 | // Acquire the HLE mutex | ||
| 150 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 151 | finished_check(); | ||
| 152 | } | ||
diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h new file mode 100644 index 000000000..73f56714f --- /dev/null +++ b/src/yuzu/applets/software_keyboard.h | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QDialog> | ||
| 8 | #include <QValidator> | ||
| 9 | #include "common/assert.h" | ||
| 10 | #include "core/frontend/applets/software_keyboard.h" | ||
| 11 | |||
| 12 | class GMainWindow; | ||
| 13 | class QDialogButtonBox; | ||
| 14 | class QLabel; | ||
| 15 | class QLineEdit; | ||
| 16 | class QVBoxLayout; | ||
| 17 | class QtSoftwareKeyboard; | ||
| 18 | |||
| 19 | class QtSoftwareKeyboardValidator final : public QValidator { | ||
| 20 | public: | ||
| 21 | explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters); | ||
| 22 | State validate(QString& input, int& pos) const override; | ||
| 23 | |||
| 24 | private: | ||
| 25 | Core::Frontend::SoftwareKeyboardParameters parameters; | ||
| 26 | }; | ||
| 27 | |||
| 28 | class QtSoftwareKeyboardDialog final : public QDialog { | ||
| 29 | Q_OBJECT | ||
| 30 | |||
| 31 | public: | ||
| 32 | QtSoftwareKeyboardDialog(QWidget* parent, | ||
| 33 | Core::Frontend::SoftwareKeyboardParameters parameters); | ||
| 34 | ~QtSoftwareKeyboardDialog() override; | ||
| 35 | |||
| 36 | void Submit(); | ||
| 37 | void Reject(); | ||
| 38 | |||
| 39 | std::u16string GetText() const; | ||
| 40 | bool GetStatus() const; | ||
| 41 | |||
| 42 | private: | ||
| 43 | bool ok = false; | ||
| 44 | std::u16string text; | ||
| 45 | |||
| 46 | QDialogButtonBox* buttons; | ||
| 47 | QLabel* header_label; | ||
| 48 | QLabel* sub_label; | ||
| 49 | QLabel* guide_label; | ||
| 50 | QLabel* length_label; | ||
| 51 | QLineEdit* line_edit; | ||
| 52 | QVBoxLayout* layout; | ||
| 53 | |||
| 54 | Core::Frontend::SoftwareKeyboardParameters parameters; | ||
| 55 | }; | ||
| 56 | |||
| 57 | class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet { | ||
| 58 | Q_OBJECT | ||
| 59 | |||
| 60 | public: | ||
| 61 | explicit QtSoftwareKeyboard(GMainWindow& parent); | ||
| 62 | ~QtSoftwareKeyboard() override; | ||
| 63 | |||
| 64 | void RequestText(std::function<void(std::optional<std::u16string>)> out, | ||
| 65 | Core::Frontend::SoftwareKeyboardParameters parameters) const override; | ||
| 66 | void SendTextCheckDialog(std::u16string error_message, | ||
| 67 | std::function<void()> finished_check) const override; | ||
| 68 | |||
| 69 | signals: | ||
| 70 | void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; | ||
| 71 | void MainWindowTextCheckDialog(std::u16string error_message) const; | ||
| 72 | |||
| 73 | public slots: | ||
| 74 | void MainWindowFinishedText(std::optional<std::u16string> text); | ||
| 75 | void MainWindowFinishedCheckDialog(); | ||
| 76 | |||
| 77 | private: | ||
| 78 | mutable std::function<void(std::optional<std::u16string>)> text_output; | ||
| 79 | mutable std::function<void()> finished_check; | ||
| 80 | }; | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4b969119c..9e13bbf7c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -8,9 +8,11 @@ | |||
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | 9 | ||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 11 | #include "applets/software_keyboard.h" | ||
| 11 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 12 | #include "core/file_sys/vfs_real.h" | 13 | #include "core/file_sys/vfs_real.h" |
| 13 | #include "core/hle/service/acc/profile_manager.h" | 14 | #include "core/hle/service/acc/profile_manager.h" |
| 15 | #include "core/hle/service/am/applets/applets.h" | ||
| 14 | 16 | ||
| 15 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows | 17 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows |
| 16 | // defines. | 18 | // defines. |
| @@ -59,6 +61,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 59 | #include "core/file_sys/romfs.h" | 61 | #include "core/file_sys/romfs.h" |
| 60 | #include "core/file_sys/savedata_factory.h" | 62 | #include "core/file_sys/savedata_factory.h" |
| 61 | #include "core/file_sys/submission_package.h" | 63 | #include "core/file_sys/submission_package.h" |
| 64 | #include "core/frontend/applets/software_keyboard.h" | ||
| 62 | #include "core/hle/kernel/process.h" | 65 | #include "core/hle/kernel/process.h" |
| 63 | #include "core/hle/service/filesystem/filesystem.h" | 66 | #include "core/hle/service/filesystem/filesystem.h" |
| 64 | #include "core/hle/service/filesystem/fsp_ldr.h" | 67 | #include "core/hle/service/filesystem/fsp_ldr.h" |
| @@ -204,6 +207,27 @@ GMainWindow::~GMainWindow() { | |||
| 204 | delete render_window; | 207 | delete render_window; |
| 205 | } | 208 | } |
| 206 | 209 | ||
| 210 | void GMainWindow::SoftwareKeyboardGetText( | ||
| 211 | const Core::Frontend::SoftwareKeyboardParameters& parameters) { | ||
| 212 | QtSoftwareKeyboardDialog dialog(this, parameters); | ||
| 213 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | | ||
| 214 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); | ||
| 215 | dialog.setWindowModality(Qt::WindowModal); | ||
| 216 | dialog.exec(); | ||
| 217 | |||
| 218 | if (!dialog.GetStatus()) { | ||
| 219 | emit SoftwareKeyboardFinishedText(std::nullopt); | ||
| 220 | return; | ||
| 221 | } | ||
| 222 | |||
| 223 | emit SoftwareKeyboardFinishedText(dialog.GetText()); | ||
| 224 | } | ||
| 225 | |||
| 226 | void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) { | ||
| 227 | QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message)); | ||
| 228 | emit SoftwareKeyboardFinishedCheckDialog(); | ||
| 229 | } | ||
| 230 | |||
| 207 | void GMainWindow::InitializeWidgets() { | 231 | void GMainWindow::InitializeWidgets() { |
| 208 | #ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING | 232 | #ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING |
| 209 | ui.action_Report_Compatibility->setVisible(true); | 233 | ui.action_Report_Compatibility->setVisible(true); |
| @@ -559,6 +583,8 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 559 | 583 | ||
| 560 | system.SetGPUDebugContext(debug_context); | 584 | system.SetGPUDebugContext(debug_context); |
| 561 | 585 | ||
| 586 | system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this)); | ||
| 587 | |||
| 562 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; | 588 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; |
| 563 | 589 | ||
| 564 | const auto drd_callout = | 590 | const auto drd_callout = |
| @@ -1228,8 +1254,13 @@ void GMainWindow::OnMenuRecentFile() { | |||
| 1228 | 1254 | ||
| 1229 | void GMainWindow::OnStartGame() { | 1255 | void GMainWindow::OnStartGame() { |
| 1230 | emu_thread->SetRunning(true); | 1256 | emu_thread->SetRunning(true); |
| 1257 | |||
| 1258 | qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>( | ||
| 1259 | "Core::Frontend::SoftwareKeyboardParameters"); | ||
| 1231 | qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); | 1260 | qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); |
| 1232 | qRegisterMetaType<std::string>("std::string"); | 1261 | qRegisterMetaType<std::string>("std::string"); |
| 1262 | qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>"); | ||
| 1263 | |||
| 1233 | connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); | 1264 | connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); |
| 1234 | 1265 | ||
| 1235 | ui.action_Start->setEnabled(false); | 1266 | ui.action_Start->setEnabled(false); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 929250e8c..674e73412 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -29,6 +29,10 @@ class ProfilerWidget; | |||
| 29 | class WaitTreeWidget; | 29 | class WaitTreeWidget; |
| 30 | enum class GameListOpenTarget; | 30 | enum class GameListOpenTarget; |
| 31 | 31 | ||
| 32 | namespace Core::Frontend { | ||
| 33 | struct SoftwareKeyboardParameters; | ||
| 34 | } // namespace Core::Frontend | ||
| 35 | |||
| 32 | namespace FileSys { | 36 | namespace FileSys { |
| 33 | class RegisteredCacheUnion; | 37 | class RegisteredCacheUnion; |
| 34 | class VfsFilesystem; | 38 | class VfsFilesystem; |
| @@ -95,6 +99,13 @@ signals: | |||
| 95 | // Signal that tells widgets to update icons to use the current theme | 99 | // Signal that tells widgets to update icons to use the current theme |
| 96 | void UpdateThemedIcons(); | 100 | void UpdateThemedIcons(); |
| 97 | 101 | ||
| 102 | void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); | ||
| 103 | void SoftwareKeyboardFinishedCheckDialog(); | ||
| 104 | |||
| 105 | public slots: | ||
| 106 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); | ||
| 107 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); | ||
| 108 | |||
| 98 | private: | 109 | private: |
| 99 | void InitializeWidgets(); | 110 | void InitializeWidgets(); |
| 100 | void InitializeDebugWidgets(); | 111 | void InitializeDebugWidgets(); |