From 18664c719edf9e432e98f667742295917e53ccd3 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Wed, 20 Jun 2018 12:01:54 +0800 Subject: [PATCH] frontend/applets: misc fixes * Renamed applet to applets * Added log classes Applet and Applet.SWKBD * Fixes to get it compile --- src/common/logging/backend.cpp | 2 + src/common/logging/log.h | 3 + src/core/CMakeLists.txt | 8 +- .../{applet => applets}/interface.cpp | 6 +- .../frontend/{applet => applets}/interface.h | 19 +++-- .../frontend/{applet => applets}/swkbd.cpp | 28 ++++--- src/core/frontend/{applet => applets}/swkbd.h | 41 +++++---- src/core/hle/applets/swkbd.cpp | 84 +++++++++++++++++-- src/core/hle/applets/swkbd.h | 6 +- 9 files changed, 147 insertions(+), 50 deletions(-) rename src/core/frontend/{applet => applets}/interface.cpp (75%) rename src/core/frontend/{applet => applets}/interface.h (80%) rename src/core/frontend/{applet => applets}/swkbd.cpp (81%) rename src/core/frontend/{applet => applets}/swkbd.h (79%) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 6f831b359e..0166beff0f 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -158,6 +158,8 @@ void FileBackend::Write(const Entry& entry) { SUB(Debug, GDBStub) \ CLS(Kernel) \ SUB(Kernel, SVC) \ + CLS(Applet) \ + SUB(Applet, SWKBD) \ CLS(Service) \ SUB(Service, SRV) \ SUB(Service, FRD) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 31ba5beae8..a51399dee1 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -48,6 +48,9 @@ enum class Class : ClassType { Debug_GDBStub, ///< GDB Stub Kernel, ///< The HLE implementation of the CTR kernel Kernel_SVC, ///< Kernel system calls + Applet, ///< HLE implementation of system applets. Each applet + /// should have its own subclass. + Applet_SWKBD, ///< The Software Keyboard applet Service, ///< HLE implementation of system services. Each major service /// should have its own subclass. Service_SRV, ///< The SRV (Service Directory) implementation diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0505a928fd..8e291294e3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -68,10 +68,10 @@ add_library(core STATIC file_sys/savedata_archive.h file_sys/title_metadata.cpp file_sys/title_metadata.h - frontend/applet/interface.cpp - frontend/applet/interface.h - frontend/applet/swkbd.cpp - frontend/applet/swkbd.h + frontend/applets/interface.cpp + frontend/applets/interface.h + frontend/applets/swkbd.cpp + frontend/applets/swkbd.h frontend/camera/blank_camera.cpp frontend/camera/blank_camera.h frontend/camera/factory.cpp diff --git a/src/core/frontend/applet/interface.cpp b/src/core/frontend/applets/interface.cpp similarity index 75% rename from src/core/frontend/applet/interface.cpp rename to src/core/frontend/applets/interface.cpp index 368f180cae..d24f8a9a15 100644 --- a/src/core/frontend/applet/interface.cpp +++ b/src/core/frontend/applets/interface.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include -#include "core/frontend/interface.h" +#include "core/frontend/applets/interface.h" namespace Frontend { @@ -17,4 +17,8 @@ void UnregisterFrontendApplet(AppletType type) { registered_applets.erase(type); } +std::shared_ptr GetRegisteredApplet(AppletType type) { + return registered_applets.at(type); +} + } // namespace Frontend diff --git a/src/core/frontend/applet/interface.h b/src/core/frontend/applets/interface.h similarity index 80% rename from src/core/frontend/applet/interface.h rename to src/core/frontend/applets/interface.h index e419df8d24..3e5c0eb356 100644 --- a/src/core/frontend/applet/interface.h +++ b/src/core/frontend/applets/interface.h @@ -16,6 +16,7 @@ enum class AppletType { class AppletConfig {}; class AppletData {}; +// TODO(jroweboy) add ability to draw to framebuffer class AppletInterface { public: virtual ~AppletInterface() = default; @@ -24,12 +25,7 @@ public: * On applet start, the applet specific configuration will be passed in along with the * framebuffer. */ - // virtual void Setup(const Config* /*, framebuffer */) = 0; - - /** - * Called on a fixed schedule to have the applet update any state such as the framebuffer. - */ - virtual void Update() = 0; + virtual void Setup(const AppletConfig*) = 0; /** * Checked every update to see if the applet is still running. When the applet is done, the core @@ -39,9 +35,14 @@ public: return running; } -private: - // framebuffer; - std::atomic_bool running = false; + /** + * Called by the core to receive the result data of this applet. + * Frontend implementation **should** block until the data is ready. + */ + virtual const AppletData* ReceiveData() = 0; + +protected: + std::atomic running = false; }; /** diff --git a/src/core/frontend/applet/swkbd.cpp b/src/core/frontend/applets/swkbd.cpp similarity index 81% rename from src/core/frontend/applet/swkbd.cpp rename to src/core/frontend/applets/swkbd.cpp index b7a0517dd8..fac36077db 100644 --- a/src/core/frontend/applet/swkbd.cpp +++ b/src/core/frontend/applets/swkbd.cpp @@ -2,13 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/frontend/applet/swkbd.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/frontend/applets/swkbd.h" namespace Frontend { ValidationError SoftwareKeyboard::ValidateFilters(const std::string& input) { if (config.filters.prevent_digit) { - if (std::any_of(input.begin(), input.end(), std::isdigit)) { + if (std::any_of(input.begin(), input.end(), + [](unsigned char c) { return std::isdigit(c); })) { return ValidationError::DigitNotAllowed; } } @@ -22,7 +25,7 @@ ValidationError SoftwareKeyboard::ValidateFilters(const std::string& input) { return ValidationError::PercentNotAllowed; } } - if (config.filter.prevent_backslash) { + if (config.filters.prevent_backslash) { if (input.find('\\') != std::string::npos) { return ValidationError::BackslashNotAllowed; } @@ -35,7 +38,7 @@ ValidationError SoftwareKeyboard::ValidateFilters(const std::string& input) { // TODO: check the callback LOG_INFO(Frontend, "App requested a swkbd callback, but its not implemented."); } - return valid; + return ValidationError::None; } ValidationError SoftwareKeyboard::ValidateInput(const std::string& input) { @@ -49,9 +52,12 @@ ValidationError SoftwareKeyboard::ValidateInput(const std::string& input) { return ValidationError::MaxLengthExceeded; } - auto is_blank = [&] { return std::all_of(input.begin(), input.end(), std::isspace); }; + auto is_blank = [&] { + return std::all_of(input.begin(), input.end(), + [](unsigned char c) { return std::isspace(c); }); + }; auto is_empty = [&] { return input.empty(); }; - switch (config.valid_input) { + switch (config.accept_mode) { case AcceptedInput::FixedLength: if (input.size() != config.max_text_length) { return ValidationError::FixedLengthRequired; @@ -80,12 +86,12 @@ ValidationError SoftwareKeyboard::ValidateInput(const std::string& input) { default: // TODO(jroweboy): What does hardware do in this case? NGLOG_CRITICAL(Frontend, "Application requested unknown validation method. Method: {}", - static_cast(config.valid_input)); + static_cast(config.accept_mode)); UNREACHABLE(); } return ValidationError::None; -} // namespace Frontend +} ValidationError SoftwareKeyboard::ValidateButton(u8 button) { switch (config.button_config) { @@ -112,12 +118,12 @@ ValidationError SoftwareKeyboard::ValidateButton(u8 button) { return ValidationError::None; } -ValidationError Finalize(cosnt std::string& text, u8 button) { +ValidationError SoftwareKeyboard::Finalize(const std::string& text, u8 button) { ValidationError error; - if ((error = ValidateInput(text)) != ValidationError::NONE) { + if ((error = ValidateInput(text)) != ValidationError::None) { return error; } - if ((error = ValidateButton(button)) != ValidationError::NONE) { + if ((error = ValidateButton(button)) != ValidationError::None) { return error; } data = {text, button}; diff --git a/src/core/frontend/applet/swkbd.h b/src/core/frontend/applets/swkbd.h similarity index 79% rename from src/core/frontend/applet/swkbd.h rename to src/core/frontend/applets/swkbd.h index 9b5237422b..aef2d7c0c1 100644 --- a/src/core/frontend/applet/swkbd.h +++ b/src/core/frontend/applets/swkbd.h @@ -4,8 +4,12 @@ #pragma once +#include +#include #include -#include "core/frontend/applet/interface.h" +#include +#include +#include "core/frontend/applets/interface.h" namespace Frontend { @@ -38,8 +42,8 @@ static const std::unordered_map> DEFAULT_ }; /// Configuration thats relevent to frontend implementation of applets. Anything missing that we -/// later learn is needed can be added here and filled in by the backed HLE applet -struct KeyboardConfig { +/// later learn is needed can be added here and filled in by the backend HLE applet +struct KeyboardConfig : public AppletConfig { ButtonConfig button_config; AcceptedInput accept_mode; /// What kinds of input are accepted (blank/empty/fixed width) bool multiline_mode; /// True if the keyboard accepts multiple lines of input @@ -49,19 +53,23 @@ struct KeyboardConfig { bool has_custom_button_text; /// If true, use the button_text instead std::vector button_text; /// Contains the button text that the caller provides struct Filters { - bool prevent_digit; /// Disallow the use of more than a certain number of digits (TODO how - /// many is a certain number) - bool prevent_at; /// Disallow the use of the @ sign. - bool prevent_percent; /// Disallow the use of the % sign. + bool prevent_digit; /// Disallow the use of more than a certain number of digits + /// TODO: how many is a certain number + bool prevent_at; /// Disallow the use of the @ sign. + bool prevent_percent; /// Disallow the use of the % sign. bool prevent_backslash; /// Disallow the use of the \ sign. bool prevent_profanity; /// Disallow profanity using Nintendo's profanity filter. bool enable_callback; /// Use a callback in order to check the input. } filters; }; -struct KeyboardData { +class KeyboardData : public AppletData { +public: std::string text; - u8 button; + u8 button{}; + + KeyboardData(std::string text, u8 button) : text(std::move(text)), button(button) {} + KeyboardData() = default; }; enum class ValidationError { @@ -77,13 +85,20 @@ enum class ValidationError { CallbackFailed, // Allowed Input Type FixedLengthRequired, + MaxLengthExceeded, BlankInputNotAllowed, EmptyInputNotAllowed, }; class SoftwareKeyboard : public AppletInterface { public: - explict SoftwareKeyboard(KeyboardConfig config) : AppletInterface(), config(config) {} + explicit SoftwareKeyboard() : AppletInterface() {} + const AppletData* ReceiveData() override { + return &data; + } + void Setup(const AppletConfig* config) override { + this->config = KeyboardConfig(*static_cast(config)); + } protected: /** @@ -109,13 +124,9 @@ protected: * Runs all validation phases. If successful, stores the data so that the HLE applet in core can * send this to the calling application */ - ValidationError Finialize(const std::string&, u8 button); + ValidationError Finalize(const std::string& text, u8 button); private: - KeyboardData ReceiveData() override { - return data; - } - KeyboardConfig config; KeyboardData data; }; diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index 6b181de06c..af782bde2d 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -69,22 +69,51 @@ ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter cons DrawScreenKeyboard(); + using namespace Frontend; + frontend_applet = GetRegisteredApplet(AppletType::SoftwareKeyboard); + if (frontend_applet) { + KeyboardConfig frontend_config = ToFrontendConfig(config); + frontend_applet->Setup(&frontend_config); + } + is_running = true; return RESULT_SUCCESS; } void SoftwareKeyboard::Update() { - // TODO(Subv): Handle input using the touch events from the HID module - - // TODO(Subv): Remove this hardcoded text - std::u16string text = Common::UTF8ToUTF16("Citra"); + using namespace Frontend; + KeyboardData data(*static_cast(frontend_applet->ReceiveData())); + std::u16string text = Common::UTF8ToUTF16(data.text); memcpy(text_memory->GetPointer(), text.c_str(), text.length() * sizeof(char16_t)); + switch (config.num_buttons_m1) { + case SoftwareKeyboardButtonConfig::SINGLE_BUTTON: + config.return_code = SoftwareKeyboardResult::D0_CLICK; + break; + case SoftwareKeyboardButtonConfig::DUAL_BUTTON: + if (data.button == 0) + config.return_code = SoftwareKeyboardResult::D1_CLICK0; + else + config.return_code = SoftwareKeyboardResult::D1_CLICK1; + break; + case SoftwareKeyboardButtonConfig::TRIPLE_BUTTON: + if (data.button == 0) + config.return_code = SoftwareKeyboardResult::D2_CLICK0; + else if (data.button == 1) + config.return_code = SoftwareKeyboardResult::D2_CLICK1; + else + config.return_code = SoftwareKeyboardResult::D2_CLICK2; + break; + case SoftwareKeyboardButtonConfig::NO_BUTTON: + // TODO: find out what is actually returned + config.return_code = SoftwareKeyboardResult::NONE; + break; + default: + NGLOG_CRITICAL(Applet_SWKBD, "Unknown button config {}", + static_cast(config.num_buttons_m1)); + UNREACHABLE(); + } - // 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 = 6; + config.text_length = static_cast(text.size() + 1); config.text_offset = 0; // TODO(Subv): We're finalizing the applet immediately after it's started, @@ -114,5 +143,42 @@ void SoftwareKeyboard::Finalize() { is_running = false; } + +Frontend::KeyboardConfig SoftwareKeyboard::ToFrontendConfig(SoftwareKeyboardConfig config) { + using namespace Frontend; + KeyboardConfig frontend_config; + frontend_config.button_config = static_cast(config.num_buttons_m1); + frontend_config.accept_mode = static_cast(config.valid_input); + frontend_config.multiline_mode = config.multiline; + frontend_config.max_text_length = config.max_text_length; + frontend_config.max_digits = config.max_digits; + std::wstring_convert, char16_t> convert; + frontend_config.hint_text = + convert.to_bytes(reinterpret_cast(config.hint_text.data())); + frontend_config.has_custom_button_text = + std::all_of(config.button_text.begin(), config.button_text.end(), + [](std::array x) { + return std::all_of(x.begin(), x.end(), [](u16 x) { return x == 0; }); + }); + if (frontend_config.has_custom_button_text) { + for (const auto& text : config.button_text) { + frontend_config.button_text.push_back( + convert.to_bytes(reinterpret_cast(text.data()))); + } + } + frontend_config.filters.prevent_digit = + static_cast(config.filter_flags & SoftwareKeyboardFilter::DIGITS); + frontend_config.filters.prevent_at = + static_cast(config.filter_flags & SoftwareKeyboardFilter::AT); + frontend_config.filters.prevent_percent = + static_cast(config.filter_flags & SoftwareKeyboardFilter::PERCENT); + frontend_config.filters.prevent_backslash = + static_cast(config.filter_flags & SoftwareKeyboardFilter::BACKSLASH); + frontend_config.filters.prevent_profanity = + static_cast(config.filter_flags & SoftwareKeyboardFilter::PROFANITY); + frontend_config.filters.enable_callback = + static_cast(config.filter_flags & SoftwareKeyboardFilter::CALLBACK); + return frontend_config; +} } // namespace Applets } // namespace HLE diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h index 0324abf27a..4ba0e36b46 100644 --- a/src/core/hle/applets/swkbd.h +++ b/src/core/hle/applets/swkbd.h @@ -6,7 +6,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" -#include "core/frontend/applet/swkbd.h" +#include "core/frontend/applets/swkbd.h" #include "core/hle/applets/applet.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" @@ -197,6 +197,8 @@ public: void Finalize(); private: + Frontend::KeyboardConfig ToFrontendConfig(SoftwareKeyboardConfig config); + /// This SharedMemory will be created when we receive the LibAppJustStarted message. /// It holds the framebuffer info retrieved by the application with /// GSPGPU::ImportDisplayCaptureInfo @@ -207,6 +209,8 @@ private: /// Configuration of this instance of the SoftwareKeyboard, as received from the application SoftwareKeyboardConfig config; + + std::shared_ptr frontend_applet; }; } // namespace Applets } // namespace HLE