From 2a6ebadf66051362cdcf07d722f7e2d3cee14c82 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 25 May 2015 23:30:20 -0500 Subject: [PATCH 1/3] HLE/APT: Initial HLE support for applets. Currently only the SWKBD is emulated, and there's currently no way to ask the user for input, so it always returns "Subv" as the text. --- src/core/CMakeLists.txt | 4 + src/core/hle/applets/applet.cpp | 40 +++++++++ src/core/hle/applets/applet.h | 53 +++++++++++ src/core/hle/applets/swkbd.cpp | 73 +++++++++++++++ src/core/hle/applets/swkbd.h | 67 ++++++++++++++ src/core/hle/service/apt/apt.cpp | 139 ++++++++++++++++++++++------- src/core/hle/service/apt/apt.h | 49 +++++++++- src/core/hle/service/apt/apt_a.cpp | 31 ++++--- src/core/hle/service/apt/apt_u.cpp | 4 +- 9 files changed, 410 insertions(+), 50 deletions(-) create mode 100644 src/core/hle/applets/applet.cpp create mode 100644 src/core/hle/applets/applet.h create mode 100644 src/core/hle/applets/swkbd.cpp create mode 100644 src/core/hle/applets/swkbd.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4fcda48748..9b004440c2 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -25,6 +25,8 @@ set(SRCS file_sys/ivfc_archive.cpp hle/config_mem.cpp hle/hle.cpp + hle/applets/applet.cpp + hle/applets/swkbd.cpp hle/kernel/address_arbiter.cpp hle/kernel/event.cpp hle/kernel/kernel.cpp @@ -150,6 +152,8 @@ set(HEADERS hle/config_mem.h hle/function_wrappers.h hle/hle.h + hle/applets/applet.h + hle/applets/swkbd.h hle/kernel/address_arbiter.h hle/kernel/event.h hle/kernel/kernel.h diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp new file mode 100644 index 0000000000..1f447e5fc2 --- /dev/null +++ b/src/core/hle/applets/applet.cpp @@ -0,0 +1,40 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" + +#include "core/hle/applets/applet.h" +#include "core/hle/applets/swkbd.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace HLE { +namespace Applets { + +static std::unordered_map> applets; + +ResultCode Applet::Create(Service::APT::AppletId id) { + switch (id) { + case Service::APT::AppletId::SoftwareKeyboard1: + case Service::APT::AppletId::SoftwareKeyboard2: + applets[id] = std::make_shared(id); + break; + default: + // TODO(Subv): Find the right error code + return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported, ErrorLevel::Permanent); + } + + return RESULT_SUCCESS; +} + +std::shared_ptr Applet::Get(Service::APT::AppletId id) { + auto itr = applets.find(id); + if (itr != applets.end()) + return itr->second; + return nullptr; +} + +} +} // namespace diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h new file mode 100644 index 0000000000..221348d9c3 --- /dev/null +++ b/src/core/hle/applets/applet.h @@ -0,0 +1,53 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +class Applet { +public: + virtual ~Applet() {}; + Applet(Service::APT::AppletId id) : id(id) {}; + + /** + * Creates an instance of the Applet subclass identified by the parameter + * and stores it in a global map. + * @param id Id of the applet to create + * @returns ResultCode Whether the operation was successful or not + */ + static ResultCode Create(Service::APT::AppletId id); + + /** + * Retrieves the Applet instance identified by the specified id + * @param id Id of the Applet to retrieve + * @returns Requested Applet or nullptr if not found + */ + static std::shared_ptr Get(Service::APT::AppletId id); + + /** + * Handles a parameter from the application + * @param parameter Parameter data to handle + * @returns ResultCode Whether the operation was successful or not + */ + virtual ResultCode ReceiveParameter(Service::APT::MessageParameter const& parameter) = 0; + + /** + * Handles the Applet start event, triggered from the application + * @param parameter Parameter data to handle + * @returns ResultCode Whether the operation was successful or not + */ + virtual ResultCode Start(Service::APT::AppletStartupParameter const& parameter) = 0; + + Service::APT::AppletId id; ///< Id of this Applet +}; + +} +} // namespace diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp new file mode 100644 index 0000000000..224aeb096b --- /dev/null +++ b/src/core/hle/applets/swkbd.cpp @@ -0,0 +1,73 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" + +#include "core/hle/applets/swkbd.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace HLE { +namespace Applets { + +SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id) { + // Create the SharedMemory that will hold the framebuffer data + // TODO(Subv): What size should we use here? + using Kernel::MemoryPermission; + framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); +} + +ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { + if (parameter.signal != static_cast(Service::APT::SignalType::LibAppJustStarted)) { + LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); + UNIMPLEMENTED(); + // TODO(Subv): Find the right error code + return ResultCode(-1); + } + + Service::APT::MessageParameter result; + // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo + result.signal = static_cast(Service::APT::SignalType::LibAppFinished); + result.data = nullptr; + result.buffer_size = 0; + result.destination_id = static_cast(Service::APT::AppletId::Application); + result.sender_id = static_cast(id); + result.object = framebuffer_memory; + + Service::APT::SendParameter(result); + return RESULT_SUCCESS; +} + +ResultCode SoftwareKeyboard::Start(Service::APT::AppletStartupParameter const& parameter) { + memcpy(&config, parameter.data, parameter.buffer_size); + text_memory = boost::static_pointer_cast(parameter.object); + + // TODO(Subv): Verify if this is the correct behavior + memset(text_memory->GetPointer(), 0, text_memory->size); + + // TODO(Subv): Remove this hardcoded text + const wchar_t str[] = L"Subv"; + memcpy(text_memory->GetPointer(), str, 4 * sizeof(wchar_t)); + + // TODO(Subv): Ask for input and write it to the shared memory + // TODO(Subv): Find out what are the possible values for the return code, + // some games seem to check for a hardcoded 2 + config.return_code = 2; + config.text_length = 5; + config.text_offset = 0; + + Service::APT::MessageParameter message; + message.buffer_size = sizeof(SoftwareKeyboardConfig); + message.data = reinterpret_cast(&config); + message.signal = static_cast(Service::APT::SignalType::LibAppClosed); + message.destination_id = static_cast(Service::APT::AppletId::Application); + message.sender_id = static_cast(id); + Service::APT::SendParameter(message); + + return RESULT_SUCCESS; +} + +} +} // namespace diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h new file mode 100644 index 0000000000..d7199690c6 --- /dev/null +++ b/src/core/hle/applets/swkbd.h @@ -0,0 +1,67 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/hle/applets/applet.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +struct SoftwareKeyboardConfig { + INSERT_PADDING_WORDS(0x8); + + u16 max_text_length; ///< Maximum length of the input text + + INSERT_PADDING_BYTES(0x6E); + + char16_t display_text[65]; ///< Text to display when asking the user for input + + INSERT_PADDING_BYTES(0xE); + + u32 default_text_offset; ///< Offset of the default text in the output SharedMemory + + INSERT_PADDING_WORDS(0x3); + + u32 shared_memory_size; ///< Size of the SharedMemory + + INSERT_PADDING_WORDS(0x1); + + u32 return_code; ///< Return code of the SoftwareKeyboard, usually 2, other values are unknown + + INSERT_PADDING_WORDS(0x2); + + u32 text_offset; ///< Offset in the SharedMemory where the output text starts + u16 text_length; ///< Length in characters of the output text + + INSERT_PADDING_BYTES(0x2B6); +}; + +static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config size is wrong"); + +class SoftwareKeyboard : public Applet { +public: + SoftwareKeyboard(Service::APT::AppletId id); + ~SoftwareKeyboard() {} + + ResultCode ReceiveParameter(Service::APT::MessageParameter const& parameter) override; + ResultCode Start(Service::APT::AppletStartupParameter const& parameter) override; + + /// TODO(Subv): Find out what this is actually used for. + // It is believed that the application stores the current screen image here. + Kernel::SharedPtr framebuffer_memory; + + /// SharedMemory where the output text will be stored + Kernel::SharedPtr text_memory; + + /// Configuration of this instance of the SoftwareKeyboard, as received from the application + SoftwareKeyboardConfig config; +}; + +} +} // namespace diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index b454a2709c..0c3889e837 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -6,6 +6,7 @@ #include "common/file_util.h" #include "common/logging/log.h" +#include "core/hle/applets/applet.h" #include "core/hle/service/service.h" #include "core/hle/service/apt/apt.h" #include "core/hle/service/apt/apt_a.h" @@ -34,12 +35,21 @@ static Kernel::SharedPtr shared_font_mem; static Kernel::SharedPtr lock; static Kernel::SharedPtr notification_event; ///< APT notification event -static Kernel::SharedPtr start_event; ///< APT start event +static Kernel::SharedPtr parameter_event; ///< APT parameter event static std::vector shared_font; static u32 cpu_percent; ///< CPU time available to the running application +/// Parameter data to be returned in the next call to Glance/ReceiveParameter +static MessageParameter next_parameter; + +void SendParameter(MessageParameter const& parameter) { + next_parameter = parameter; + // Signal the event to let the application know that a new parameter is ready to be read + parameter_event->Signal(); +} + void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; @@ -47,18 +57,18 @@ void Initialize(Service::Interface* self) { cmd_buff[2] = IPC::MoveHandleDesc(2); cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(start_event).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(parameter_event).MoveFrom(); // TODO(bunnei): Check if these events are cleared every time Initialize is called. notification_event->Clear(); - start_event->Clear(); + parameter_event->Clear(); ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); lock->Release(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_TRACE(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); + LOG_WARNING(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); } void GetSharedFont(Service::Interface* self) { @@ -85,9 +95,7 @@ void GetSharedFont(Service::Interface* self) { void NotifyToWait(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; - // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further. - start_event->Signal(); - + cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); } @@ -112,6 +120,7 @@ void Enable(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? cmd_buff[1] = RESULT_SUCCESS.raw; // No error + parameter_event->Signal(); // Let the application know that it has been started LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); } @@ -121,8 +130,8 @@ void GetAppletManInfo(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; cmd_buff[3] = 0; - cmd_buff[4] = static_cast(AppID::HomeMenu); // Home menu AppID - cmd_buff[5] = static_cast(AppID::Application); // TODO(purpasmart96): Do this correctly + cmd_buff[4] = static_cast(AppletId::HomeMenu); // Home menu AppID + cmd_buff[5] = static_cast(AppletId::Application); // TODO(purpasmart96): Do this correctly LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); } @@ -131,7 +140,13 @@ void IsRegistered(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 1; // Set to registered + /// TODO(Subv): It is currently unknown what this value (0x400) means, + /// but i believe it is used as a global "LibraryApplet" id, to verify if there's + /// any LibApplet currently running. This is not verified. + if (app_id != 0x400) + cmd_buff[2] = 1; // Set to registered + else + cmd_buff[2] = 0; // Set to not registered LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); } @@ -145,50 +160,81 @@ void InquireNotification(Service::Interface* self) { void SendParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 src_app_id = cmd_buff[1]; - u32 dst_app_id = cmd_buff[2]; - u32 signal_type = cmd_buff[3]; - u32 buffer_size = cmd_buff[4]; - u32 value = cmd_buff[5]; - u32 handle = cmd_buff[6]; - u32 size = cmd_buff[7]; - u32 in_param_buffer_ptr = cmd_buff[8]; + u32 src_app_id = cmd_buff[1]; + u32 dst_app_id = cmd_buff[2]; + u32 signal_type = cmd_buff[3]; + u32 buffer_size = cmd_buff[4]; + u32 value = cmd_buff[5]; + u32 handle = cmd_buff[6]; + u32 size = cmd_buff[7]; + u32 buffer = cmd_buff[8]; - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + std::shared_ptr dest_applet = HLE::Applets::Applet::Get(static_cast(dst_app_id)); + + if (dest_applet == nullptr) { + LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); + return; + } + + MessageParameter param; + param.buffer_size = buffer_size; + param.destination_id = dst_app_id; + param.sender_id = src_app_id; + param.object = Kernel::g_handle_table.GetGeneric(handle); + param.signal = signal_type; + param.data = Memory::GetPointer(buffer); + + cmd_buff[1] = dest_applet->ReceiveParameter(param).raw; LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X", - src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, in_param_buffer_ptr); + src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, buffer); } void ReceiveParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; u32 buffer_size = cmd_buff[2]; + VAddr buffer = cmd_buff[0x104 >> 2]; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; - cmd_buff[3] = static_cast(SignalType::AppJustStarted); // Signal type - cmd_buff[4] = 0x10; // Parameter buffer size (16) - cmd_buff[5] = 0; + cmd_buff[2] = next_parameter.sender_id; + cmd_buff[3] = next_parameter.signal; // Signal type + cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size + cmd_buff[5] = 0x10; cmd_buff[6] = 0; - cmd_buff[7] = 0; - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); + if (next_parameter.object != nullptr) + cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); + cmd_buff[7] = (next_parameter.buffer_size << 14) | 2; + cmd_buff[8] = buffer; + + if (next_parameter.data) + memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size)); + + LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); } void GlanceParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; u32 buffer_size = cmd_buff[2]; + VAddr buffer = cmd_buff[0x104 >> 2]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; - cmd_buff[3] = static_cast(SignalType::AppJustStarted); // Signal type - cmd_buff[4] = 0x10; // Parameter buffer size (16) - cmd_buff[5] = 0; + cmd_buff[2] = next_parameter.sender_id; + cmd_buff[3] = next_parameter.signal; // Signal type + cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size + cmd_buff[5] = 0x10; cmd_buff[6] = 0; - cmd_buff[7] = 0; + if (next_parameter.object != nullptr) + cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); + cmd_buff[7] = (next_parameter.buffer_size << 14) | 2; + cmd_buff[8] = buffer; - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); + if (next_parameter.data) + memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size)); + + LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); } void CancelParameter(Service::Interface* self) { @@ -281,6 +327,28 @@ void GetAppCpuTimeLimit(Service::Interface* self) { LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value); } +void PrepareToStartLibraryApplet(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = HLE::Applets::Applet::Create(static_cast(cmd_buff[1])).raw; +} + +void StartLibraryApplet(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + std::shared_ptr applet = HLE::Applets::Applet::Get(static_cast(cmd_buff[1])); + + if (applet == nullptr) { + cmd_buff[1] = -1; // TODO(Subv): Find the right error code + return; + } + + AppletStartupParameter parameter; + parameter.buffer_size = cmd_buff[2]; + parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]); + parameter.data = Memory::GetPointer(cmd_buff[6]); + + cmd_buff[1] = applet->Start(parameter).raw; +} + void Init() { AddService(new APT_A_Interface); AddService(new APT_S_Interface); @@ -318,7 +386,10 @@ void Init() { // TODO(bunnei): Check if these are created in Initialize or on APT process startup. notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); - start_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); + parameter_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Start"); + + next_parameter.signal = static_cast(SignalType::AppJustStarted); + next_parameter.destination_id = 0x300; } void Shutdown() { @@ -326,7 +397,7 @@ void Shutdown() { shared_font_mem = nullptr; lock = nullptr; notification_event = nullptr; - start_event = nullptr; + parameter_event = nullptr; } } // namespace APT diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index a03e1712a3..510193cc80 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -11,6 +11,23 @@ namespace Service { namespace APT { +/// Holds information about the parameters used in Send/Glance/ReceiveParameter +struct MessageParameter { + u32 sender_id = 0; + u32 destination_id = 0; + u32 signal = 0; + u32 buffer_size = 0; + Kernel::SharedPtr object = nullptr; + u8* data = nullptr; +}; + +/// Holds information about the parameters used in StartLibraryApplet +struct AppletStartupParameter { + u32 buffer_size = 0; + Kernel::SharedPtr object = nullptr; + u8* data = nullptr; +}; + /// Signals used by APT functions enum class SignalType : u32 { None = 0x0, @@ -23,7 +40,7 @@ enum class SignalType : u32 { }; /// App Id's used by APT functions -enum class AppID : u32 { +enum class AppletId : u32 { HomeMenu = 0x101, AlternateMenu = 0x103, Camera = 0x110, @@ -45,6 +62,9 @@ enum class AppID : u32 { SoftwareKeyboard2 = 0x401, }; +/// Send a parameter to the currently-running application, which will read it via ReceiveParameter +void SendParameter(MessageParameter const& parameter); + /** * APT::Initialize service function * Service function that initializes the APT process for the running application @@ -249,6 +269,33 @@ void SetAppCpuTimeLimit(Service::Interface* self); */ void GetAppCpuTimeLimit(Service::Interface* self); +/** + * APT::PrepareToStartLibraryApplet service function + * Inputs: + * 0 : Command header [0x00180040] + * 1 : Id of the applet to start + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +void PrepareToStartLibraryApplet(Service::Interface* self); + +/** + * APT::StartLibraryApplet service function + * Inputs: + * 0 : Command header [0x001E0084] + * 1 : Id of the applet to start + * 2 : Buffer size + * 3 : Always 0? + * 4 : Handle passed to the applet + * 5 : (Size << 14) | 2 + * 6 : Input buffer virtual address + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +void StartLibraryApplet(Service::Interface* self); + /// Initialize the APT service void Init(); diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 8649342458..88de339f97 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -10,19 +10,24 @@ namespace Service { namespace APT { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetLockHandle, "GetLockHandle?"}, - {0x00020080, Initialize, "Initialize?"}, - {0x00030040, Enable, "Enable?"}, - {0x00040040, nullptr, "Finalize?"}, - {0x00050040, nullptr, "GetAppletManInfo?"}, - {0x00060040, nullptr, "GetAppletInfo?"}, - {0x000D0080, ReceiveParameter, "ReceiveParameter?"}, - {0x000E0080, GlanceParameter, "GlanceParameter?"}, - {0x003B0040, nullptr, "CancelLibraryApplet?"}, - {0x00430040, NotifyToWait, "NotifyToWait?"}, - {0x00440000, GetSharedFont, "GetSharedFont?"}, - {0x004B00C2, AppletUtility, "AppletUtility?"}, - {0x00550040, nullptr, "WriteInputToNsState?"}, + {0x00010040, GetLockHandle, "GetLockHandle?"}, + {0x00020080, Initialize, "Initialize?"}, + {0x00030040, Enable, "Enable?"}, + {0x00040040, nullptr, "Finalize?"}, + {0x00050040, nullptr, "GetAppletManInfo?"}, + {0x00060040, nullptr, "GetAppletInfo?"}, + {0x00090040, IsRegistered, "IsRegistered"}, + {0x000C0104, SendParameter, "SendParameter"}, + {0x000D0080, ReceiveParameter, "ReceiveParameter"}, + {0x000E0080, GlanceParameter, "GlanceParameter"}, + {0x000F0100, CancelParameter, "CancelParameter"}, + {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, + {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, + {0x003B0040, nullptr, "CancelLibraryApplet?"}, + {0x00430040, NotifyToWait, "NotifyToWait?"}, + {0x00440000, GetSharedFont, "GetSharedFont?"}, + {0x004B00C2, AppletUtility, "AppletUtility?"}, + {0x00550040, nullptr, "WriteInputToNsState?"}, }; APT_A_Interface::APT_A_Interface() { diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index d006b59308..b724cd72bd 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -35,13 +35,13 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00150140, nullptr, "PrepareToStartApplication"}, {0x00160040, nullptr, "PreloadLibraryApplet"}, {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, - {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, + {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, {0x001B00C4, nullptr, "StartApplication"}, {0x001C0000, nullptr, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, - {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, nullptr, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, {0x00210000, nullptr, "OrderToCloseApplication"}, From 621ee10eae0546d4ec3f9e911e113aa9ee609c22 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 26 May 2015 11:00:26 -0500 Subject: [PATCH 2/3] Applets: Add infrastructure to allow custom drawing and input handling in Applets. --- src/core/hle/applets/applet.cpp | 38 +++++++++++++++++++++++++++ src/core/hle/applets/applet.h | 43 +++++++++++++++++++++--------- src/core/hle/applets/swkbd.cpp | 45 +++++++++++++++++++++++++++----- src/core/hle/applets/swkbd.h | 16 ++++++++++++ src/core/hle/service/apt/apt.cpp | 45 +++++++++++++++++++------------- src/core/hle/service/gsp_gpu.cpp | 4 +-- src/core/hle/service/gsp_gpu.h | 10 +++++++ 7 files changed, 162 insertions(+), 39 deletions(-) diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 1f447e5fc2..689f5adc8b 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -5,15 +5,35 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "core/core_timing.h" #include "core/hle/applets/applet.h" #include "core/hle/applets/swkbd.h" //////////////////////////////////////////////////////////////////////////////////////////////////// +// Specializes std::hash for AppletId, so that we can use it in std::unordered_map. +// Workaround for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 +namespace std { + template <> + struct hash { + typedef Service::APT::AppletId argument_type; + typedef std::size_t result_type; + + result_type operator()(const argument_type& id_code) const { + typedef std::underlying_type::type Type; + return std::hash()(static_cast(id_code)); + } + }; +} + namespace HLE { namespace Applets { static std::unordered_map> applets; +static u32 applet_update_event = -1; ///< The CoreTiming event identifier for the Applet update callback. +/// The interval at which the Applet update callback will be called. +static const u64 applet_update_interval_microseconds = 16666; +std::shared_ptr g_current_applet = nullptr; ///< The applet that is currently executing ResultCode Applet::Create(Service::APT::AppletId id) { switch (id) { @@ -36,5 +56,23 @@ std::shared_ptr Applet::Get(Service::APT::AppletId id) { return nullptr; } +/// Handles updating the current Applet every time it's called. +static void AppletUpdateEvent(u64, int cycles_late) { + if (g_current_applet && g_current_applet->IsRunning()) + g_current_applet->Update(); + + CoreTiming::ScheduleEvent(usToCycles(applet_update_interval) - cycles_late, + applet_update_event); +} + +void Init() { + applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent); + CoreTiming::ScheduleEvent(usToCycles(applet_update_interval), applet_update_event); +} + +void Shutdown() { + CoreTiming::UnscheduleEvent(applet_update_event, 0); +} + } } // namespace diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h index 221348d9c3..f50f7d6047 100644 --- a/src/core/hle/applets/applet.h +++ b/src/core/hle/applets/applet.h @@ -12,42 +12,59 @@ namespace HLE { namespace Applets { -class Applet { +class Applet : public std::enable_shared_from_this { public: virtual ~Applet() {}; Applet(Service::APT::AppletId id) : id(id) {}; /** - * Creates an instance of the Applet subclass identified by the parameter + * Creates an instance of the Applet subclass identified by the parameter. * and stores it in a global map. - * @param id Id of the applet to create - * @returns ResultCode Whether the operation was successful or not + * @param id Id of the applet to create. + * @returns ResultCode Whether the operation was successful or not. */ static ResultCode Create(Service::APT::AppletId id); /** - * Retrieves the Applet instance identified by the specified id - * @param id Id of the Applet to retrieve - * @returns Requested Applet or nullptr if not found + * Retrieves the Applet instance identified by the specified id. + * @param id Id of the Applet to retrieve. + * @returns Requested Applet or nullptr if not found. */ static std::shared_ptr Get(Service::APT::AppletId id); /** - * Handles a parameter from the application - * @param parameter Parameter data to handle - * @returns ResultCode Whether the operation was successful or not + * Handles a parameter from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. */ virtual ResultCode ReceiveParameter(Service::APT::MessageParameter const& parameter) = 0; /** - * Handles the Applet start event, triggered from the application - * @param parameter Parameter data to handle - * @returns ResultCode Whether the operation was successful or not + * Handles the Applet start event, triggered from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. */ virtual ResultCode Start(Service::APT::AppletStartupParameter const& parameter) = 0; + /** + * Whether the applet is currently executing instead of the host application or not. + */ + virtual bool IsRunning() = 0; + + /** + * Handles an update tick for the Applet, lets it update the screen, send commands, etc. + */ + virtual void Update() = 0; + Service::APT::AppletId id; ///< Id of this Applet }; +/// Initializes the HLE applets +void Init(); + +/// Shuts down the HLE applets +void Shutdown(); + +extern std::shared_ptr g_current_applet; ///< Applet that is currently executing } } // namespace diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index 224aeb096b..b800e0eb43 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -6,13 +6,16 @@ #include "common/logging/log.h" #include "core/hle/applets/swkbd.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/gsp_gpu.h" +#include "video_core/video_core.h" //////////////////////////////////////////////////////////////////////////////////////////////////// namespace HLE { namespace Applets { -SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id) { +SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) { // Create the SharedMemory that will hold the framebuffer data // TODO(Subv): What size should we use here? using Kernel::MemoryPermission; @@ -47,17 +50,45 @@ ResultCode SoftwareKeyboard::Start(Service::APT::AppletStartupParameter const& p // TODO(Subv): Verify if this is the correct behavior memset(text_memory->GetPointer(), 0, text_memory->size); + DrawScreenKeyboard(); + + // Update the current applet so we can get update events + started = true; + g_current_applet = shared_from_this(); + return RESULT_SUCCESS; +} + +void SoftwareKeyboard::Update() { + // TODO(Subv): Handle input using the touch events from the HID module + // TODO(Subv): Remove this hardcoded text - const wchar_t str[] = L"Subv"; - memcpy(text_memory->GetPointer(), str, 4 * sizeof(wchar_t)); - + std::u16string text = Common::UTF8ToUTF16("Citra"); + memcpy(text_memory->GetPointer(), text.c_str(), text.length() * sizeof(char16_t)); + // TODO(Subv): Ask for input and write it to the shared memory // TODO(Subv): Find out what are the possible values for the return code, // some games seem to check for a hardcoded 2 config.return_code = 2; - config.text_length = 5; + config.text_length = 6; config.text_offset = 0; + // TODO(Subv): We're finalizing the applet immediately after it's started, + // but we should defer this call until after all the input has been collected. + Finalize(); +} + +void SoftwareKeyboard::DrawScreenKeyboard() { + auto bottom_screen = GSP_GPU::GetFrameBufferInfo(0, 1); + auto info = bottom_screen->framebuffer_info[bottom_screen->index]; + + // TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer + memset(Memory::GetPointer(info.address_left), 0, info.stride * 320); + + GSP_GPU::SetBufferSwap(1, info); +} + +void SoftwareKeyboard::Finalize() { + // Let the application know that we're closing Service::APT::MessageParameter message; message.buffer_size = sizeof(SoftwareKeyboardConfig); message.data = reinterpret_cast(&config); @@ -66,7 +97,9 @@ ResultCode SoftwareKeyboard::Start(Service::APT::AppletStartupParameter const& p message.sender_id = static_cast(id); Service::APT::SendParameter(message); - return RESULT_SUCCESS; + started = false; + // Unset the current applet, we are not running anymore + g_current_applet = nullptr; } } diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h index d7199690c6..5970390c65 100644 --- a/src/core/hle/applets/swkbd.h +++ b/src/core/hle/applets/swkbd.h @@ -51,6 +51,19 @@ public: ResultCode ReceiveParameter(Service::APT::MessageParameter const& parameter) override; ResultCode Start(Service::APT::AppletStartupParameter const& parameter) override; + void Update() override; + bool IsRunning() override { return started; } + + /** + * Draws a keyboard to the current bottom screen framebuffer. + */ + void DrawScreenKeyboard(); + + /** + * Sends the LibAppletClosing signal to the application, + * along with the relevant data buffers. + */ + void Finalize(); /// TODO(Subv): Find out what this is actually used for. // It is believed that the application stores the current screen image here. @@ -61,6 +74,9 @@ public: /// Configuration of this instance of the SoftwareKeyboard, as received from the application SoftwareKeyboardConfig config; + + /// Whether this applet is currently running instead of the host application or not. + bool started; }; } diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 0c3889e837..783fad7ca1 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -68,7 +68,7 @@ void Initialize(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); } void GetSharedFont(Service::Interface* self) { @@ -95,7 +95,6 @@ void GetSharedFont(Service::Interface* self) { void NotifyToWait(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; - cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); } @@ -108,7 +107,7 @@ void GetLockHandle(Service::Interface* self) { // Not sure what these parameters are used for, but retail apps check that they are 0 after // GetLockHandle has been called. - cmd_buff[2] = 0; + cmd_buff[2] = 0; // Applet Attributes, this value is passed to Enable. cmd_buff[3] = 0; cmd_buff[4] = 0; @@ -118,10 +117,10 @@ void GetLockHandle(Service::Interface* self) { void Enable(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? + u32 attributes = cmd_buff[1]; cmd_buff[1] = RESULT_SUCCESS.raw; // No error parameter_event->Signal(); // Let the application know that it has been started - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); + LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); } void GetAppletManInfo(Service::Interface* self) { @@ -160,19 +159,20 @@ void InquireNotification(Service::Interface* self) { void SendParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 src_app_id = cmd_buff[1]; - u32 dst_app_id = cmd_buff[2]; - u32 signal_type = cmd_buff[3]; - u32 buffer_size = cmd_buff[4]; - u32 value = cmd_buff[5]; - u32 handle = cmd_buff[6]; - u32 size = cmd_buff[7]; - u32 buffer = cmd_buff[8]; + u32 src_app_id = cmd_buff[1]; + u32 dst_app_id = cmd_buff[2]; + u32 signal_type = cmd_buff[3]; + u32 buffer_size = cmd_buff[4]; + u32 value = cmd_buff[5]; + u32 handle = cmd_buff[6]; + u32 size = cmd_buff[7]; + u32 buffer = cmd_buff[8]; std::shared_ptr dest_applet = HLE::Applets::Applet::Get(static_cast(dst_app_id)); if (dest_applet == nullptr) { LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); + cmd_buff[1] = -1; // TODO(Subv): Find the right error code return; } @@ -286,7 +286,7 @@ void AppletUtility(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // These are from 3dbrew - I'm not really sure what they're used for. - u32 unk = cmd_buff[1]; + u32 command = cmd_buff[1]; u32 buffer1_size = cmd_buff[2]; u32 buffer2_size = cmd_buff[3]; u32 buffer1_addr = cmd_buff[5]; @@ -294,8 +294,8 @@ void AppletUtility(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " - "buffer1_addr=0x%08X, buffer2_addr=0x%08X", unk, buffer1_size, buffer2_size, + LOG_WARNING(Service_APT, "(STUBBED) called command=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " + "buffer1_addr=0x%08X, buffer2_addr=0x%08X", command, buffer1_size, buffer2_size, buffer1_addr, buffer2_addr); } @@ -329,14 +329,20 @@ void GetAppCpuTimeLimit(Service::Interface* self) { void PrepareToStartLibraryApplet(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[1] = HLE::Applets::Applet::Create(static_cast(cmd_buff[1])).raw; + AppletId applet_id = static_cast(cmd_buff[1]); + cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw; + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); } void StartLibraryApplet(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - std::shared_ptr applet = HLE::Applets::Applet::Get(static_cast(cmd_buff[1])); + AppletId applet_id = static_cast(cmd_buff[1]); + std::shared_ptr applet = HLE::Applets::Applet::Get(applet_id); + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + if (applet == nullptr) { + LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id); cmd_buff[1] = -1; // TODO(Subv): Find the right error code return; } @@ -354,6 +360,8 @@ void Init() { AddService(new APT_S_Interface); AddService(new APT_U_Interface); + HLE::Applets::Init(); + // Load the shared system font (if available). // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided @@ -398,6 +406,7 @@ void Shutdown() { lock = nullptr; notification_event = nullptr; parameter_event = nullptr; + HLE::Applets::Shutdown(); } } // namespace APT diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index f56bbe50f3..f175085e8c 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -42,7 +42,7 @@ static inline u8* GetCommandBuffer(u32 thread_id) { return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); } -static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { +FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { DEBUG_ASSERT_MSG(screen_index < 2, "Invalid screen index"); // For each thread there are two FrameBufferUpdate fields @@ -205,7 +205,7 @@ static void ReadHWRegs(Service::Interface* self) { } } -static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { +void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { u32 base_address = 0x400000; PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index d9e9a1a608..9fcf6f06f4 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -173,4 +173,14 @@ public: */ void SignalInterrupt(InterruptId interrupt_id); +void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); + +/** + * Retrieves the framebuffer info stored in the GSP shared memory for the + * specified screen index and thread id. + * @param thread_id GSP thread id of the process that accesses the structure that we are requesting. + * @param screen_index Index of the screen we are requesting (Top = 0, Bottom = 1). + * @returns FramebufferUpdate Information about the specified framebuffer. + */ +FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); } // namespace From 725d5eea7879fa152c51f15fd76003d3c6bc44ed Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 27 May 2015 15:21:06 -0500 Subject: [PATCH 3/3] Applets: Reworked how the Applet update event is handled. Applets are now cleaned up in AppletUpdateEvent after calling their respective Update method. --- src/core/hle/applets/applet.cpp | 36 +++++++++++++++++++++++--------- src/core/hle/applets/applet.h | 21 ++++++++++++------- src/core/hle/applets/swkbd.cpp | 13 ++++++------ src/core/hle/applets/swkbd.h | 18 +++++++++------- src/core/hle/service/apt/apt.cpp | 4 ++-- src/core/hle/service/apt/apt.h | 2 +- src/core/hle/service/gsp_gpu.h | 2 +- 7 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 689f5adc8b..4dcce729a1 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -31,9 +31,8 @@ namespace Applets { static std::unordered_map> applets; static u32 applet_update_event = -1; ///< The CoreTiming event identifier for the Applet update callback. -/// The interval at which the Applet update callback will be called. -static const u64 applet_update_interval_microseconds = 16666; -std::shared_ptr g_current_applet = nullptr; ///< The applet that is currently executing +/// The interval at which the Applet update callback will be called, 16.6ms +static const u64 applet_update_interval_us = 16666; ResultCode Applet::Create(Service::APT::AppletId id) { switch (id) { @@ -57,21 +56,38 @@ std::shared_ptr Applet::Get(Service::APT::AppletId id) { } /// Handles updating the current Applet every time it's called. -static void AppletUpdateEvent(u64, int cycles_late) { - if (g_current_applet && g_current_applet->IsRunning()) - g_current_applet->Update(); +static void AppletUpdateEvent(u64 applet_id, int cycles_late) { + Service::APT::AppletId id = static_cast(applet_id); + std::shared_ptr applet = Applet::Get(id); + ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", id); - CoreTiming::ScheduleEvent(usToCycles(applet_update_interval) - cycles_late, - applet_update_event); + applet->Update(); + + // If the applet is still running after the last update, reschedule the event + if (applet->IsRunning()) { + CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late, + applet_update_event, applet_id); + } else { + // Otherwise the applet has terminated, in which case we should clean it up + applets[id] = nullptr; + } +} + +ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter) { + ResultCode result = StartImpl(parameter); + if (result.IsError()) + return result; + // Schedule the update event + CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event, static_cast(id)); + return result; } void Init() { + // Register the applet update callback applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent); - CoreTiming::ScheduleEvent(usToCycles(applet_update_interval), applet_update_event); } void Shutdown() { - CoreTiming::UnscheduleEvent(applet_update_event, 0); } } diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h index f50f7d6047..fe537e70d6 100644 --- a/src/core/hle/applets/applet.h +++ b/src/core/hle/applets/applet.h @@ -12,10 +12,10 @@ namespace HLE { namespace Applets { -class Applet : public std::enable_shared_from_this { +class Applet { public: - virtual ~Applet() {}; - Applet(Service::APT::AppletId id) : id(id) {}; + virtual ~Applet() { } + Applet(Service::APT::AppletId id) : id(id) { } /** * Creates an instance of the Applet subclass identified by the parameter. @@ -37,25 +37,33 @@ public: * @param parameter Parameter data to handle. * @returns ResultCode Whether the operation was successful or not. */ - virtual ResultCode ReceiveParameter(Service::APT::MessageParameter const& parameter) = 0; + virtual ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) = 0; /** * Handles the Applet start event, triggered from the application. * @param parameter Parameter data to handle. * @returns ResultCode Whether the operation was successful or not. */ - virtual ResultCode Start(Service::APT::AppletStartupParameter const& parameter) = 0; + ResultCode Start(const Service::APT::AppletStartupParameter& parameter); /** * Whether the applet is currently executing instead of the host application or not. */ - virtual bool IsRunning() = 0; + virtual bool IsRunning() const = 0; /** * Handles an update tick for the Applet, lets it update the screen, send commands, etc. */ virtual void Update() = 0; +protected: + /** + * Handles the Applet start event, triggered from the application. + * @param parameter Parameter data to handle. + * @returns ResultCode Whether the operation was successful or not. + */ + virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0; + Service::APT::AppletId id; ///< Id of this Applet }; @@ -65,6 +73,5 @@ void Init(); /// Shuts down the HLE applets void Shutdown(); -extern std::shared_ptr g_current_applet; ///< Applet that is currently executing } } // namespace diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index b800e0eb43..7431ebcf82 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "core/hle/applets/swkbd.h" #include "core/hle/service/hid/hid.h" @@ -33,7 +34,7 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con Service::APT::MessageParameter result; // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo result.signal = static_cast(Service::APT::SignalType::LibAppFinished); - result.data = nullptr; + result.data = nullptr; result.buffer_size = 0; result.destination_id = static_cast(Service::APT::AppletId::Application); result.sender_id = static_cast(id); @@ -43,7 +44,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con return RESULT_SUCCESS; } -ResultCode SoftwareKeyboard::Start(Service::APT::AppletStartupParameter const& parameter) { +ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) { + ASSERT_MSG(parameter.buffer_size == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong"); + memcpy(&config, parameter.data, parameter.buffer_size); text_memory = boost::static_pointer_cast(parameter.object); @@ -52,9 +55,7 @@ ResultCode SoftwareKeyboard::Start(Service::APT::AppletStartupParameter const& p DrawScreenKeyboard(); - // Update the current applet so we can get update events started = true; - g_current_applet = shared_from_this(); return RESULT_SUCCESS; } @@ -72,7 +73,7 @@ void SoftwareKeyboard::Update() { config.text_length = 6; config.text_offset = 0; - // TODO(Subv): We're finalizing the applet immediately after it's started, + // TODO(Subv): We're finalizing the applet immediately after it's started, // but we should defer this call until after all the input has been collected. Finalize(); } @@ -98,8 +99,6 @@ void SoftwareKeyboard::Finalize() { Service::APT::SendParameter(message); started = false; - // Unset the current applet, we are not running anymore - g_current_applet = nullptr; } } diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h index 5970390c65..98e81c48a9 100644 --- a/src/core/hle/applets/swkbd.h +++ b/src/core/hle/applets/swkbd.h @@ -42,17 +42,21 @@ struct SoftwareKeyboardConfig { INSERT_PADDING_BYTES(0x2B6); }; +/** + * The size of this structure (0x400) has been verified via reverse engineering of multiple games + * that use the software keyboard. + */ static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config size is wrong"); -class SoftwareKeyboard : public Applet { +class SoftwareKeyboard final : public Applet { public: SoftwareKeyboard(Service::APT::AppletId id); ~SoftwareKeyboard() {} - ResultCode ReceiveParameter(Service::APT::MessageParameter const& parameter) override; - ResultCode Start(Service::APT::AppletStartupParameter const& parameter) override; + ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; + ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; void Update() override; - bool IsRunning() override { return started; } + bool IsRunning() const override { return started; } /** * Draws a keyboard to the current bottom screen framebuffer. @@ -65,13 +69,13 @@ public: */ void Finalize(); - /// TODO(Subv): Find out what this is actually used for. - // It is believed that the application stores the current screen image here. + /// TODO(Subv): Find out what this is actually used for. + /// It is believed that the application stores the current screen image here. Kernel::SharedPtr framebuffer_memory; /// SharedMemory where the output text will be stored Kernel::SharedPtr text_memory; - + /// Configuration of this instance of the SoftwareKeyboard, as received from the application SoftwareKeyboardConfig config; diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 783fad7ca1..b364beed92 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -44,7 +44,7 @@ static u32 cpu_percent; ///< CPU time available to the running application /// Parameter data to be returned in the next call to Glance/ReceiveParameter static MessageParameter next_parameter; -void SendParameter(MessageParameter const& parameter) { +void SendParameter(const MessageParameter& parameter) { next_parameter = parameter; // Signal the event to let the application know that a new parameter is ready to be read parameter_event->Signal(); @@ -338,7 +338,7 @@ void StartLibraryApplet(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); AppletId applet_id = static_cast(cmd_buff[1]); std::shared_ptr applet = HLE::Applets::Applet::Get(applet_id); - + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); if (applet == nullptr) { diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 510193cc80..9f08025088 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -63,7 +63,7 @@ enum class AppletId : u32 { }; /// Send a parameter to the currently-running application, which will read it via ReceiveParameter -void SendParameter(MessageParameter const& parameter); +void SendParameter(const MessageParameter& parameter); /** * APT::Initialize service function diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 9fcf6f06f4..268089fdd3 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -176,7 +176,7 @@ void SignalInterrupt(InterruptId interrupt_id); void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); /** - * Retrieves the framebuffer info stored in the GSP shared memory for the + * Retrieves the framebuffer info stored in the GSP shared memory for the * specified screen index and thread id. * @param thread_id GSP thread id of the process that accesses the structure that we are requesting. * @param screen_index Index of the screen we are requesting (Top = 0, Bottom = 1).