#include #include #include "libapplet_internal.h" #include "applets/swkbd.h" #include "services/vi.h" #include "runtime/hosversion.h" #include "runtime/util/utf.h" #include "../runtime/alloc.h" static Result _swkbdGetReplies(SwkbdInline* s); static void _swkbdConvertToUTF8(char* out, const u16* in, size_t max) { 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); } /// Clamp a float to the range 0.0f..1.0.f. static void _swkbdClampFloat(float *val) { float tmpval = *val; tmpval = fminf(tmpval, 1.0f); tmpval = fmaxf(tmpval, 0.0f); *val = tmpval; } static void _swkbdConfigClear(SwkbdConfig* c) { memset(&c->arg.arg, 0, sizeof(c->arg.arg)); memset(c->arg.textGrouping, 0xff, sizeof(c->arg.textGrouping)); } static void _swkbdInitVersion(u32* version) { u32 hosver = hosversionGet(); if (hosver >= MAKEHOSVERSION(8,0,0)) *version = 0x8000D; else if (hosver >= MAKEHOSVERSION(6,0,0)) *version = 0x6000B; else if (hosver >= MAKEHOSVERSION(5,0,0)) *version = 0x50009; else if (hosver >= MAKEHOSVERSION(4,0,0)) *version = 0x40008; else if (hosver >= MAKEHOSVERSION(3,0,0)) *version = 0x30007; else if (hosver >= MAKEHOSVERSION(2,0,0)) *version = 0x10006; else *version=0x5; // [1.0.0+] version } Result swkbdCreate(SwkbdConfig* c, s32 max_dictwords) { Result rc=0; s32 maxwords = 0x3e8; memset(c, 0, sizeof(SwkbdConfig)); _swkbdConfigClear(c); _swkbdInitVersion(&c->version); if (c->version >= 0x8000D) maxwords = 0x1388; c->workbuf_size = 0x1000; if (max_dictwords > 0 && max_dictwords <= maxwords) c->max_dictwords = max_dictwords; if (c->max_dictwords) { c->workbuf_size = c->max_dictwords*sizeof(SwkbdDictWord) + 0x7e8; c->workbuf_size = (c->workbuf_size + 0xfff) & ~0xfff; } c->workbuf = (u8*)__libnx_aligned_alloc(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) { __libnx_free(c->workbuf); memset(c, 0, sizeof(SwkbdConfig)); } void swkbdConfigMakePresetDefault(SwkbdConfig* c) { _swkbdConfigClear(c); swkbdConfigSetType(c, SwkbdType_QWERTY); swkbdConfigSetInitialCursorPos(c, 1); if (c->version < 0x50009) swkbdConfigSetTextDrawType(c, SwkbdTextDrawType_Box);//removed with 5.x swkbdConfigSetReturnButtonFlag(c, 1); swkbdConfigSetBlurBackground(c, 1); } void swkbdConfigMakePresetPassword(SwkbdConfig* c) { _swkbdConfigClear(c); swkbdConfigSetType(c, SwkbdType_QWERTY); swkbdConfigSetInitialCursorPos(c, 1); swkbdConfigSetPasswordFlag(c, 1); swkbdConfigSetBlurBackground(c, 1); } void swkbdConfigMakePresetUserName(SwkbdConfig* c) { _swkbdConfigClear(c); swkbdConfigSetType(c, SwkbdType_Normal); swkbdConfigSetKeySetDisableBitmask(c, SwkbdKeyDisableBitmask_UserName); swkbdConfigSetInitialCursorPos(c, 1); swkbdConfigSetBlurBackground(c, 1); } void swkbdConfigMakePresetDownloadCode(SwkbdConfig* c) { _swkbdConfigClear(c); swkbdConfigSetType(c, SwkbdType_Normal); swkbdConfigSetKeySetDisableBitmask(c, SwkbdKeyDisableBitmask_DownloadCode); swkbdConfigSetInitialCursorPos(c, 1); if (c->version >= 0x50009) {//5.x swkbdConfigSetType(c, SwkbdType_QWERTY); swkbdConfigSetStringLenMax(c, 16); swkbdConfigSetStringLenMin(c, 1); swkbdConfigSetTextDrawType(c, SwkbdTextDrawType_DownloadCode); } swkbdConfigSetBlurBackground(c, 1); if (c->version >= 0x50009) {//5.x swkbdConfigSetTextGrouping(c, 0, 0x3); swkbdConfigSetTextGrouping(c, 1, 0x7); swkbdConfigSetTextGrouping(c, 2, 0xb); } } void swkbdConfigSetOkButtonText(SwkbdConfig* c, const char* str) { _swkbdConvertToUTF16ByteSize(c->arg.arg.arg.okButtonText, str, sizeof(c->arg.arg.arg.okButtonText)); } void swkbdConfigSetLeftOptionalSymbolKey(SwkbdConfig* c, const char* str) { _swkbdConvertToUTF16(&c->arg.arg.arg.leftButtonText, str, 1); } void swkbdConfigSetRightOptionalSymbolKey(SwkbdConfig* c, const char* str) { _swkbdConvertToUTF16(&c->arg.arg.arg.rightButtonText, str, 1); } void swkbdConfigSetHeaderText(SwkbdConfig* c, const char* str) { _swkbdConvertToUTF16ByteSize(c->arg.arg.arg.headerText, str, sizeof(c->arg.arg.arg.headerText)); } void swkbdConfigSetSubText(SwkbdConfig* c, const char* str) { _swkbdConvertToUTF16ByteSize(c->arg.arg.arg.subText, str, sizeof(c->arg.arg.arg.subText)); } void swkbdConfigSetGuideText(SwkbdConfig* c, const char* str) { _swkbdConvertToUTF16ByteSize(c->arg.arg.arg.guideText, str, sizeof(c->arg.arg.arg.guideText)); } void swkbdConfigSetInitialText(SwkbdConfig* c, const char* str) { c->arg.arg.arg.initialStringOffset = 0; c->arg.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.arg.initialStringOffset = offset; c->arg.arg.arg.initialStringSize = units; } void swkbdConfigSetDictionary(SwkbdConfig* c, const SwkbdDictWord *buffer, s32 entries) { c->arg.arg.arg.userDicOffset = 0; c->arg.arg.arg.userDicEntries = 0; if (c->workbuf==NULL) return; if (entries < 1 || entries > c->max_dictwords) return; u32 offset=0x7e8; c->arg.arg.arg.userDicOffset = offset; c->arg.arg.arg.userDicEntries = entries; memcpy(&c->workbuf[offset], buffer, entries*sizeof(SwkbdDictWord)); } Result swkbdConfigSetCustomizedDictionaries(SwkbdConfig* c, const SwkbdCustomizedDictionarySet *dic) { if (c->version < 0x6000B) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); // [6.0.0+] memcpy(&c->customizedDictionarySet, dic, sizeof(*dic)); return 0; } void swkbdConfigSetTextCheckCallback(SwkbdConfig* c, SwkbdTextCheckCb cb) { c->arg.arg.arg.textCheckFlag = cb!=0; c->arg.arg.textCheckCb = cb; } 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.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; rc = appletHolderPopOutData(h, &outstorage); if (R_FAILED(rc)) return rc; 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); appletStorageClose(&outstorage); return rc; } Result swkbdShow(SwkbdConfig* c, char* out_string, size_t out_string_size) { Result rc=0; AppletHolder holder; AppletStorage storage; AppletStorage customizedDictionarySet_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)); memset(&customizedDictionarySet_storage, 0, sizeof(customizedDictionarySet_storage)); strbuf = (u16*)__libnx_alloc(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_LibraryAppletSwkbd, LibAppletMode_AllForeground); if (R_FAILED(rc)) { __libnx_free(strbuf); return rc; } LibAppletArgs commonargs; libappletArgsCreate(&commonargs, c->version); rc = libappletArgsPush(&commonargs, &holder); if (R_SUCCEEDED(rc)) { if (c->version >= 0x6000B) { SwkbdArgVB arg_vb; memset(&arg_vb, 0, sizeof(arg_vb)); memcpy(&arg_vb.arg, &c->arg.arg.arg, sizeof(arg_vb.arg)); memcpy(arg_vb.textGrouping, c->arg.textGrouping, sizeof(arg_vb.textGrouping)); memcpy(arg_vb.entries, c->customizedDictionarySet.entries, sizeof(arg_vb.entries)); arg_vb.total_entries = c->customizedDictionarySet.total_entries; if (c->version >= 0x8000D) { // [8.0.0+] arg_vb.unkFlag = c->unkFlag; arg_vb.trigger = c->trigger; } rc = libappletPushInData(&holder, &arg_vb, sizeof(arg_vb)); } else if (c->version >= 0x30007) rc = libappletPushInData(&holder, &c->arg, sizeof(c->arg)); // [3.0.0+] has a larger struct. else rc = libappletPushInData(&holder, &c->arg.arg, sizeof(c->arg.arg)); } if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) rc = appletCreateTransferMemoryStorage(&storage, c->workbuf, c->workbuf_size, true); if (R_SUCCEEDED(rc)) rc = appletHolderPushInData(&holder, &storage); } if (R_SUCCEEDED(rc) && c->version >= 0x6000B && c->customizedDictionarySet.buffer_size && c->customizedDictionarySet.total_entries) { // [6.0.0+] rc = appletCreateHandleStorageTmem(&customizedDictionarySet_storage, c->customizedDictionarySet.buffer, c->customizedDictionarySet.buffer_size); if (R_SUCCEEDED(rc)) rc = appletHolderPushInData(&holder, &customizedDictionarySet_storage); } if (R_SUCCEEDED(rc)) rc = appletHolderStart(&holder); if (R_SUCCEEDED(rc)) { while(appletHolderWaitInteractiveOut(&holder)) { _swkbdProcessInteractive(c, &holder, strbuf, strbuf_size, out_string, out_string_size); } } if (R_SUCCEEDED(rc)) { appletHolderJoin(&holder); LibAppletExitReason reason = appletHolderGetExitReason(&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); } else { //success memset(out_string, 0, out_string_size); rc = _swkbdProcessOutput(&holder, strbuf, strbuf_size, out_string, out_string_size); } } appletHolderClose(&holder); appletStorageCloseTmem(&storage); appletStorageCloseTmem(&customizedDictionarySet_storage); __libnx_free(strbuf); 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) { Result rc=0; memset(s, 0, sizeof(SwkbdInline)); _swkbdInitVersion(&s->version); // swkbd-inline is only available on [2.0.0+]. if (s->version < 0x10006) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); s->calcArg.unk_x0 = 0x30000; s->calcArg.size = sizeof(s->calcArg); s->calcArg.volume = 1.0f; s->calcArg.appearArg.type = SwkbdType_QWERTY; s->calcArg.unk_x6 = 1; s->calcArg.unk_x7 = 1; s->calcArg.appearArg.stringLenMax = -1; s->calcArg.appearArg.stringLenMin = -1; s->calcArg.appearArg.unk_x30 = 1; s->calcArg.enableBackspace = 1; s->calcArg.unk_x45f[0] = 1; s->calcArg.footerScalable = 1; s->calcArg.inputModeFadeType = 1; s->calcArg.keytopScaleX = 1.0f; s->calcArg.keytopScaleY = 1.0f; s->calcArg.keytopBgAlpha = 1.0f; s->calcArg.footerBgAlpha = 1.0f; s->calcArg.balloonScale = 1.0f; s->calcArg.unk_x48c = 1.0f; swkbdInlineSetUtf8Mode(s, true); s->interactive_tmpbuf_size = 0x1000; s->interactive_tmpbuf = (u8*)__libnx_alloc(s->interactive_tmpbuf_size); if (s->interactive_tmpbuf==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); if (R_SUCCEEDED(rc)) memset(s->interactive_tmpbuf, 0, s->interactive_tmpbuf_size); if (R_SUCCEEDED(rc)) { s->interactive_strbuf_size = 0x1001; s->interactive_strbuf = (char*)__libnx_alloc(s->interactive_strbuf_size); if (s->interactive_strbuf==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); if (R_SUCCEEDED(rc)) memset(s->interactive_strbuf, 0, s->interactive_strbuf_size); if (R_FAILED(rc)) { __libnx_free(s->interactive_tmpbuf); s->interactive_tmpbuf = NULL; } } if (R_FAILED(rc)) { s->interactive_tmpbuf_size = 0; s->interactive_strbuf_size = 0; } return rc; } Result swkbdInlineClose(SwkbdInline* s) { Result rc=0; if (appletHolderActive(&s->holder)) { _swkbdSendRequest(s, SwkbdRequestCommand_Finalize, NULL, 0);//Finalize cmd int cnt=0; while (s->dicCustomInitialized && cnt<9) { rc = _swkbdGetReplies(s); if (R_FAILED(rc)) break; if (s->dicCustomInitialized) { cnt++; svcSleepThread(100000000ULL); } } appletHolderJoin(&s->holder); if (R_SUCCEEDED(rc)) { 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); } __libnx_free(s->interactive_tmpbuf); s->interactive_tmpbuf = NULL; s->interactive_tmpbuf_size = 0; __libnx_free(s->interactive_strbuf); s->interactive_strbuf = NULL; s->interactive_strbuf_size = 0; if (s->dicCustomInitialized) appletStorageCloseTmem(&s->dicStorage); if (s->wordInfoInitialized) appletStorageCloseTmem(&s->wordInfoStorage); memset(s, 0, sizeof(SwkbdInline)); return rc; } static bool _swkbdInlineHandleFinished(SwkbdInline* s) { bool ret = appletHolderCheckFinished(&s->holder); if (ret) { appletHolderJoin(&s->holder); appletHolderClose(&s->holder); s->state = SwkbdState_Inactive; } return ret; } static Result _swkbdInlineLaunch(SwkbdInline* s, SwkbdInitializeArg *initArg) { Result rc=0; memcpy(&s->calcArg.initArg, initArg, sizeof(*initArg)); s->calcArg.flags |= 0x1; rc = appletCreateLibraryApplet(&s->holder, AppletId_LibraryAppletSwkbd, s->calcArg.initArg.mode!=SwkbdInlineMode_UserDisplay ? LibAppletMode_Background : LibAppletMode_BackgroundIndirect); if (R_FAILED(rc)) return rc; if (hosversionAtLeast(10,0,0)) rc = appletHolderPresetLibraryAppletGpuTimeSliceZero(&s->holder); LibAppletArgs commonargs; libappletArgsCreate(&commonargs, s->version); if (R_SUCCEEDED(rc)) 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); if (R_FAILED(rc)) appletHolderClose(&s->holder); return rc; } Result swkbdInlineLaunch(SwkbdInline* s) { SwkbdInitializeArg initArg = {0}; if (s->version >= 0x50009) initArg.unk_x5 = 0x1; // [5.0.0+] return _swkbdInlineLaunch(s, &initArg); } Result swkbdInlineLaunchForLibraryApplet(SwkbdInline* s, u8 mode, u8 unk_x5) { SwkbdInitializeArg initArg = {0}; initArg.mode = mode; initArg.unk_x5 = unk_x5; return _swkbdInlineLaunch(s, &initArg); } Result swkbdInlineGetImageMemoryRequirement(u64 *out_size, u64 *out_alignment) { s32 width=0, height=0; swkbdInlineGetWindowSize(&width, &height); return viGetIndirectLayerImageRequiredMemoryInfo(width, height, out_size, out_alignment); } Result swkbdInlineGetImage(SwkbdInline* s, void* buffer, u64 size, bool *data_available) { Result rc=0; u64 out_size=0, out_alignment=0; s32 width=0, height=0; u64 handle=0; if (!buffer || !size) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (_swkbdInlineHandleFinished(s)) { *data_available = false; return 0; } rc = swkbdInlineGetImageMemoryRequirement(&out_size, &out_alignment); if (R_SUCCEEDED(rc)) { if (out_size != size || (uintptr_t)buffer & (out_alignment-1)) rc = MAKERESULT(Module_Libnx, LibnxError_BadInput); } swkbdInlineGetWindowSize(&width, &height); if (R_SUCCEEDED(rc)) rc = appletHolderGetIndirectLayerConsumerHandle(&s->holder, &handle); if (R_SUCCEEDED(rc)) rc = viGetIndirectLayerImageMap(buffer, size, width, height, handle, NULL, NULL); if (R_SUCCEEDED(rc)) *data_available = true; else if (R_VALUE(rc) == MAKERESULT(114, 11)) { *data_available = false; rc = 0; } return rc; } s32 swkbdInlineGetMaxHeight(SwkbdInline* s) { bool flag=0; if (s->calcArg.appearArg.dicFlag || s->wordInfoInitialized || s->dicCustomInitialized) flag = 1; if (s->calcArg.appearArg.type >= SwkbdType_ZhHans && s->calcArg.appearArg.type <= SwkbdType_Unknown9) flag = 1; else flag = flag && !s->calcArg.appearArg.keySetDisableBitmask && (s->calcArg.appearArg.type != SwkbdType_NumPad && s->calcArg.appearArg.type != SwkbdType_QWERTY); s32 extra_height = flag ? 50 : 0; s32 height0 = s->calcArg.appearArg.type == SwkbdType_NumPad ? 350 : 400; height0+= extra_height; s32 height1 = flag ? 132 : 72; return s->state == SwkbdState_Unknown6 ? height1 : height0; } s32 swkbdInlineGetMiniaturizedHeight(SwkbdInline* s) { bool flag=0; if (s->calcArg.appearArg.type >= SwkbdType_ZhHans && s->calcArg.appearArg.type <= SwkbdType_Unknown9) flag = 1; else if (!(s->calcArg.appearArg.dicFlag && s->wordInfoInitialized && s->dicCustomInitialized)) { flag = !s->calcArg.appearArg.keySetDisableBitmask && (s->calcArg.appearArg.type != SwkbdType_NumPad && s->calcArg.appearArg.type != SwkbdType_QWERTY); } return flag ? 132 : 72; } s32 swkbdInlineGetTouchRectangles(SwkbdInline* s, SwkbdRect *keytop, SwkbdRect *footer) { float keytop_max_height = (float)swkbdInlineGetMaxHeight(s); float footer_max_height = keytop_max_height; if (!s->calcArg.footerScalable) { footer_max_height-= 72.0f; if (s->calcArg.keytopAsFloating) footer_max_height+= 14.0f; } if (keytop) { float image_width = 1280.0f; float width = s->calcArg.keytopScaleX*image_width; float scale_y = s->calcArg.keytopScaleY; float tmp_y = (keytop_max_height - 360.0f)*scale_y; keytop->x = lroundf(((width*-0.5f)+640.0f) + (s->calcArg.keytopTranslateX*image_width)); keytop->y = lroundf((360.0f - tmp_y) - (s->calcArg.keytopTranslateY * 720.0f)); keytop->width = lroundf(width); keytop->height = lroundf(footer_max_height*scale_y); } if (footer) { footer->x = 0; if (s->calcArg.footerScalable) { footer->y = 0; footer->width = 0; footer->height = 0; } else { footer->y = 648; footer->width = 1280; footer->height = 72; } } return s->calcArg.footerScalable ? 1 : 2; } bool swkbdInlineIsUsedTouchPointByKeyboard(SwkbdInline* s, s32 x, s32 y) { SwkbdRect keytop={0}; SwkbdRect footer={0}; s32 ret = swkbdInlineGetTouchRectangles(s, &keytop, &footer); if (keytop.x <= x && keytop.x + keytop.width >= x && keytop.y <= y && keytop.y + keytop.height >= y) return true; if (ret >= 2) { if (footer.x <= x && footer.x + footer.width >= x && footer.y <= y && footer.y + footer.height >= y) return true; } return false; } static void _swkbdProcessReply(SwkbdInline* s, SwkbdReplyType ReplyType, size_t size) { size_t stringendoff_utf8 = 0x7D4; size_t stringendoff_utf16 = 0x3EC; void* argdataend_utf8 = &s->interactive_tmpbuf[stringendoff_utf8]; void* argdataend_utf16 = &s->interactive_tmpbuf[stringendoff_utf16]; char* strdata = (char*)s->interactive_tmpbuf; memset(s->interactive_strbuf, 0, s->interactive_strbuf_size); if ((ReplyType==SwkbdReplyType_ChangedString && size != 0x3FC) || (ReplyType==SwkbdReplyType_ChangedStringUtf8 && size != 0x7E4)) return; if ((ReplyType==SwkbdReplyType_MovedCursor && size != 0x3F4) || (ReplyType==SwkbdReplyType_MovedCursorUtf8 && size != 0x7DC)) return; if ((ReplyType==SwkbdReplyType_ChangedStringV2 && size != 0x3FC+0x1) || (ReplyType==SwkbdReplyType_ChangedStringUtf8V2 && size != 0x7E4+0x1)) return; if ((ReplyType==SwkbdReplyType_MovedCursorV2 && size != 0x3F4+0x1) || (ReplyType==SwkbdReplyType_MovedCursorUtf8V2 && size != 0x7DC+0x1)) return; if ((ReplyType==SwkbdReplyType_DecidedEnter && size != 0x3F0) || (ReplyType==SwkbdReplyType_DecidedEnterUtf8 && size != 0x7D8)) return; if (ReplyType==SwkbdReplyType_MovedTab && size != 0x3F4) return; if (ReplyType==SwkbdReplyType_ChangedString || ReplyType==SwkbdReplyType_ChangedStringV2 || ReplyType==SwkbdReplyType_MovedCursor || ReplyType==SwkbdReplyType_MovedCursorV2 || ReplyType==SwkbdReplyType_MovedTab || ReplyType==SwkbdReplyType_DecidedEnter) { _swkbdConvertToUTF8(s->interactive_strbuf, (u16*)strdata, s->interactive_strbuf_size-1); strdata = s->interactive_strbuf; } switch(ReplyType) { case SwkbdReplyType_FinishedInitialize: if (s->finishedInitializeCb) s->finishedInitializeCb(); break; case SwkbdReplyType_DecidedCancel: if (s->decidedCancelCb) s->decidedCancelCb(); break; case SwkbdReplyType_ChangedString: case SwkbdReplyType_ChangedStringUtf8: if (s->changedStringCb) { if (ReplyType==SwkbdReplyType_ChangedString) s->changedStringCb(strdata, (SwkbdChangedStringArg*)argdataend_utf16); if (ReplyType==SwkbdReplyType_ChangedStringUtf8) s->changedStringCb(strdata, (SwkbdChangedStringArg*)argdataend_utf8); } break; case SwkbdReplyType_ChangedStringV2: case SwkbdReplyType_ChangedStringUtf8V2: if (s->changedStringV2Cb) { if (ReplyType==SwkbdReplyType_ChangedStringV2) s->changedStringV2Cb(strdata, (SwkbdChangedStringArg*)argdataend_utf16, s->interactive_tmpbuf[size-1]==0); if (ReplyType==SwkbdReplyType_ChangedStringUtf8V2) s->changedStringV2Cb(strdata, (SwkbdChangedStringArg*)argdataend_utf8, s->interactive_tmpbuf[size-1]==0); } break; case SwkbdReplyType_MovedCursor: case SwkbdReplyType_MovedCursorUtf8: if (s->movedCursorCb) { if (ReplyType==SwkbdReplyType_MovedCursor) s->movedCursorCb(strdata, (SwkbdMovedCursorArg*)argdataend_utf16); if (ReplyType==SwkbdReplyType_MovedCursorUtf8) s->movedCursorCb(strdata, (SwkbdMovedCursorArg*)argdataend_utf8); } break; case SwkbdReplyType_MovedCursorV2: case SwkbdReplyType_MovedCursorUtf8V2: if (s->movedCursorV2Cb) { if (ReplyType==SwkbdReplyType_MovedCursorV2) s->movedCursorV2Cb(strdata, (SwkbdMovedCursorArg*)argdataend_utf16, s->interactive_tmpbuf[size-1]==0); if (ReplyType==SwkbdReplyType_MovedCursorUtf8V2) s->movedCursorV2Cb(strdata, (SwkbdMovedCursorArg*)argdataend_utf8, s->interactive_tmpbuf[size-1]==0); } break; case SwkbdReplyType_MovedTab: if (s->movedTabCb) s->movedTabCb(strdata, (SwkbdMovedTabArg*)argdataend_utf16); break; case SwkbdReplyType_DecidedEnter: case SwkbdReplyType_DecidedEnterUtf8: if (s->decidedEnterCb) { if (ReplyType==SwkbdReplyType_DecidedEnter) s->decidedEnterCb(strdata, (SwkbdDecidedEnterArg*)argdataend_utf16); if (ReplyType==SwkbdReplyType_DecidedEnterUtf8) s->decidedEnterCb(strdata, (SwkbdDecidedEnterArg*)argdataend_utf8); } break; case SwkbdReplyType_UnsetCustomizeDic: case SwkbdReplyType_UnsetCustomizedDictionaries: if (s->dicCustomInitialized) { appletStorageCloseTmem(&s->dicStorage); s->dicCustomInitialized = false; s->customizedDictionariesInitialized = false; } break; case SwkbdReplyType_ReleasedUserWordInfo: if (s->releasedUserWordInfoCb) s->releasedUserWordInfoCb(); if (s->wordInfoInitialized) { appletStorageCloseTmem(&s->wordInfoStorage); s->wordInfoInitialized = false; } break; default: break; } } static Result _swkbdGetReplies(SwkbdInline* s) { Result rc=0; AppletStorage storage; SwkbdReplyType ReplyType=0; while(R_SUCCEEDED(appletHolderPopInteractiveOutData(&s->holder, &storage))) { s64 tmpsize=0; rc = appletStorageGetSize(&storage, &tmpsize); memset(s->interactive_tmpbuf, 0, s->interactive_tmpbuf_size); if (R_SUCCEEDED(rc) && (tmpsize < 8 || tmpsize-8 > s->interactive_tmpbuf_size)) rc = MAKERESULT(Module_Libnx, LibnxError_BadInput); if (R_SUCCEEDED(rc)) rc = appletStorageRead(&storage, 0x0, &s->state, sizeof(s->state)); if (R_SUCCEEDED(rc)) rc = appletStorageRead(&storage, 0x4, &ReplyType, sizeof(u32)); if (R_SUCCEEDED(rc) && tmpsize >= 8) rc = appletStorageRead(&storage, 0x8, s->interactive_tmpbuf, tmpsize-8); appletStorageClose(&storage); if (R_FAILED(rc)) break; _swkbdProcessReply(s, ReplyType, tmpsize-8); } return rc; } Result swkbdInlineUpdate(SwkbdInline* s, SwkbdState* out_state) { Result rc=0; u8 fadetype=0; if (s->calcArg.footerScalable) { swkbdInlineSetFooterBgAlpha(s, s->calcArg.keytopBgAlpha); fadetype = s->calcArg.keytopBgAlpha != 1.0f; } else { fadetype = 2; } swkbdInlineSetInputModeFadeType(s, fadetype); if (_swkbdInlineHandleFinished(s)) { if (out_state) *out_state = s->state; 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; } rc = _swkbdGetReplies(s); if (out_state) *out_state = s->state; return rc; } static inline Result _swkbdSendRequestV2Flag(SwkbdInline* s, SwkbdRequestCommand req, bool flag) { if (s->version < 0x8000D) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); u8 tmp = flag!=0; return _swkbdSendRequest(s, req, &tmp, sizeof(tmp)); } void swkbdInlineSetFinishedInitializeCallback(SwkbdInline* s, VoidFn cb) { s->finishedInitializeCb = cb; } void swkbdInlineSetDecidedCancelCallback(SwkbdInline* s, VoidFn cb) { s->decidedCancelCb = cb; } void swkbdInlineSetChangedStringCallback(SwkbdInline* s, SwkbdChangedStringCb cb) { s->changedStringCb = cb; s->changedStringV2Cb = NULL; _swkbdSendRequestV2Flag(s, SwkbdRequestCommand_SetChangedStringV2Flag, false); } void swkbdInlineSetChangedStringV2Callback(SwkbdInline* s, SwkbdChangedStringV2Cb cb) { if (R_FAILED(_swkbdSendRequestV2Flag(s, SwkbdRequestCommand_SetChangedStringV2Flag, cb!=NULL))) return; s->changedStringV2Cb = cb; } void swkbdInlineSetMovedCursorCallback(SwkbdInline* s, SwkbdMovedCursorCb cb) { s->movedCursorCb = cb; s->movedCursorV2Cb = NULL; _swkbdSendRequestV2Flag(s, SwkbdRequestCommand_SetMovedCursorV2Flag, false); } void swkbdInlineSetMovedCursorV2Callback(SwkbdInline* s, SwkbdMovedCursorV2Cb cb) { if (R_FAILED(_swkbdSendRequestV2Flag(s, SwkbdRequestCommand_SetMovedCursorV2Flag, cb!=NULL))) return; s->movedCursorV2Cb = cb; } void swkbdInlineSetMovedTabCallback(SwkbdInline* s, SwkbdMovedTabCb cb) { s->movedTabCb = cb; } void swkbdInlineSetDecidedEnterCallback(SwkbdInline* s, SwkbdDecidedEnterCb cb) { s->decidedEnterCb = cb; } void swkbdInlineSetReleasedUserWordInfoCallback(SwkbdInline* s, VoidFn cb) { s->releasedUserWordInfoCb = cb; } static void _swkbdInlineUpdateAppearFlags(SwkbdInline* s) { u32 mask = 0x10000000; u32 tmp = s->calcArg.appearArg.flags; if (!s->directionalButtonAssignFlag) tmp &= ~mask; if (s->directionalButtonAssignFlag) tmp |= mask; mask = 0x10000; if (!s->calcArg.triggerFlag) tmp &= ~mask; // Official sw doesn't clear this bitmask. if (s->calcArg.triggerFlag) tmp |= mask; s->calcArg.appearArg.flags = tmp; } void swkbdInlineAppearEx(SwkbdInline* s, const SwkbdAppearArg* arg, u8 trigger) { memcpy(&s->calcArg.appearArg, arg, sizeof(SwkbdAppearArg)); if (s->version < 0x6000B) trigger=0; // [6.0.0+] s->calcArg.trigger = trigger; s->calcArg.triggerFlag = s->calcArg.trigger!=0; if (hosversionAtLeast(10,0,0) && (arg->unk_x29 == 1 || arg->unk_x29 == 2)) { s->calcArg.keytopAsFloating = false; s->calcArg.footerScalable = true; s->calcArg.flags |= 0x200; } _swkbdInlineUpdateAppearFlags(s); s->calcArg.flags = (s->calcArg.flags & ~0x80) | 0x4; } void swkbdInlineAppear(SwkbdInline* s, const SwkbdAppearArg* arg) { swkbdInlineAppearEx(s, arg, 0); } void swkbdInlineDisappear(SwkbdInline* s) { s->calcArg.flags = (s->calcArg.flags & ~0x4) | 0x80; } void swkbdInlineMakeAppearArg(SwkbdAppearArg* arg, SwkbdType type) { memset(arg, 0, sizeof(SwkbdAppearArg)); arg->stringLenMax = -1; arg->stringLenMin = -1; arg->unk_x30 = 1; arg->type = type; } void swkbdInlineAppearArgSetOkButtonText(SwkbdAppearArg* arg, const char* str) { _swkbdConvertToUTF16ByteSize(arg->okButtonText, str, sizeof(arg->okButtonText)); } void swkbdInlineAppearArgSetLeftButtonText(SwkbdAppearArg* arg, const char* str) { _swkbdConvertToUTF16(&arg->leftButtonText, str, 1); } void swkbdInlineAppearArgSetRightButtonText(SwkbdAppearArg* arg, const char* str) { _swkbdConvertToUTF16(&arg->rightButtonText, str, 1); } void swkbdInlineSetVolume(SwkbdInline* s, float volume) { if (s->calcArg.volume == volume) return; s->calcArg.volume = volume; s->calcArg.flags |= 0x2; } void swkbdInlineSetInputText(SwkbdInline* s, const char* str) { _swkbdConvertToUTF16ByteSize(s->calcArg.inputText, str, sizeof(s->calcArg.inputText)); s->calcArg.flags |= 0x8; } void swkbdInlineSetCursorPos(SwkbdInline* s, s32 pos) { s->calcArg.cursorPos = pos; s->calcArg.flags |= 0x10; } Result swkbdInlineSetUserWordInfo(SwkbdInline* s, const SwkbdDictWord *input, s32 entries) { Result rc=0; size_t size=0; s32 maxwords = 0x3e8; if (s->version >= 0x8000D) maxwords = 0x1388; if (s->state > SwkbdState_Initialized || s->wordInfoInitialized) return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); if (entries < 0 || entries > maxwords) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (input==NULL || entries==0) return swkbdInlineUnsetUserWordInfo(s); size = size*sizeof(SwkbdDictWord) + 0x8; size = (size + 0xfff) & ~0xfff; rc = appletCreateTransferMemoryStorage(&s->wordInfoStorage, NULL, size, true); if (R_FAILED(rc)) return rc; u32 req = SwkbdRequestCommand_SetUserWordInfo; rc = appletStorageWrite(&s->wordInfoStorage, 0x0, &req, sizeof(req)); if (R_SUCCEEDED(rc)) rc = appletStorageWrite(&s->wordInfoStorage, 0x4, &entries, sizeof(entries)); if (R_SUCCEEDED(rc) && entries>0) rc = appletStorageWrite(&s->wordInfoStorage, 0x8, input, sizeof(SwkbdDictWord) * entries); if (R_SUCCEEDED(rc)) rc = appletHolderPushInteractiveInData(&s->holder, &s->wordInfoStorage); if (R_FAILED(rc)) appletStorageCloseTmem(&s->wordInfoStorage); if (R_SUCCEEDED(rc)) { s->wordInfoInitialized = true; s->calcArg.flags &= ~0x400; } return rc; } Result swkbdInlineUnsetUserWordInfo(SwkbdInline* s) { if (s->state > SwkbdState_Initialized) return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); if (!s->wordInfoInitialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); s->calcArg.flags |= 0x400; return 0; } static void _swkbdInlineSetBoolFlag(SwkbdInline* s, u8* arg, bool flag, u64 bitmask) { u8 tmp = flag!=0; if (*arg == tmp) return; *arg = tmp; s->calcArg.flags |= bitmask; } static void _swkbdInlineSetBoolDisableFlag(SwkbdInline* s, u8* arg, bool flag, u64 bitmask) { _swkbdInlineSetBoolFlag(s, arg, !flag, bitmask); } void swkbdInlineSetUtf8Mode(SwkbdInline* s, bool flag) { _swkbdInlineSetBoolFlag(s, &s->calcArg.utf8Mode, flag, 0x20); } Result swkbdInlineSetCustomizeDic(SwkbdInline* s, void* buffer, size_t size, SwkbdCustomizeDicInfo *info) { Result rc=0; if (s->state > SwkbdState_Initialized || s->dicCustomInitialized) return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); rc = appletCreateHandleStorageTmem(&s->dicStorage, buffer, size); if (R_FAILED(rc)) return rc; rc = appletHolderPushInteractiveInData(&s->holder, &s->dicStorage); if (R_FAILED(rc)) { appletStorageCloseTmem(&s->dicStorage); return rc; } s->dicCustomInitialized = true; rc = _swkbdSendRequest(s, SwkbdRequestCommand_SetCustomizeDic, info, sizeof(SwkbdCustomizeDicInfo)); return rc; } void swkbdInlineUnsetCustomizeDic(SwkbdInline* s) { if (s->state > SwkbdState_Initialized || !s->dicCustomInitialized) return; s->calcArg.flags |= 0x40; } Result swkbdInlineSetCustomizedDictionaries(SwkbdInline* s, const SwkbdCustomizedDictionarySet *dic) { Result rc=0; u8 tmpdata[0xd0]; if (s->state > SwkbdState_Initialized || s->dicCustomInitialized) return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); if (s->version < 0x6000B) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); // [6.0.0+] rc = appletCreateHandleStorageTmem(&s->dicStorage, dic->buffer, dic->buffer_size); if (R_FAILED(rc)) return rc; rc = appletHolderPushInteractiveInData(&s->holder, &s->dicStorage); if (R_FAILED(rc)) { appletStorageCloseTmem(&s->dicStorage); return rc; } s->dicCustomInitialized = true; s->customizedDictionariesInitialized = true; memcpy(tmpdata, dic, sizeof(*dic)); tmpdata[0xce] = 0; tmpdata[0xcf] = 0; rc = _swkbdSendRequest(s, SwkbdRequestCommand_SetCustomizedDictionaries, tmpdata, sizeof(tmpdata)); return rc; } Result swkbdInlineUnsetCustomizedDictionaries(SwkbdInline* s) { Result rc=0; if (s->state > SwkbdState_Initialized || !s->dicCustomInitialized || !s->customizedDictionariesInitialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (s->version < 0x6000B) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); // [6.0.0+] rc = _swkbdSendRequest(s, SwkbdRequestCommand_UnsetCustomizedDictionaries, NULL, 0); return rc; } void swkbdInlineSetInputModeFadeType(SwkbdInline* s, u8 type) { if (s->calcArg.inputModeFadeType == type) return; s->calcArg.inputModeFadeType = type; s->calcArg.flags |= 0x100; } void swkbdInlineSetAlphaEnabledInInputMode(SwkbdInline* s, bool flag) { _swkbdInlineSetBoolFlag(s, &s->calcArg.alphaEnabledInInputMode, flag, 0x100); } void swkbdInlineSetKeytopBgAlpha(SwkbdInline* s, float alpha) { _swkbdClampFloat(&alpha); if (s->calcArg.keytopBgAlpha == alpha) return; s->calcArg.keytopBgAlpha = alpha; s->calcArg.flags |= 0x100; } void swkbdInlineSetFooterBgAlpha(SwkbdInline* s, float alpha) { _swkbdClampFloat(&alpha); if (s->calcArg.footerBgAlpha == alpha) return; s->calcArg.footerBgAlpha = alpha; s->calcArg.flags |= 0x100; } void swkbdInlineSetKeytopAsFloating(SwkbdInline* s, bool flag) { if (s->state > SwkbdState_Initialized) return; _swkbdInlineSetBoolFlag(s, &s->calcArg.keytopAsFloating, flag, 0x200); } void swkbdInlineSetFooterScalable(SwkbdInline* s, bool flag) { _swkbdInlineSetBoolFlag(s, &s->calcArg.footerScalable, flag, 0x200); } void swkbdInlineSetTouchFlag(SwkbdInline* s, bool flag) { _swkbdInlineSetBoolDisableFlag(s, &s->calcArg.disableTouch, flag, 0x200); } static void _swkbdInlineSetKeytopScale(SwkbdInline* s, float x, float y) { if (s->calcArg.keytopScaleX == x && s->calcArg.keytopScaleY == y) return; s->calcArg.keytopScaleX = x; s->calcArg.keytopScaleY = y; s->calcArg.flags |= 0x200; } static void _swkbdInlineSetBalloonScale(SwkbdInline* s, float scale) { if (s->state > SwkbdState_Initialized || s->calcArg.balloonScale == scale) return; s->calcArg.balloonScale = scale; s->calcArg.flags |= 0x200; } void swkbdInlineSetKeytopScale(SwkbdInline* s, float scale) { _swkbdInlineSetKeytopScale(s, scale, scale); scale = fminf(scale + 0.15f, 1.0f); _swkbdInlineSetBalloonScale(s, scale); } void swkbdInlineSetKeytopTranslate(SwkbdInline* s, float x, float y) { if (s->calcArg.keytopTranslateX == x && s->calcArg.keytopTranslateY == y) return; s->calcArg.keytopTranslateX = x; s->calcArg.keytopTranslateY = y; s->calcArg.flags |= 0x200; } void swkbdInlineSetHardwareKeyboardFlag(SwkbdInline* s, bool flag) { _swkbdInlineSetBoolDisableFlag(s, &s->calcArg.disableHardwareKeyboard, flag, 0x800); } void swkbdInlineSetDirectionalButtonAssignFlag(SwkbdInline* s, bool flag) { if (s->version < 0x40008) return; // [4.0.0+] s->directionalButtonAssignFlag = flag; _swkbdInlineUpdateAppearFlags(s); s->calcArg.flags |= 0x1000; } void swkbdInlineSetSeGroup(SwkbdInline* s, u8 seGroup, bool flag) { if (s->version < 0x50009) return; // [5.0.0+] s->calcArg.seGroup = seGroup; s->calcArg.flags |= flag ? 0x2000 : 0x4000; } void swkbdInlineSetBackspaceFlag(SwkbdInline* s, bool flag) { if (s->version < 0x50009) return; // [5.0.0+] s->calcArg.enableBackspace = flag!=0; s->calcArg.flags |= 0x8000; }