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; }