From 2900622cb5374b7df5af998f7990894521f49a27 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Fri, 4 Jan 2019 22:05:06 -0500 Subject: [PATCH] Initial SwkbdInline impl, minor swkbd adjustments. --- nx/include/switch/applets/swkbd.h | 99 ++++++++++++++++++++ nx/source/applets/swkbd.c | 147 +++++++++++++++++++++++++++--- 2 files changed, 233 insertions(+), 13 deletions(-) diff --git a/nx/include/switch/applets/swkbd.h b/nx/include/switch/applets/swkbd.h index 64bc4bca..7f49a103 100644 --- a/nx/include/switch/applets/swkbd.h +++ b/nx/include/switch/applets/swkbd.h @@ -40,6 +40,13 @@ typedef enum { SwkbdTextDrawType_DownloadCode = 2, ///< Used by \ref swkbdConfigMakePresetDownloadCode on 5.0.0+. Enables using \ref SwkbdArgV7 unk_x3e0. } SwkbdTextDrawType; +/// SwkbdInline Interactive input storage request ID. +typedef enum { + SwkbdRequestCommand_Finalize = 0x4, + SwkbdRequestCommand_SetCustomizeDic = 0x7, + SwkbdRequestCommand_Calc = 0xA, +} SwkbdRequestCommand; + 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. @@ -93,6 +100,71 @@ typedef struct { u8 unk_x0[0x64]; } SwkbdDictWord; +typedef struct { + u32 unk_x0; + u8 mode; ///< Controls the LibAppletMode when launching the applet. Non-zero indicates LibAppletMode_Background, otherwise LibAppletMode_Unknown3 + u8 unk_x5; ///< Only set on 5.0.0+. + u8 pad[2]; +} SwkbdInitializeArg; + +typedef struct { + u32 unk_x0; + u64 unk_x4; + u64 unk_xc; + u8 unk_x14[0x6]; + u8 unk_x1a; + u8 unk_x1b; + u32 unk_x1c; + s32 unk_x20; + s32 unk_x24; + u64 unk_x28; + u8 unk_x30; + u8 unk_x31[0x17]; +} SwkbdAppearArg; + +typedef struct { + u32 unk_x0; + u16 size; ///< Size of this struct. + u8 unk_x6; + u8 unk_x7; + u64 flags; + SwkbdInitializeArg initArg; ///< Flags bitmask 0x1. + float volume; ///< Flags bitmask 0x2. + s32 cursorPos; ///< Flags bitmask 0x10. + SwkbdAppearArg appearArg; + u8 unk_x68[0x3d4]; + u8 utf8Mode; ///< Flags bitmask 0x20. + u8 unk_x43d; + u8 enableBackspace; ///< Flags bitmask 0x8000. Added with 5.x. + u8 unk_x43f[3]; + u8 keytopAsFloating; ///< Flags bitmask 0x200. + u8 footerScalable; ///< Flags bitmask 0x100. + u8 alphaEnabledInInputMode; ///< Flags bitmask 0x200. + u8 inputModeFadeType; ///< Flags bitmask 0x100. + u8 disableTouch; ///< Flags bitmask 0x200. + u8 disableUSBKeyboard; ///< Flags bitmask 0x800. + u8 unk_x448[5]; + u16 unk_x44d; + u8 unk_x44f; + float keytopScale0; ///< Flags bitmask 0x200. + float keytopScale1; ///< Flags bitmask 0x200. + float keytopTranslate0; ///< Flags bitmask 0x200. + float keytopTranslate1; ///< Flags bitmask 0x200. + float keytopBgAlpha; ///< Flags bitmask 0x100. + float unk_x464; + float balloonScale; ///< Flags bitmask 0x200. + float unk_x46c; + u64 unk_x470[4]; + u8 unk_x490[0x10]; +} PACKED SwkbdInlineCalcArg; + +/// InlineKeyboard +typedef struct { + u32 version; + AppletHolder holder; + SwkbdInlineCalcArg calcArg; +} SwkbdInline; + /** * @brief Creates a SwkbdConfig struct. * @param c SwkbdConfig struct. @@ -214,3 +286,30 @@ void swkbdConfigSetTextCheckCallback(SwkbdConfig* c, SwkbdTextCheckCb cb); */ Result swkbdShow(SwkbdConfig* c, char* out_string, size_t out_string_size); +/** + * @brief Creates a SwkbdInline object. + * @note This is essentially an asynchronous version of the regular swkbd. + * @param s SwkbdInline object. + */ +Result swkbdInlineCreate(SwkbdInline* s); + +/** + * @brief Closes a SwkbdInline object. If the applet is running, this will tell the applet to exit, then wait for the applet to exit + applet exit handling. + * @param s SwkbdInline object. + */ +Result swkbdInlineClose(SwkbdInline* s); + +/** + * @brief Launches the applet with the SwkbdInline object. + * @param s SwkbdInline object. + */ +Result swkbdInlineLaunch(SwkbdInline* s); + +/** + * @brief Handles updating SwkbdInline state, this should be called periodically. + * @note Handles applet exit if needed, and also sends the \ref SwkbdInlineCalcArg to the applet if needed. Hence, this should be called at some point after writing to \ref SwkbdInlineCalcArg. + * @note Handles applet Interactive storage output when needed. + * @param s SwkbdInline object. + */ +Result swkbdInlineUpdate(SwkbdInline* s); + diff --git a/nx/source/applets/swkbd.c b/nx/source/applets/swkbd.c index e2111fb7..8d25b991 100644 --- a/nx/source/applets/swkbd.c +++ b/nx/source/applets/swkbd.c @@ -39,25 +39,29 @@ static void _swkbdConfigClear(SwkbdConfig* c) { memset(c->arg.unk_x3e0, 0xff, sizeof(c->arg.unk_x3e0)); } +static void _swkbdInitVersion(u32* version) { + *version=0x5;//1.0.0+ version + if (kernelAbove500()) { + *version = 0x50009; + } + else if (kernelAbove400()) { + *version = 0x40008; + } + else if (kernelAbove300()) { + *version = 0x30007; + } + else if (kernelAbove200()) { + *version = 0x10006; + } +} + Result swkbdCreate(SwkbdConfig* c, s32 max_dictwords) { Result rc=0; memset(c, 0, sizeof(SwkbdConfig)); _swkbdConfigClear(c); - c->version=0x5;//1.0.0+ version - if (kernelAbove500()) { - c->version = 0x50009; - } - else if (kernelAbove400()) { - c->version = 0x40008; - } - else if (kernelAbove300()) { - c->version = 0x30007; - } - else if (kernelAbove200()) { - c->version = 0x10006; - } + _swkbdInitVersion(&c->version); c->workbuf_size = 0x1000; if (max_dictwords > 0 && max_dictwords <= 0x3e8) c->max_dictwords = max_dictwords; @@ -310,3 +314,120 @@ Result swkbdShow(SwkbdConfig* c, char* out_string, size_t out_string_size) { return rc; } +static Result _swkbdSendRequest(SwkbdInline* s, u32 RequestCommand, const void* buffer, size_t size) { + Result rc=0; + AppletStorage storage; + + rc = appletCreateStorage(&storage, size+sizeof(u32)); + if (R_FAILED(rc)) return rc; + + rc = appletStorageWrite(&storage, 0, &RequestCommand, sizeof(RequestCommand)); + if (R_SUCCEEDED(rc) && buffer!=NULL) rc = appletStorageWrite(&storage, sizeof(RequestCommand), buffer, size); + if (R_FAILED(rc)) { + appletStorageClose(&storage); + return rc; + } + + return appletHolderPushInteractiveInData(&s->holder, &storage); +} + +Result swkbdInlineCreate(SwkbdInline* s) { + memset(s, 0, sizeof(SwkbdInline)); + + _swkbdInitVersion(&s->version); + + s->calcArg.unk_x0 = 0x30000; + s->calcArg.size = sizeof(s->calcArg); + + //if (s->version >= 0x50009) s->calcArg.initArg.unk_x5 = 0x1;//Set in a separate init func by official sw on 5.0.0+. + + s->calcArg.volume = 1.0f; + s->calcArg.appearArg.unk_x0 = 0x2; + s->calcArg.unk_x6 = 1; + s->calcArg.unk_x7 = 1; + s->calcArg.appearArg.unk_x20 = -1; + s->calcArg.appearArg.unk_x24 = -1; + s->calcArg.appearArg.unk_x30 = 1; + + s->calcArg.enableBackspace = 1; + s->calcArg.unk_x43f[0] = 1; + s->calcArg.footerScalable = 1; + s->calcArg.inputModeFadeType = 1; + + s->calcArg.keytopScale0 = 1.0f; + s->calcArg.keytopScale1 = 1.0f; + s->calcArg.keytopBgAlpha = 1.0f; + s->calcArg.unk_x464 = 1.0f; + s->calcArg.balloonScale = 1.0f; + s->calcArg.unk_x46c = 1.0f; + + return 0; +} + +Result swkbdInlineClose(SwkbdInline* s) { + Result rc=0; + + if (appletHolderActive(&s->holder)) + { + _swkbdSendRequest(s, SwkbdRequestCommand_Finalize, NULL, 0);//Finalize cmd + + appletHolderJoin(&s->holder); + + LibAppletExitReason reason = appletHolderGetExitReason(&s->holder); + + if (reason == LibAppletExitReason_Canceled) { + rc = MAKERESULT(Module_Libnx, LibnxError_LibAppletBadExit); + } + else if (reason == LibAppletExitReason_Abnormal || reason == LibAppletExitReason_Unexpected) { + rc = MAKERESULT(Module_Libnx, LibnxError_LibAppletBadExit); + } + + appletHolderClose(&s->holder); + } + + return rc; +} + +Result swkbdInlineLaunch(SwkbdInline* s) { + Result rc=0; + + rc = appletCreateLibraryApplet(&s->holder, AppletId_swkbd, s->calcArg.initArg.mode!=0 ? LibAppletMode_Background : LibAppletMode_Unknown3); + if (R_FAILED(rc)) return rc; + + LibAppletArgs commonargs; + libappletArgsCreate(&commonargs, s->version); + rc = libappletArgsPush(&commonargs, &s->holder); + + if (R_SUCCEEDED(rc)) rc = libappletPushInData(&s->holder, &s->calcArg.initArg, sizeof(s->calcArg.initArg)); + + if (R_SUCCEEDED(rc)) rc = appletHolderStart(&s->holder); + + return rc; +} + +Result swkbdInlineUpdate(SwkbdInline* s) { + Result rc=0; + AppletStorage storage; + + //TODO: 'Normalize' floats. + + if (appletHolderCheckFinished(&s->holder)) { + appletHolderJoin(&s->holder); + appletHolderClose(&s->holder); + return 0; + } + + if (s->calcArg.flags) { + rc = _swkbdSendRequest(s, SwkbdRequestCommand_Calc, &s->calcArg, sizeof(s->calcArg)); + s->calcArg.flags = 0; + if (R_FAILED(rc)) return rc; + } + + while(R_SUCCEEDED(appletHolderPopInteractiveOutData(&s->holder, &storage))) { + //TODO: Process storage content. + appletStorageClose(&storage); + } + + return rc; +} +