diff --git a/include/applets/applet.hpp b/include/applets/applet.hpp index 98fb6c20..d9ba6143 100644 --- a/include/applets/applet.hpp +++ b/include/applets/applet.hpp @@ -84,7 +84,7 @@ namespace Applets { virtual const char* name() = 0; // Called by APT::StartLibraryApplet and similar - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) = 0; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) = 0; // Transfer parameters from application -> applet virtual Result::HorizonResult receiveParameter(const Parameter& parameter) = 0; virtual void reset() = 0; diff --git a/include/applets/mii_selector.hpp b/include/applets/mii_selector.hpp index 8bd757dd..3b6cf2ec 100644 --- a/include/applets/mii_selector.hpp +++ b/include/applets/mii_selector.hpp @@ -4,7 +4,7 @@ namespace Applets { class MiiSelectorApplet final : public AppletBase { public: virtual const char* name() override { return "Mii Selector"; } - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) override; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) override; virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; virtual void reset() override; diff --git a/include/applets/software_keyboard.hpp b/include/applets/software_keyboard.hpp index 785ba20a..3a167ad0 100644 --- a/include/applets/software_keyboard.hpp +++ b/include/applets/software_keyboard.hpp @@ -1,13 +1,162 @@ +#include + #include "applets/applet.hpp" +#include "swap.hpp" namespace Applets { + // Software keyboard definitions adapted from libctru/Citra + // Keyboard input filtering flags. Allows the caller to specify what input is explicitly not allowed + namespace SoftwareKeyboardFilter { + enum Filter : u32 { + Digits = 1, // Disallow the use of more than a certain number of digits (0 or more) + At = 1 << 1, // Disallow the use of the @ sign. + Percent = 1 << 2, // Disallow the use of the % sign. + Backslash = 1 << 3, // Disallow the use of the \ sign. + Profanity = 1 << 4, // Disallow profanity using Nintendo's profanity filter. + Callback = 1 << 5, // Use a callback in order to check the input. + }; + } // namespace SoftwareKeyboardFilter + + // Keyboard features. + namespace SoftwareKeyboardFeature { + enum Feature { + Parental = 1, // Parental PIN mode. + DarkenTopScreen = 1 << 1, // Darken the top screen when the keyboard is shown. + PredictiveInput = 1 << 2, // Enable predictive input (necessary for Kanji input in JPN systems). + Multiline = 1 << 3, // Enable multiline input. + FixedWidth = 1 << 4, // Enable fixed-width mode. + AllowHome = 1 << 5, // Allow the usage of the HOME button. + AllowReset = 1 << 6, // Allow the usage of a software-reset combination. + AllowPower = 1 << 7, // Allow the usage of the POWER button. + DefaultQWERTY = 1 << 9, // Default to the QWERTY page when the keyboard is shown. + }; + } // namespace SoftwareKeyboardFeature + class SoftwareKeyboardApplet final : public AppletBase { public: + static constexpr int MAX_BUTTON = 3; // Maximum number of buttons that can be in the keyboard. + static constexpr int MAX_BUTTON_TEXT_LEN = 16; // Maximum button text length, in UTF-16 code units. + static constexpr int MAX_HINT_TEXT_LEN = 64; // Maximum hint text length, in UTF-16 code units. + static constexpr int MAX_CALLBACK_MSG_LEN = 256; // Maximum filter callback error message length, in UTF-16 code units. + + // Keyboard types + enum class SoftwareKeyboardType : u32 { + Normal, // Normal keyboard with several pages (QWERTY/accents/symbol/mobile) + QWERTY, // QWERTY keyboard only. + NumPad, // Number pad. + Western, // On JPN systems, a text keyboard without Japanese input capabilities, otherwise same as SWKBD_TYPE_NORMAL. + }; + + // Keyboard dialog buttons. + enum class SoftwareKeyboardButtonConfig : u32 { + SingleButton, // Ok button + DualButton, // Cancel | Ok buttons + TripleButton, // Cancel | I Forgot | Ok buttons + NoButton, // No button (returned by swkbdInputText in special cases) + }; + + // Accepted input types. + enum class SoftwareKeyboardValidInput : u32 { + Anything, // All inputs are accepted. + NotEmpty, // Empty inputs are not accepted. + NotEmptyNotBlank, // Empty or blank inputs (consisting solely of whitespace) are not accepted. + NotBlank, // Blank inputs (consisting solely of whitespace) are not accepted, but empty inputs are. + FixedLen, // The input must have a fixed length (specified by maxTextLength in swkbdInit) + }; + + // Keyboard password modes. + enum class SoftwareKeyboardPasswordMode : u32 { + None, // Characters are not concealed. + Hide, // Characters are concealed immediately. + HideDelay, // Characters are concealed a second after they've been typed. + }; + + // Keyboard filter callback return values. + enum class SoftwareKeyboardCallbackResult : u32 { + OK, // Specifies that the input is valid. + Close, // Displays an error message, then closes the keyboard. + Continue, // Displays an error message and continues displaying the keyboard. + }; + + // Keyboard return values. + enum class SoftwareKeyboardResult : s32 { + None = -1, // Dummy/unused. + InvalidInput = -2, // Invalid parameters to swkbd. + OutOfMem = -3, // Out of memory. + + D0Click = 0, // The button was clicked in 1-button dialogs. + D1Click0, // The left button was clicked in 2-button dialogs. + D1Click1, // The right button was clicked in 2-button dialogs. + D2Click0, // The left button was clicked in 3-button dialogs. + D2Click1, // The middle button was clicked in 3-button dialogs. + D2Click2, // The right button was clicked in 3-button dialogs. + + HomePressed = 10, // The HOME button was pressed. + ResetPressed, // The soft-reset key combination was pressed. + PowerPressed, // The POWER button was pressed. + + ParentalOK = 20, // The parental PIN was verified successfully. + ParentalFail, // The parental PIN was incorrect. + + BannedInput = 30, // The filter callback returned SoftwareKeyboardCallback::CLOSE. + }; + + struct SoftwareKeyboardConfig { + enum_le type; + enum_le numButtonsM1; + enum_le validInput; + enum_le passwordMode; + s32_le isParentalScreen; + s32_le darkenTopScreen; + u32_le filterFlags; + u32_le saveStateFlags; + u16_le maxTextLength; + u16_le dictWordCount; + u16_le maxDigits; + std::array, MAX_BUTTON> buttonText; + std::array numpadKeys; + std::array hintText; // Text to display when asking the user for input + bool predictiveInput; + bool multiline; + bool fixedWidth; + bool allowHome; + bool allowReset; + bool allowPower; + bool unknown; + bool defaultQwerty; + std::array buttonSubmitsText; + u16_le language; + + u32_le initialTextOffset; // Offset of the default text in the output SharedMemory + u32_le dictOffset; + u32_le initialStatusOffset; + u32_le initialLearningOffset; + u32_le sharedMemorySize; // Size of the SharedMemory + u32_le version; + + enum_le returnCode; + + u32_le statusOffset; + u32_le learningOffset; + + u32_le textOffset; // Offset in the SharedMemory where the output text starts + u16_le textLength; // Length in characters of the output text + + enum_le callbackResult; + std::array callbackMessage; + bool skipAtCheck; + std::array pad; + }; + static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software keyboard config size is wrong"); + virtual const char* name() override { return "Software Keyboard"; } - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) override; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) override; virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; virtual void reset() override; SoftwareKeyboardApplet(Memory& memory, std::optional& nextParam) : AppletBase(memory, nextParam) {} + void closeKeyboard(u32 appID); + + SoftwareKeyboardConfig config; }; } // namespace Applets \ No newline at end of file diff --git a/src/core/applets/mii_selector.cpp b/src/core/applets/mii_selector.cpp index ed1c4ad2..ede8de28 100644 --- a/src/core/applets/mii_selector.cpp +++ b/src/core/applets/mii_selector.cpp @@ -5,7 +5,7 @@ using namespace Applets; void MiiSelectorApplet::reset() {} -Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters) { return Result::Success; } +Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) { return Result::Success; } Result::HorizonResult MiiSelectorApplet::receiveParameter(const Applets::Parameter& parameter) { Helpers::warn("Mii Selector: Unimplemented ReceiveParameter"); diff --git a/src/core/applets/software_keyboard.cpp b/src/core/applets/software_keyboard.cpp index 07259d0d..2f3e2e35 100644 --- a/src/core/applets/software_keyboard.cpp +++ b/src/core/applets/software_keyboard.cpp @@ -1,11 +1,12 @@ #include "applets/software_keyboard.hpp" +#include + #include "kernel/handles.hpp" using namespace Applets; void SoftwareKeyboardApplet::reset() {} -Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters) { return Result::Success; } Result::HorizonResult SoftwareKeyboardApplet::receiveParameter(const Applets::Parameter& parameter) { Helpers::warn("Software keyboard: Unimplemented ReceiveParameter"); @@ -25,8 +26,47 @@ Result::HorizonResult SoftwareKeyboardApplet::receiveParameter(const Applets::Pa break; } - default: Helpers::panic("SWKBD SIGNAL %d\n", parameter.signal); + default: Helpers::panic("Unimplemented swkbd signal %d\n", parameter.signal); } return Result::Success; +} + +Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) { + if (parameters.size() < sizeof(SoftwareKeyboardConfig)) { + Helpers::warn("SoftwareKeyboard::Start: Invalid size for keyboard configuration"); + return Result::Success; + } + + std::memcpy(&config, ¶meters[0], sizeof(config)); + + // Temporarily hardcode the pressed button to be the firs tone + switch (config.numButtonsM1) { + case SoftwareKeyboardButtonConfig::SingleButton: config.returnCode = SoftwareKeyboardResult::D0Click; break; + case SoftwareKeyboardButtonConfig::DualButton: config.returnCode = SoftwareKeyboardResult::D1Click0; break; + case SoftwareKeyboardButtonConfig::TripleButton: config.returnCode = SoftwareKeyboardResult::D2Click0; break; + case SoftwareKeyboardButtonConfig::NoButton: config.returnCode = SoftwareKeyboardResult::None; break; + } + + if (config.filterFlags & SoftwareKeyboardFilter::Callback) { + Helpers::warn("Unimplemented software keyboard profanity callback"); + } + + closeKeyboard(appID); + return Result::Success; +} + +void SoftwareKeyboardApplet::closeKeyboard(u32 appID) { + Applets::Parameter param = Applets::Parameter{ + .senderID = appID, + .destID = AppletIDs::Application, + .signal = static_cast(APTSignal::WakeupByExit), + .object = 0, + }; + + // Copy software keyboard configuration into the response parameter + param.data.resize(sizeof(config)); + std::memcpy(¶m.data[0], &config, sizeof(config)); + + nextParameter = param; } \ No newline at end of file diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index b2f3e849..f98c1bb5 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -154,6 +154,8 @@ void APTService::startLibraryApplet(u32 messagePointer) { Applets::AppletBase* destApplet = appletManager.getApplet(appID); if (destApplet == nullptr) { Helpers::warn("APT::StartLibraryApplet: Unimplemented dest applet ID"); + mem.write32(messagePointer, IPC::responseHeader(0x1E, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); } else { KernelObject* sharedMemObject = kernel.getObject(parameters); if (sharedMemObject == nullptr) { @@ -169,12 +171,13 @@ void APTService::startLibraryApplet(u32 messagePointer) { data.push_back(mem.read8(buffer + i)); } - Helpers::warn("DONE STARTED DAT STUFF"); - destApplet->start(*sharedMem, data); - + Result::HorizonResult result = destApplet->start(*sharedMem, data, appID); if (resumeEvent.has_value()) { kernel.signalEvent(resumeEvent.value()); } + + mem.write32(messagePointer, IPC::responseHeader(0x1E, 1, 0)); + mem.write32(messagePointer + 4, result); } }