From 56b767efbf6b3b7e82365fe33d6762c77e08f8b9 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sun, 23 Dec 2018 00:38:32 -0500 Subject: [PATCH] Implemented actual swkbd TextCheck support. Moved strbuf alloc from _swkbdProcessOutput into swkbdShow(), so that it can be used for TextCheck. Added validation for the out_string* params in swkbdShow(). The out_string buffer is now cleared before calling _swkbdProcessOutput. --- nx/include/switch/applets/swkbd.h | 14 +++++-- nx/source/applets/swkbd.c | 64 +++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/nx/include/switch/applets/swkbd.h b/nx/include/switch/applets/swkbd.h index d4a8fe01..ac967862 100644 --- a/nx/include/switch/applets/swkbd.h +++ b/nx/include/switch/applets/swkbd.h @@ -8,7 +8,14 @@ #include "../types.h" #include "../services/applet.h" -typedef bool (*SwkbdTextCheckCb)(void *); ///< TextCheck callback. TODO +typedef enum { + SwkbdTextCheckResult_OK = 0, ///< Success, valid string. + SwkbdTextCheckResult_Bad = 1, ///< Failure, invalid string. Error message is displayed in a message-box, pressing OK will return to swkbd again. + SwkbdTextCheckResult_Prompt = 2, ///< Failure, invalid string. Error message is displayed in a message-box, pressing Cancel will return to swkbd again, while pressing OK will continue as if the text was valid. + SwkbdTextCheckResult_Silent = 3, ///< Failure, invalid string. With value 3 and above, swkbd will silently not accept the string, without displaying any error. +} SwkbdTextCheckResult; + +typedef SwkbdTextCheckResult (*SwkbdTextCheckCb)(char* tmp_string, size_t tmp_string_size); /// TextCheck callback set by \ref swkbdConfigSetTextCheckCallback, for validating the input string when the swkbd ok-button is pressed. This buffer contains an UTF-8 string. This callback should validate the input string, then return a \ref SwkbdTextCheckResult indicating success/failure. On failure, this function must write an error message to the tmp_string buffer, which will then be displayed by swkbd. /// Base swkbd arg struct. typedef struct { @@ -165,14 +172,15 @@ void swkbdConfigSetInitialText(SwkbdConfig* c, const char* str); void swkbdConfigSetDictionary(SwkbdConfig* c, const SwkbdDictWord *input, s32 entries); /** - * @brief Sets the TextCheck callback. TODO: this is not yet used. + * @brief Sets the TextCheck callback. * @param c SwkbdConfig struct. - * @param cb callback + * @param cb \ref SwkbdTextCheckCb callback. */ void swkbdConfigSetTextCheckCallback(SwkbdConfig* c, SwkbdTextCheckCb cb); /** * @brief Launch swkbd with the specified config. This will return once swkbd is finished running. + * @note The string buffer is also used for the buffer passed to the \ref SwkbdTextCheckCb, when it's set. Hence, in that case this buffer should be large enough to handle TextCheck string input/output. The size passed to the callback is the same size passed here, -1. * @param c SwkbdConfig struct. * @param out_string UTF-8 Output string buffer. * @param out_string_size UTF-8 Output string buffer size, including NUL-terminator. diff --git a/nx/source/applets/swkbd.c b/nx/source/applets/swkbd.c index d166c554..0e9b01d1 100644 --- a/nx/source/applets/swkbd.c +++ b/nx/source/applets/swkbd.c @@ -183,27 +183,54 @@ void swkbdConfigSetTextCheckCallback(SwkbdConfig* c, SwkbdTextCheckCb cb) { c->arg.arg.textCheckCb = cb; } -static Result _swkbdProcessOutput(AppletHolder* h, char* out_string, size_t out_string_size) { +static Result _swkbdProcessInteractive(SwkbdConfig* c, AppletHolder* h, uint16_t* strbuf, size_t strbuf_size, char* tmp_string, size_t tmp_string_size) { + Result rc=0; + AppletStorage storage; + u64 strsize=0; + u32 res=0; + + rc = appletHolderPopInteractiveOutData(h, &storage); + if (R_FAILED(rc)) return rc; + + if (R_SUCCEEDED(rc)) rc = appletStorageRead(&storage, 0, &strsize, sizeof(strsize)); + if (R_SUCCEEDED(rc) && strsize > strbuf_size) strsize = strbuf_size; + if (R_SUCCEEDED(rc)) rc = appletStorageRead(&storage, sizeof(strsize), strbuf, strsize); + appletStorageClose(&storage); + + if (R_SUCCEEDED(rc) && (c->arg.arg.textCheckFlag && c->arg.arg.textCheckCb)) { + _swkbdConvertToUTF8(tmp_string, strbuf, tmp_string_size-1); + + res = c->arg.arg.textCheckCb(tmp_string, tmp_string_size-1); + + _swkbdConvertToUTF16ByteSize(strbuf, tmp_string, strbuf_size-2); + } + + if (R_SUCCEEDED(rc)) rc = appletCreateStorage(&storage, sizeof(res)+strbuf_size); + if (R_SUCCEEDED(rc)) { + rc = appletStorageWrite(&storage, 0, &res, sizeof(res)); + if (R_SUCCEEDED(rc)) rc = appletStorageWrite(&storage, sizeof(res), strbuf, strbuf_size); + + if (R_FAILED(rc)) appletStorageClose(&storage); + if (R_SUCCEEDED(rc)) rc = appletHolderPushInteractiveInData(h, &storage); + } + + return rc; +} + +static Result _swkbdProcessOutput(AppletHolder* h, uint16_t* strbuf, size_t strbuf_size, char* out_string, size_t out_string_size) { Result rc=0; AppletStorage outstorage; u32 CloseResult=0; - uint16_t* strbuf = NULL; - size_t strbuf_size = 0x7D4; rc = appletHolderPopOutData(h, &outstorage); if (R_FAILED(rc)) return rc; - strbuf = (u16*)malloc(strbuf_size+2); - if (strbuf==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); - if (strbuf) memset(strbuf, 0, strbuf_size+2); - if (R_SUCCEEDED(rc)) rc = appletStorageRead(&outstorage, 0, &CloseResult, sizeof(CloseResult)); if (R_SUCCEEDED(rc) && CloseResult!=0) rc = MAKERESULT(Module_Libnx, LibnxError_LibAppletBadExit); if (R_SUCCEEDED(rc)) rc = appletStorageRead(&outstorage, sizeof(CloseResult), strbuf, strbuf_size); if (R_SUCCEEDED(rc)) _swkbdConvertToUTF8(out_string, strbuf, out_string_size-1); - free(strbuf); appletStorageClose(&outstorage); return rc; @@ -213,12 +240,24 @@ Result swkbdShow(SwkbdConfig* c, char* out_string, size_t out_string_size) { Result rc=0; AppletHolder holder; AppletStorage storage; + uint16_t* strbuf = NULL; + size_t strbuf_size = 0x7D4; + + if (out_string==NULL || out_string_size==0) return MAKERESULT(Module_Libnx, LibnxError_BadInput); memset(&storage, 0, sizeof(AppletStorage)); - rc = appletCreateLibraryApplet(&holder, AppletId_swkbd, LibAppletMode_AllForeground); + strbuf = (u16*)malloc(strbuf_size+2); + if (strbuf==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + if (strbuf) memset(strbuf, 0, strbuf_size+2); if (R_FAILED(rc)) return rc; + rc = appletCreateLibraryApplet(&holder, AppletId_swkbd, LibAppletMode_AllForeground); + if (R_FAILED(rc)) { + free(strbuf); + return rc; + } + LibAppletArgs commonargs; libappletArgsCreate(&commonargs, c->version); rc = libappletArgsPush(&commonargs, &holder); @@ -238,7 +277,7 @@ Result swkbdShow(SwkbdConfig* c, char* out_string, size_t out_string_size) { if (R_SUCCEEDED(rc)) { while(appletHolderWaitInteractiveOut(&holder)) { - //TODO: Handle Interactive data here. + _swkbdProcessInteractive(c, &holder, strbuf, strbuf_size, out_string, out_string_size); } } @@ -254,13 +293,16 @@ Result swkbdShow(SwkbdConfig* c, char* out_string, size_t out_string_size) { rc = MAKERESULT(Module_Libnx, LibnxError_LibAppletBadExit); } else { //success - rc = _swkbdProcessOutput(&holder, out_string, out_string_size); + memset(out_string, 0, out_string_size); + rc = _swkbdProcessOutput(&holder, strbuf, strbuf_size, out_string, out_string_size); } } appletHolderClose(&holder); appletStorageCloseTmem(&storage); + free(strbuf); + return rc; }