diff --git a/nx/include/switch/applets/swkbd.h b/nx/include/switch/applets/swkbd.h index d7b30709..c2b912b2 100644 --- a/nx/include/switch/applets/swkbd.h +++ b/nx/include/switch/applets/swkbd.h @@ -8,19 +8,132 @@ #include "../types.h" #include "../services/applet.h" +typedef bool (*SwkbdTextCheckCb)(void *); ///< TextCheck callback. TODO + +/// Base swkbd arg struct. typedef struct { - u8 data[0x3E0];//TODO: Fill this in. -} SwkbdArg; + u32 unk_x0; + u16 okButtonText[18/2]; + u16 leftButtonText; + u16 rightButtonText; + u16 unk_x1a; + u32 keySetDisableBitmask; + u32 initialCursorPos; + u16 headerText[130/2]; + u16 subText[258/2]; + u16 guideText[514/2]; + u16 pad_x3aa; + u32 stringLenMax; + u32 unk_x3b0; + u32 passwordFlag; + u32 unk_x3b8; + u16 returnButtonFlag; ///< Controls whether the Return button is enabled, for newlines input. 0 = disabled, non-zero = enabled. + u8 blurBackground; ///< When enabled with value 1, the background is blurred. + u8 pad_x3bf; + u32 initialStringOffset; + u32 initialStringSize; + u32 userDicOffset; + s32 userDicEntries; + u8 textCheckFlag; + u8 pad_x3d1[7]; + SwkbdTextCheckCb textCheckCb; ///< This really doesn't belong in a struct sent to another process, but official sw does this. +} SwkbdArgV0; + +/// Arg struct for version 0x30007+. +typedef struct { + SwkbdArgV0 arg; + u64 unk_x3e0[4]; +} SwkbdArgV7; typedef struct { - SwkbdArg arg; + SwkbdArgV7 arg; + + u8* workbuf; + size_t workbuf_size; + s32 max_dictwords; } SwkbdConfig; +/// User dictionary word. +typedef struct { + u8 unk_x0[0x64]; +} SwkbdDictWord; + /** * @brief Creates a SwkbdConfig struct. * @param c SwkbdConfig struct. + * @param max_dictwords Max \ref SwkbdDictWord entries, 0 for none. */ -void swkbdCreate(SwkbdConfig* c); +Result swkbdCreate(SwkbdConfig* c, s32 max_dictwords); + +/** + * @brief Closes a SwkbdConfig struct. + * @param c SwkbdConfig struct. + */ +void swkbdClose(SwkbdConfig* c); + +/** + * @brief Sets the Ok button text. The default is "". + * @param c SwkbdConfig struct. + * @param str UTF-8 input string. + */ +void swkbdConfigSetOkButtonText(SwkbdConfig* c, const char* str); + +/** + * @brief Sets the LeftOptionalSymbolKey. The default is "". + * @param c SwkbdConfig struct. + * @param str UTF-8 input string. + */ +void swkbdConfigSetLeftOptionalSymbolKey(SwkbdConfig* c, const char* str); + +/** + * @brief Sets the RightOptionalSymbolKey. The default is "". + * @param c SwkbdConfig struct. + * @param str UTF-8 input string. + */ +void swkbdConfigSetRightOptionalSymbolKey(SwkbdConfig* c, const char* str); + +/** + * @brief Sets the Header text. The default is "". + * @param c SwkbdConfig struct. + * @param str UTF-8 input string. + */ +void swkbdConfigSetHeaderText(SwkbdConfig* c, const char* str); + +/** + * @brief Sets the Sub text. The default is "". + * @param c SwkbdConfig struct. + * @param str UTF-8 input string. + */ +void swkbdConfigSetSubText(SwkbdConfig* c, const char* str); + +/** + * @brief Sets the Guide text. The default is "". + * @param c SwkbdConfig struct. + * @param str UTF-8 input string. + */ +void swkbdConfigSetGuideText(SwkbdConfig* c, const char* str); + +/** + * @brief Sets the Initial text. The default is "". + * @param c SwkbdConfig struct. + * @param str UTF-8 input string. + */ +void swkbdConfigSetInitialText(SwkbdConfig* c, const char* str); + +/** + * @brief Sets the user dictionary. + * @param c SwkbdConfig struct. + * @param input Input data. + * @param entries Total entries in the buffer. + */ +void swkbdConfigSetDictionary(SwkbdConfig* c, const SwkbdDictWord *input, s32 entries); + +/** + * @brief Sets the TextCheck callback. TODO: this is not yet used. + * @param c SwkbdConfig struct. + * @param cb callback + */ +void swkbdConfigSetTextCheckCallback(SwkbdConfig* c, SwkbdTextCheckCb cb); /** * @brief Launch swkbd with the specified config. This will return once swkbd is finished running. diff --git a/nx/source/applets/swkbd.c b/nx/source/applets/swkbd.c index 98b4cdf5..8aba473e 100644 --- a/nx/source/applets/swkbd.c +++ b/nx/source/applets/swkbd.c @@ -2,6 +2,7 @@ #include #include "types.h" #include "result.h" +#include "kernel/detect.h" #include "services/applet.h" #include "applets/libapplet.h" #include "applets/swkbd.h" @@ -9,19 +10,111 @@ //TODO: InlineKeyboard currently isn't supported. -void swkbdCreate(SwkbdConfig* c) { - memset(c, 0, sizeof(SwkbdConfig)); -} - static void _swkbdConvertToUTF8(char* out, const u16* in, size_t max) { - out[0] = 0; if (out==NULL || in==NULL) return; + out[0] = 0; ssize_t units = utf16_to_utf8((uint8_t*)out, in, max); if (units < 0) return; out[units] = 0; } +static ssize_t _swkbdConvertToUTF16(u16* out, const char* in, size_t max) { + if (out==NULL || in==NULL) return 0; + out[0] = 0; + + ssize_t units = utf8_to_utf16(out, (uint8_t*)in, max); + if (units < 0 || max<=1) return 0; + out[units] = 0; + + return units; +} + +static ssize_t _swkbdConvertToUTF16ByteSize(u16* out, const char* in, size_t max) { + return _swkbdConvertToUTF16(out, in, (max/sizeof(u16)) - 1); +} + +Result swkbdCreate(SwkbdConfig* c, s32 max_dictwords) { + Result rc=0; + + memset(c, 0, sizeof(SwkbdConfig)); + memset(c->arg.unk_x3e0, 0xff, sizeof(c->arg.unk_x3e0)); + c->workbuf_size = 0x1000; + if (max_dictwords > 0 && max_dictwords <= 0x3e8) c->max_dictwords = max_dictwords; + + if (c->max_dictwords) { + c->workbuf_size = c->max_dictwords*0x64 + 0x7e8; + c->workbuf_size = (c->workbuf_size + 0xfff) & ~0xfff; + } + + c->workbuf = (u8*)memalign(0x1000, c->workbuf_size); + if (c->workbuf==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + if (R_SUCCEEDED(rc)) memset(c->workbuf, 0, c->workbuf_size); + + return rc; +} + +void swkbdClose(SwkbdConfig* c) { + free(c->workbuf); + memset(c, 0, sizeof(SwkbdConfig)); +} + +void swkbdConfigSetOkButtonText(SwkbdConfig* c, const char* str) { + _swkbdConvertToUTF16ByteSize(c->arg.arg.okButtonText, str, sizeof(c->arg.arg.okButtonText)); +} + +void swkbdConfigSetLeftOptionalSymbolKey(SwkbdConfig* c, const char* str) { + _swkbdConvertToUTF16(&c->arg.arg.leftButtonText, str, 1); +} + +void swkbdConfigSetRightOptionalSymbolKey(SwkbdConfig* c, const char* str) { + _swkbdConvertToUTF16(&c->arg.arg.rightButtonText, str, 1); +} + +void swkbdConfigSetHeaderText(SwkbdConfig* c, const char* str) { + _swkbdConvertToUTF16ByteSize(c->arg.arg.headerText, str, sizeof(c->arg.arg.headerText)); +} + +void swkbdConfigSetSubText(SwkbdConfig* c, const char* str) { + _swkbdConvertToUTF16ByteSize(c->arg.arg.subText, str, sizeof(c->arg.arg.subText)); +} + +void swkbdConfigSetGuideText(SwkbdConfig* c, const char* str) { + _swkbdConvertToUTF16ByteSize(c->arg.arg.guideText, str, sizeof(c->arg.arg.guideText)); +} + +void swkbdConfigSetInitialText(SwkbdConfig* c, const char* str) { + c->arg.arg.initialStringOffset = 0; + c->arg.arg.initialStringSize = 0; + + if (c->workbuf==NULL) return; + u32 offset=0x14; + + ssize_t units = _swkbdConvertToUTF16ByteSize((u16*)&c->workbuf[offset], str, 0x1f4); + if (units<=0) return; + + c->arg.arg.initialStringOffset = offset; + c->arg.arg.initialStringSize = units; +} + +void swkbdConfigSetDictionary(SwkbdConfig* c, const SwkbdDictWord *buffer, s32 entries) { + c->arg.arg.userDicOffset = 0; + c->arg.arg.userDicEntries = 0; + + if (c->workbuf==NULL) return; + if (entries < 1 || entries > c->max_dictwords) return; + u32 offset=0x7e8; + + c->arg.arg.userDicOffset = offset; + c->arg.arg.userDicEntries = entries; + memcpy(&c->workbuf[offset], buffer, entries*0x64); +} + +void swkbdConfigSetTextCheckCallback(SwkbdConfig* c, SwkbdTextCheckCb cb) { + c->arg.arg.textCheckFlag = cb!=0; + c->arg.arg.textCheckCb = cb; +} + static Result _swkbdProcessOutput(AppletHolder* h, char* out_string, size_t out_string_size) { Result rc=0; AppletStorage outstorage; @@ -52,22 +145,32 @@ Result swkbdShow(SwkbdConfig* c, char* out_string, size_t out_string_size) { Result rc=0; AppletHolder holder; AppletStorage storage; - u8 *workbuf = NULL;//TODO - size_t workbuf_size = 0x1000;//TODO + u32 version=0x5;//1.0.0+ version memset(&storage, 0, sizeof(AppletStorage)); + //TODO: >3.0.0 versions. + if (kernelAbove300()) { + version = 0x30007; + } + else if (kernelAbove200()) { + version = 0x10006; + } + rc = appletCreateLibraryApplet(&holder, AppletId_swkbd, LibAppletMode_AllForeground); if (R_FAILED(rc)) return rc; LibAppletArgs commonargs; - libappletArgsCreate(&commonargs, 0x5);//1.0.0+ version + libappletArgsCreate(&commonargs, version); rc = libappletArgsPush(&commonargs, &holder); - if (R_SUCCEEDED(rc)) rc = libappletPushInData(&holder, &c->arg, sizeof(c->arg)); + if (R_SUCCEEDED(rc)) { + if (version < 0x30007) rc = libappletPushInData(&holder, &c->arg.arg, sizeof(c->arg.arg)); + if (version >= 0x30007) rc = libappletPushInData(&holder, &c->arg, sizeof(c->arg)); + } if (R_SUCCEEDED(rc)) { - if (R_SUCCEEDED(rc)) rc = appletCreateTransferMemoryStorage(&storage, workbuf, workbuf_size, true); + if (R_SUCCEEDED(rc)) rc = appletCreateTransferMemoryStorage(&storage, c->workbuf, c->workbuf_size, true); appletHolderPushInData(&holder, &storage); }