summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/string_util.cpp9
-rw-r--r--src/common/string_util.h8
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/core.cpp17
-rw-r--r--src/core/core.h5
-rw-r--r--src/core/frontend/applets/software_keyboard.cpp29
-rw-r--r--src/core/frontend/applets/software_keyboard.h54
-rw-r--r--src/core/hle/kernel/svc.cpp36
-rw-r--r--src/core/hle/service/am/am.cpp331
-rw-r--r--src/core/hle/service/am/am.h29
-rw-r--r--src/core/hle/service/am/applets/applets.cpp115
-rw-r--r--src/core/hle/service/am/applets/applets.h94
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp161
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h69
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/applets/software_keyboard.cpp152
-rw-r--r--src/yuzu/applets/software_keyboard.h80
-rw-r--r--src/yuzu/main.cpp31
-rw-r--r--src/yuzu/main.h11
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
217std::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
217const char* TrimSourcePath(const char* path, const char* root) { 226const 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) {
67std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); 67std::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 */
74std::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
500void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
501 impl->software_keyboard = std::move(applet);
502}
503
504const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
505 return *impl->software_keyboard;
506}
507
491System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 508System::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
14namespace Core::Frontend { 14namespace Core::Frontend {
15class EmuWindow; 15class EmuWindow;
16class SoftwareKeyboardApplet;
16} // namespace Core::Frontend 17} // namespace Core::Frontend
17 18
18namespace FileSys { 19namespace 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
239private: 244private:
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
9namespace Core::Frontend {
10SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
11
12void 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
21void 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
13namespace Core::Frontend {
14struct 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
36class SoftwareKeyboardApplet {
37public:
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
46class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
47public:
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
1185static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { 1185static 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
29namespace Service::AM { 33namespace Service::AM {
30 34
35constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
36constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
37
38enum class AppletId : u32 {
39 SoftwareKeyboard = 0x11,
40};
41
31constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; 42constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
32 43
33struct LaunchParameters { 44struct 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
495IStorage::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
507IStorage::~IStorage() = default;
508
509const std::vector<u8>& IStorage::GetData() const {
510 return buffer;
511}
512
484void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { 513void 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
503class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { 532class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
504public: 533public:
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
518private: 563private:
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
563class IStorage final : public ServiceFramework<IStorage> { 641 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
564public: 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
577private: 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
590class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { 676void IStorage::Open(Kernel::HLERequestContext& ctx) {
591public: 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
685IStorageAccessor::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(); 698IStorageAccessor::~IStorageAccessor() = default;
619 state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
620 "ILibraryAppletAccessor:StateChangedEvent");
621 }
622 699
623private: 700void 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) { 709void 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) { 728void 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
672ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { 747ILibraryAppletCreator::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
684ILibraryAppletCreator::~ILibraryAppletCreator() = default; 759ILibraryAppletCreator::~ILibraryAppletCreator() = default;
685 760
761static 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
686void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { 771void 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
807void 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
707IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { 832IApplicationFunctions::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
158class IStorage final : public ServiceFramework<IStorage> {
159public:
160 explicit IStorage(std::vector<u8> buffer);
161 ~IStorage() override;
162
163 const std::vector<u8>& GetData() const;
164
165private:
166 void Open(Kernel::HLERequestContext& ctx);
167
168 std::vector<u8> buffer;
169
170 friend class IStorageAccessor;
171};
172
173class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
174public:
175 explicit IStorageAccessor(IStorage& backing);
176 ~IStorageAccessor() override;
177
178private:
179 void GetSize(Kernel::HLERequestContext& ctx);
180 void Write(Kernel::HLERequestContext& ctx);
181 void Read(Kernel::HLERequestContext& ctx);
182
183 IStorage& backing;
184};
185
158class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { 186class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
159public: 187public:
160 ILibraryAppletCreator(); 188 ILibraryAppletCreator();
@@ -163,6 +191,7 @@ public:
163private: 191private:
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
168class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { 197class 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
13namespace Service::AM::Applets {
14
15AppletDataBroker::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
25AppletDataBroker::~AppletDataBroker() = default;
26
27std::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
36std::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
45std::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
54std::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
63void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
64 in_channel.push(std::make_unique<IStorage>(storage));
65}
66
67void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
68 out_channel.push(std::make_unique<IStorage>(storage));
69 pop_out_data_event->Signal();
70}
71
72void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
73 in_interactive_channel.push(std::make_unique<IStorage>(storage));
74}
75
76void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
77 out_interactive_channel.push(std::make_unique<IStorage>(storage));
78 pop_interactive_out_data_event->Signal();
79}
80
81void AppletDataBroker::SignalStateChanged() const {
82 state_changed_event->Signal();
83}
84
85Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const {
86 return pop_out_data_event;
87}
88
89Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const {
90 return pop_interactive_out_data_event;
91}
92
93Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const {
94 return state_changed_event;
95}
96
97Applet::Applet() = default;
98
99Applet::~Applet() = default;
100
101void 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
13union ResultCode;
14
15namespace Service::AM {
16
17class IStorage;
18
19namespace Applets {
20
21class AppletDataBroker final {
22public:
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
44private:
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
61class Applet {
62public:
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
77protected:
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
13namespace Service::AM::Applets {
14
15constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
16constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
17constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
18constexpr bool INTERACTIVE_STATUS_OK = false;
19
20static 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
41SoftwareKeyboard::SoftwareKeyboard() = default;
42
43SoftwareKeyboard::~SoftwareKeyboard() = default;
44
45void 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
72bool SoftwareKeyboard::TransactionComplete() const {
73 return complete;
74}
75
76ResultCode SoftwareKeyboard::GetStatus() const {
77 return RESULT_SUCCESS;
78}
79
80void 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
102void 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
116void 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
11namespace Service::AM::Applets {
12
13enum class KeysetDisable : u32 {
14 Space = 0x02,
15 Address = 0x04,
16 Percent = 0x08,
17 Slashes = 0x10,
18 Numbers = 0x40,
19 DownloadCode = 0x80,
20};
21
22struct 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};
46static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
47
48class SoftwareKeyboard final : public Applet {
49public:
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
62private:
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
16QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
17 Core::Frontend::SoftwareKeyboardParameters parameters)
18 : parameters(std::move(parameters)) {}
19
20QValidator::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
44QtSoftwareKeyboardDialog::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
97QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
98
99void QtSoftwareKeyboardDialog::Submit() {
100 ok = true;
101 text = line_edit->text().toStdU16String();
102 accept();
103}
104
105void QtSoftwareKeyboardDialog::Reject() {
106 ok = false;
107 text.clear();
108 accept();
109}
110
111std::u16string QtSoftwareKeyboardDialog::GetText() const {
112 return text;
113}
114
115bool QtSoftwareKeyboardDialog::GetStatus() const {
116 return ok;
117}
118
119QtSoftwareKeyboard::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
128QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
129
130void 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
136void 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
142void 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
148void 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
12class GMainWindow;
13class QDialogButtonBox;
14class QLabel;
15class QLineEdit;
16class QVBoxLayout;
17class QtSoftwareKeyboard;
18
19class QtSoftwareKeyboardValidator final : public QValidator {
20public:
21 explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters);
22 State validate(QString& input, int& pos) const override;
23
24private:
25 Core::Frontend::SoftwareKeyboardParameters parameters;
26};
27
28class QtSoftwareKeyboardDialog final : public QDialog {
29 Q_OBJECT
30
31public:
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
42private:
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
57class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
58 Q_OBJECT
59
60public:
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
69signals:
70 void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const;
71 void MainWindowTextCheckDialog(std::u16string error_message) const;
72
73public slots:
74 void MainWindowFinishedText(std::optional<std::u16string> text);
75 void MainWindowFinishedCheckDialog();
76
77private:
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
210void 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
226void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) {
227 QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message));
228 emit SoftwareKeyboardFinishedCheckDialog();
229}
230
207void GMainWindow::InitializeWidgets() { 231void 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
1229void GMainWindow::OnStartGame() { 1255void 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;
29class WaitTreeWidget; 29class WaitTreeWidget;
30enum class GameListOpenTarget; 30enum class GameListOpenTarget;
31 31
32namespace Core::Frontend {
33struct SoftwareKeyboardParameters;
34} // namespace Core::Frontend
35
32namespace FileSys { 36namespace FileSys {
33class RegisteredCacheUnion; 37class RegisteredCacheUnion;
34class VfsFilesystem; 38class 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
105public slots:
106 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
107 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
108
98private: 109private:
99 void InitializeWidgets(); 110 void InitializeWidgets();
100 void InitializeDebugWidgets(); 111 void InitializeDebugWidgets();