diff options
| author | 2015-07-11 23:56:59 -0400 | |
|---|---|---|
| committer | 2015-07-11 23:56:59 -0400 | |
| commit | f4e1d8ea36fc3f8309234b8a4a8c9f659b171c80 (patch) | |
| tree | 8756cd27489dcb424103e443836b6ea8dba3fccd /src/core/hle/applets | |
| parent | Merge pull request #912 from yuriks/process-loading (diff) | |
| parent | Applets: Reworked how the Applet update event is handled. (diff) | |
| download | yuzu-f4e1d8ea36fc3f8309234b8a4a8c9f659b171c80.tar.gz yuzu-f4e1d8ea36fc3f8309234b8a4a8c9f659b171c80.tar.xz yuzu-f4e1d8ea36fc3f8309234b8a4a8c9f659b171c80.zip | |
Merge pull request #823 from Subv/applets_drawing
Library applet support (swkbd for now)
Diffstat (limited to 'src/core/hle/applets')
| -rw-r--r-- | src/core/hle/applets/applet.cpp | 94 | ||||
| -rw-r--r-- | src/core/hle/applets/applet.h | 77 | ||||
| -rw-r--r-- | src/core/hle/applets/swkbd.cpp | 105 | ||||
| -rw-r--r-- | src/core/hle/applets/swkbd.h | 87 |
4 files changed, 363 insertions, 0 deletions
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp new file mode 100644 index 000000000..4dcce729a --- /dev/null +++ b/src/core/hle/applets/applet.cpp | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | |||
| 8 | #include "core/core_timing.h" | ||
| 9 | #include "core/hle/applets/applet.h" | ||
| 10 | #include "core/hle/applets/swkbd.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | |||
| 14 | // Specializes std::hash for AppletId, so that we can use it in std::unordered_map. | ||
| 15 | // Workaround for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 | ||
| 16 | namespace std { | ||
| 17 | template <> | ||
| 18 | struct hash<Service::APT::AppletId> { | ||
| 19 | typedef Service::APT::AppletId argument_type; | ||
| 20 | typedef std::size_t result_type; | ||
| 21 | |||
| 22 | result_type operator()(const argument_type& id_code) const { | ||
| 23 | typedef std::underlying_type<argument_type>::type Type; | ||
| 24 | return std::hash<Type>()(static_cast<Type>(id_code)); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | namespace HLE { | ||
| 30 | namespace Applets { | ||
| 31 | |||
| 32 | static std::unordered_map<Service::APT::AppletId, std::shared_ptr<Applet>> applets; | ||
| 33 | static u32 applet_update_event = -1; ///< The CoreTiming event identifier for the Applet update callback. | ||
| 34 | /// The interval at which the Applet update callback will be called, 16.6ms | ||
| 35 | static const u64 applet_update_interval_us = 16666; | ||
| 36 | |||
| 37 | ResultCode Applet::Create(Service::APT::AppletId id) { | ||
| 38 | switch (id) { | ||
| 39 | case Service::APT::AppletId::SoftwareKeyboard1: | ||
| 40 | case Service::APT::AppletId::SoftwareKeyboard2: | ||
| 41 | applets[id] = std::make_shared<SoftwareKeyboard>(id); | ||
| 42 | break; | ||
| 43 | default: | ||
| 44 | // TODO(Subv): Find the right error code | ||
| 45 | return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||
| 46 | } | ||
| 47 | |||
| 48 | return RESULT_SUCCESS; | ||
| 49 | } | ||
| 50 | |||
| 51 | std::shared_ptr<Applet> Applet::Get(Service::APT::AppletId id) { | ||
| 52 | auto itr = applets.find(id); | ||
| 53 | if (itr != applets.end()) | ||
| 54 | return itr->second; | ||
| 55 | return nullptr; | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Handles updating the current Applet every time it's called. | ||
| 59 | static void AppletUpdateEvent(u64 applet_id, int cycles_late) { | ||
| 60 | Service::APT::AppletId id = static_cast<Service::APT::AppletId>(applet_id); | ||
| 61 | std::shared_ptr<Applet> applet = Applet::Get(id); | ||
| 62 | ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", id); | ||
| 63 | |||
| 64 | applet->Update(); | ||
| 65 | |||
| 66 | // If the applet is still running after the last update, reschedule the event | ||
| 67 | if (applet->IsRunning()) { | ||
| 68 | CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late, | ||
| 69 | applet_update_event, applet_id); | ||
| 70 | } else { | ||
| 71 | // Otherwise the applet has terminated, in which case we should clean it up | ||
| 72 | applets[id] = nullptr; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter) { | ||
| 77 | ResultCode result = StartImpl(parameter); | ||
| 78 | if (result.IsError()) | ||
| 79 | return result; | ||
| 80 | // Schedule the update event | ||
| 81 | CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event, static_cast<u64>(id)); | ||
| 82 | return result; | ||
| 83 | } | ||
| 84 | |||
| 85 | void Init() { | ||
| 86 | // Register the applet update callback | ||
| 87 | applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent); | ||
| 88 | } | ||
| 89 | |||
| 90 | void Shutdown() { | ||
| 91 | } | ||
| 92 | |||
| 93 | } | ||
| 94 | } // namespace | ||
diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h new file mode 100644 index 000000000..fe537e70d --- /dev/null +++ b/src/core/hle/applets/applet.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | // Copyright 2015 Citra 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 "common/common_types.h" | ||
| 8 | #include "core/hle/kernel/kernel.h" | ||
| 9 | #include "core/hle/kernel/shared_memory.h" | ||
| 10 | #include "core/hle/service/apt/apt.h" | ||
| 11 | |||
| 12 | namespace HLE { | ||
| 13 | namespace Applets { | ||
| 14 | |||
| 15 | class Applet { | ||
| 16 | public: | ||
| 17 | virtual ~Applet() { } | ||
| 18 | Applet(Service::APT::AppletId id) : id(id) { } | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Creates an instance of the Applet subclass identified by the parameter. | ||
| 22 | * and stores it in a global map. | ||
| 23 | * @param id Id of the applet to create. | ||
| 24 | * @returns ResultCode Whether the operation was successful or not. | ||
| 25 | */ | ||
| 26 | static ResultCode Create(Service::APT::AppletId id); | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Retrieves the Applet instance identified by the specified id. | ||
| 30 | * @param id Id of the Applet to retrieve. | ||
| 31 | * @returns Requested Applet or nullptr if not found. | ||
| 32 | */ | ||
| 33 | static std::shared_ptr<Applet> Get(Service::APT::AppletId id); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Handles a parameter from the application. | ||
| 37 | * @param parameter Parameter data to handle. | ||
| 38 | * @returns ResultCode Whether the operation was successful or not. | ||
| 39 | */ | ||
| 40 | virtual ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) = 0; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Handles the Applet start event, triggered from the application. | ||
| 44 | * @param parameter Parameter data to handle. | ||
| 45 | * @returns ResultCode Whether the operation was successful or not. | ||
| 46 | */ | ||
| 47 | ResultCode Start(const Service::APT::AppletStartupParameter& parameter); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Whether the applet is currently executing instead of the host application or not. | ||
| 51 | */ | ||
| 52 | virtual bool IsRunning() const = 0; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Handles an update tick for the Applet, lets it update the screen, send commands, etc. | ||
| 56 | */ | ||
| 57 | virtual void Update() = 0; | ||
| 58 | |||
| 59 | protected: | ||
| 60 | /** | ||
| 61 | * Handles the Applet start event, triggered from the application. | ||
| 62 | * @param parameter Parameter data to handle. | ||
| 63 | * @returns ResultCode Whether the operation was successful or not. | ||
| 64 | */ | ||
| 65 | virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0; | ||
| 66 | |||
| 67 | Service::APT::AppletId id; ///< Id of this Applet | ||
| 68 | }; | ||
| 69 | |||
| 70 | /// Initializes the HLE applets | ||
| 71 | void Init(); | ||
| 72 | |||
| 73 | /// Shuts down the HLE applets | ||
| 74 | void Shutdown(); | ||
| 75 | |||
| 76 | } | ||
| 77 | } // namespace | ||
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp new file mode 100644 index 000000000..7431ebcf8 --- /dev/null +++ b/src/core/hle/applets/swkbd.cpp | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "common/string_util.h" | ||
| 8 | |||
| 9 | #include "core/hle/applets/swkbd.h" | ||
| 10 | #include "core/hle/service/hid/hid.h" | ||
| 11 | #include "core/hle/service/gsp_gpu.h" | ||
| 12 | #include "video_core/video_core.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | |||
| 16 | namespace HLE { | ||
| 17 | namespace Applets { | ||
| 18 | |||
| 19 | SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) { | ||
| 20 | // Create the SharedMemory that will hold the framebuffer data | ||
| 21 | // TODO(Subv): What size should we use here? | ||
| 22 | using Kernel::MemoryPermission; | ||
| 23 | framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); | ||
| 24 | } | ||
| 25 | |||
| 26 | ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { | ||
| 27 | if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { | ||
| 28 | LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); | ||
| 29 | UNIMPLEMENTED(); | ||
| 30 | // TODO(Subv): Find the right error code | ||
| 31 | return ResultCode(-1); | ||
| 32 | } | ||
| 33 | |||
| 34 | Service::APT::MessageParameter result; | ||
| 35 | // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo | ||
| 36 | result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); | ||
| 37 | result.data = nullptr; | ||
| 38 | result.buffer_size = 0; | ||
| 39 | result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | ||
| 40 | result.sender_id = static_cast<u32>(id); | ||
| 41 | result.object = framebuffer_memory; | ||
| 42 | |||
| 43 | Service::APT::SendParameter(result); | ||
| 44 | return RESULT_SUCCESS; | ||
| 45 | } | ||
| 46 | |||
| 47 | ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) { | ||
| 48 | ASSERT_MSG(parameter.buffer_size == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong"); | ||
| 49 | |||
| 50 | memcpy(&config, parameter.data, parameter.buffer_size); | ||
| 51 | text_memory = boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object); | ||
| 52 | |||
| 53 | // TODO(Subv): Verify if this is the correct behavior | ||
| 54 | memset(text_memory->GetPointer(), 0, text_memory->size); | ||
| 55 | |||
| 56 | DrawScreenKeyboard(); | ||
| 57 | |||
| 58 | started = true; | ||
| 59 | return RESULT_SUCCESS; | ||
| 60 | } | ||
| 61 | |||
| 62 | void SoftwareKeyboard::Update() { | ||
| 63 | // TODO(Subv): Handle input using the touch events from the HID module | ||
| 64 | |||
| 65 | // TODO(Subv): Remove this hardcoded text | ||
| 66 | std::u16string text = Common::UTF8ToUTF16("Citra"); | ||
| 67 | memcpy(text_memory->GetPointer(), text.c_str(), text.length() * sizeof(char16_t)); | ||
| 68 | |||
| 69 | // TODO(Subv): Ask for input and write it to the shared memory | ||
| 70 | // TODO(Subv): Find out what are the possible values for the return code, | ||
| 71 | // some games seem to check for a hardcoded 2 | ||
| 72 | config.return_code = 2; | ||
| 73 | config.text_length = 6; | ||
| 74 | config.text_offset = 0; | ||
| 75 | |||
| 76 | // TODO(Subv): We're finalizing the applet immediately after it's started, | ||
| 77 | // but we should defer this call until after all the input has been collected. | ||
| 78 | Finalize(); | ||
| 79 | } | ||
| 80 | |||
| 81 | void SoftwareKeyboard::DrawScreenKeyboard() { | ||
| 82 | auto bottom_screen = GSP_GPU::GetFrameBufferInfo(0, 1); | ||
| 83 | auto info = bottom_screen->framebuffer_info[bottom_screen->index]; | ||
| 84 | |||
| 85 | // TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer | ||
| 86 | memset(Memory::GetPointer(info.address_left), 0, info.stride * 320); | ||
| 87 | |||
| 88 | GSP_GPU::SetBufferSwap(1, info); | ||
| 89 | } | ||
| 90 | |||
| 91 | void SoftwareKeyboard::Finalize() { | ||
| 92 | // Let the application know that we're closing | ||
| 93 | Service::APT::MessageParameter message; | ||
| 94 | message.buffer_size = sizeof(SoftwareKeyboardConfig); | ||
| 95 | message.data = reinterpret_cast<u8*>(&config); | ||
| 96 | message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); | ||
| 97 | message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | ||
| 98 | message.sender_id = static_cast<u32>(id); | ||
| 99 | Service::APT::SendParameter(message); | ||
| 100 | |||
| 101 | started = false; | ||
| 102 | } | ||
| 103 | |||
| 104 | } | ||
| 105 | } // namespace | ||
diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h new file mode 100644 index 000000000..98e81c48a --- /dev/null +++ b/src/core/hle/applets/swkbd.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2015 Citra 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 "common/common_types.h" | ||
| 8 | #include "core/hle/applets/applet.h" | ||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | #include "core/hle/kernel/shared_memory.h" | ||
| 11 | #include "core/hle/service/apt/apt.h" | ||
| 12 | |||
| 13 | namespace HLE { | ||
| 14 | namespace Applets { | ||
| 15 | |||
| 16 | struct SoftwareKeyboardConfig { | ||
| 17 | INSERT_PADDING_WORDS(0x8); | ||
| 18 | |||
| 19 | u16 max_text_length; ///< Maximum length of the input text | ||
| 20 | |||
| 21 | INSERT_PADDING_BYTES(0x6E); | ||
| 22 | |||
| 23 | char16_t display_text[65]; ///< Text to display when asking the user for input | ||
| 24 | |||
| 25 | INSERT_PADDING_BYTES(0xE); | ||
| 26 | |||
| 27 | u32 default_text_offset; ///< Offset of the default text in the output SharedMemory | ||
| 28 | |||
| 29 | INSERT_PADDING_WORDS(0x3); | ||
| 30 | |||
| 31 | u32 shared_memory_size; ///< Size of the SharedMemory | ||
| 32 | |||
| 33 | INSERT_PADDING_WORDS(0x1); | ||
| 34 | |||
| 35 | u32 return_code; ///< Return code of the SoftwareKeyboard, usually 2, other values are unknown | ||
| 36 | |||
| 37 | INSERT_PADDING_WORDS(0x2); | ||
| 38 | |||
| 39 | u32 text_offset; ///< Offset in the SharedMemory where the output text starts | ||
| 40 | u16 text_length; ///< Length in characters of the output text | ||
| 41 | |||
| 42 | INSERT_PADDING_BYTES(0x2B6); | ||
| 43 | }; | ||
| 44 | |||
| 45 | /** | ||
| 46 | * The size of this structure (0x400) has been verified via reverse engineering of multiple games | ||
| 47 | * that use the software keyboard. | ||
| 48 | */ | ||
| 49 | static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config size is wrong"); | ||
| 50 | |||
| 51 | class SoftwareKeyboard final : public Applet { | ||
| 52 | public: | ||
| 53 | SoftwareKeyboard(Service::APT::AppletId id); | ||
| 54 | ~SoftwareKeyboard() {} | ||
| 55 | |||
| 56 | ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; | ||
| 57 | ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; | ||
| 58 | void Update() override; | ||
| 59 | bool IsRunning() const override { return started; } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Draws a keyboard to the current bottom screen framebuffer. | ||
| 63 | */ | ||
| 64 | void DrawScreenKeyboard(); | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Sends the LibAppletClosing signal to the application, | ||
| 68 | * along with the relevant data buffers. | ||
| 69 | */ | ||
| 70 | void Finalize(); | ||
| 71 | |||
| 72 | /// TODO(Subv): Find out what this is actually used for. | ||
| 73 | /// It is believed that the application stores the current screen image here. | ||
| 74 | Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; | ||
| 75 | |||
| 76 | /// SharedMemory where the output text will be stored | ||
| 77 | Kernel::SharedPtr<Kernel::SharedMemory> text_memory; | ||
| 78 | |||
| 79 | /// Configuration of this instance of the SoftwareKeyboard, as received from the application | ||
| 80 | SoftwareKeyboardConfig config; | ||
| 81 | |||
| 82 | /// Whether this applet is currently running instead of the host application or not. | ||
| 83 | bool started; | ||
| 84 | }; | ||
| 85 | |||
| 86 | } | ||
| 87 | } // namespace | ||