Difference between revisions of "Software Keyboard"
(49 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
The software keyboard (swkbd) expects to be passed three [[AM_services#IStorage|IStorage]]s. See also [[AM_services#Library_Applets]]. | The software keyboard (swkbd) expects to be passed three [[AM_services#IStorage|IStorage]]s. See also [[AM_services#Library_Applets]]. | ||
− | The below is for normal swkbd usage | + | The below is for normal swkbd usage, see the [[#InlineKeyboard]] section for InlineKeyboard. |
− | + | With version 0x6000B+ after pushing all other storage: when [[#CustomizedDictionarySet]] was setup where buffer addr/size is set and total_entries is non-zero, [[Applet_Manager_services#CreateHandleStorage]] will be used to create TransferMemory storage which is then pushed. | |
+ | |||
+ | = Library Applet Versions = | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
Line 23: | Line 25: | ||
|- | |- | ||
| [5.0.0+] || 0x50009 | | [5.0.0+] || 0x50009 | ||
+ | |- | ||
+ | | [6.0.0+] || 0x6000B | ||
+ | |- | ||
+ | | [8.0.0+] || 0x8000D | ||
+ | |} | ||
+ | |||
+ | = KeyboardConfig = | ||
+ | The second IStorage passed to this applet should contain the configuration for the keyboard. | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x4 || [[#KeyboardMode]] | ||
+ | |- | ||
+ | | 0x4 || 0x12 || OkText | ||
+ | |- | ||
+ | | 0x16 || 0x2 || LeftOptionalSymbolKey | ||
+ | |- | ||
+ | | 0x18 || 0x2 || RightOptionalSymbolKey | ||
+ | |- | ||
+ | | 0x1A || 0x1 || IsPredictionEnabled | ||
+ | |- | ||
+ | | 0x1C || 0x4 || [[#InvalidCharFlag]] | ||
+ | |- | ||
+ | | 0x20 || 0x4 || [[#InitialCursorPos]] | ||
+ | |- | ||
+ | | 0x24 || 0x82 || HeaderText | ||
+ | |- | ||
+ | | 0xA6 || 0x102 || SubText | ||
+ | |- | ||
+ | | 0x1A8 || 0x202 || GuideText | ||
+ | |- | ||
+ | | 0x3AC || 0x4 || TextMaxLength | ||
+ | |- | ||
+ | | 0x3B0 || 0x4 || TextMinLength | ||
+ | |- | ||
+ | | 0x3B4 || 0x4 || [[#PasswordMode]] | ||
+ | |- | ||
+ | | 0x3B8 || 0x4 || [[#InputFormMode]] | ||
+ | |- | ||
+ | | 0x3BC || 0x1 || IsUseNewLine | ||
+ | |- | ||
+ | | 0x3BD || 0x1 || IsUseUtf8 | ||
+ | |- | ||
+ | | 0x3BE || 0x1 || IsUseBlurBackground | ||
+ | |- | ||
+ | | 0x3C0 || 0x4 || InitialStringOffset | ||
+ | |- | ||
+ | | 0x3C4 || 0x4 || InitialStringLength | ||
+ | |- | ||
+ | | 0x3C8 || 0x4 || UserDictionaryOffset | ||
+ | |- | ||
+ | | 0x3CC || 0x4 || UserDictionaryNum | ||
+ | |- | ||
+ | | 0x3D0 || 0x1 || IsUseTextCheck | ||
+ | |- | ||
+ | | 0x3D1 || 0x7 || Reserved | ||
+ | |- | ||
+ | | 0x3D8 || 0x8 || [[#TextCheckCallback]] | ||
+ | |- | ||
+ | | 0x3E0 || 0x20 || SeparateTextPos | ||
+ | |} | ||
+ | |||
+ | Version 0x6000B+: | ||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x3D4 || 0x20 || SeparateTextPos | ||
+ | |- | ||
+ | | 0x3F4 || 0xC0 || [[#CustomizedDictionarySet|CustomizedDicInfoList]] | ||
+ | |- | ||
+ | | 0x4B4 || 0x1 || CustomizedDicCount | ||
+ | |- | ||
+ | | 0x4B5 || 0x1 || [8.0.0+] IsCancelButtonDisabled | ||
+ | |- | ||
+ | | 0x4B6 || 0xD || Reserved | ||
+ | |- | ||
+ | | 0x4C3 || 0x1 || [8.0.0+] Trigger | ||
+ | |- | ||
+ | | 0x4C4 || 0x4 || Reserved | ||
+ | |} | ||
+ | |||
+ | Struct sizes: | ||
+ | * Initial version: 0x3E0-bytes. | ||
+ | * Version 0x30007+: 0x400-bytes. | ||
+ | * Version 0x6000B+: 0x4C8-bytes. | ||
+ | |||
+ | Each entry in the user dictionary is 100 bytes long. | ||
+ | |||
+ | TextMaxLength: When the input is too long, swkbd will stop accepting more input until text is deleted via the B button (Backspace). | ||
+ | |||
+ | = WorkBuffer = | ||
+ | This is the third IStorage passed to this applet. It is a transfer memory storage. The transfer memory should have size 0x1000 (0xd000 in certain cases) and permissions 0. | ||
+ | |||
+ | The layout of the work buffer doesn't seem to matter as long as the offsets in the [[#KeyboardConfig]] are adjusted, but official code lays it out like this. | ||
+ | |||
+ | Prior to version 0x5, offset 0x0 size 0x14 is a header (size 0x10 for version <=0x2). | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x14 || Unknown || UTF-16 initial string | ||
+ | |- | ||
+ | | 0x7E8 || Unknown || User dictionary | ||
|} | |} | ||
− | == KeyboardConfig == | + | = TextCheckCallback = |
+ | If text checking is enabled in [[#KeyboardConfig]], text will be checked when the submit button is pressed. First, swkbd sends the text via PushInteractiveOutData. Normally size 0x7D4 is allocated for the string. | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x8 || Buffer size | ||
+ | |- | ||
+ | | 0x8 || Variable || UTF-16 text | ||
+ | |} | ||
− | The | + | The application then has an opportunity to validate or reject the text. It creates a new IStorage, writes the response to it, and sends it via PushInteractiveInData. This storage is 0x7D8-bytes. |
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x4 || [[#TextCheckResult]] | ||
+ | |- | ||
+ | | 0x4 || Variable || UTF-16 error message (shown in a dialog box) | ||
+ | |} | ||
+ | |||
+ | = Output = | ||
+ | When either the submit button is pressed and input has been validated, or the user cancels the text entry, swkbd will push its response and exit. The response IStorage has the following format. This storage is 0x7D8-bytes. | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x4 || Result code (0 = OK, 1 = Cancel) | ||
+ | |- | ||
+ | | 0x4 || Variable || UTF-16 text | ||
+ | |} | ||
+ | |||
+ | = InlineKeyboard = | ||
+ | This doesn't run in the foreground and has completely different input/output [[AM_services#IStorage|IStorage]]s. This is essentially an asynchronous version of the regular swkbd. Whether it displays on the screen is controlled by the user-process. The user-process can also get the gfx data via [[Display_services]]. InlineKeyboard was added with 2.0.0, however it wasn't added to sdk-nso until the version corresponding to sysver 3.x (even though 2.0.0 system titles use it). | ||
+ | |||
+ | == InitializeArg == | ||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x4 || Unknown, normally 0. | ||
+ | |- | ||
+ | | 0x4 || 0x1 || Controls the LibraryAppletMode when launching the applet. Non-zero indicates LibraryAppletMode=0x1, otherwise LibraryAppletMode=0x3. | ||
+ | |- | ||
+ | | 0x5 || 0x1 || Set to 0x1 with [5.0.0+] in a separate init func by official sw, originally set to value 0. | ||
+ | |- | ||
+ | | 0x6 || 0x2 || Padding | ||
+ | |} | ||
+ | |||
+ | == AppearArg == | ||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x4 || [[#KeyboardMode]] | ||
+ | |- | ||
+ | | 0x4 || 0x12 || OkText | ||
+ | |- | ||
+ | | 0x16 || 2 || LeftOptionalSymbolKey | ||
+ | |- | ||
+ | | 0x18 || 2 || RightOptionalSymbolKey | ||
+ | |- | ||
+ | | 0x1A || 0x1 || IsPredictionEnabled | ||
+ | |- | ||
+ | | 0x1B || 0x1 || IsCancelButtonDisabled | ||
+ | |- | ||
+ | | 0x1C || 0x4 || [[#InvalidCharFlag]] | ||
+ | |- | ||
+ | | 0x20 || 0x4 || TextMaxLength | ||
+ | |- | ||
+ | | 0x24 || 0x4 || TextMinLength | ||
+ | |- | ||
+ | | 0x28 || 0x1 || IsUseNewLine | ||
+ | |- | ||
+ | | 0x29 || 0x1 || [10.0.0+] [[#MiniaturizationMode]] | ||
+ | |- | ||
+ | | 0x2A || 0x1 || Reserved | ||
+ | |- | ||
+ | | 0x2B || 0x1 || Reserved | ||
+ | |- | ||
+ | | 0x2C || 0x4 || [4.0.0+] [[#InvalidButtonFlag]] | ||
+ | |- | ||
+ | | 0x30 || 0x1 || IsUseSaveData | ||
+ | |- | ||
+ | | 0x31 || 0x7 || Reserved | ||
+ | |- | ||
+ | | 0x38 || 0x10 || [[Account_services#Uid|Uid]] | ||
+ | |- | ||
+ | | 0x48 || 0x8 || StartSamplingNumber | ||
+ | |- | ||
+ | | 0x50 || 0x20 || Reserved | ||
+ | |} | ||
+ | |||
+ | The above struct is cleared to 0 during initialization, besides the fields specified otherwise. | ||
+ | |||
+ | [6.0.0+] Flags bitmask 0x10000 is set when [[#CalcArg]] trigger is set. | ||
+ | |||
+ | == CalcArg == | ||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Flags bitmask || Description | ||
+ | |- | ||
+ | | 0x0 || 0x4 || || Set to 0x30000. | ||
+ | |- | ||
+ | | 0x4 || 0x2 || || Size of this struct. | ||
+ | |- | ||
+ | | 0x6 || 0x1 || || Unknown, set to value 0x1. | ||
+ | |- | ||
+ | | 0x7 || 0x1 || || Unknown, set to value 0x1. | ||
+ | |- | ||
+ | | 0x8 || 0x8 || || Flags | ||
+ | |- | ||
+ | | 0x10 || 0x8 || 0x1 || [[#InitializeArg]] | ||
+ | |- | ||
+ | | 0x18 || 0x4 || 0x2 || float volume | ||
+ | |- | ||
+ | | 0x1C || 0x4 || 0x10 || s32 cursorPos | ||
+ | |- | ||
+ | | 0x20 || 0x48 || || [[#AppearArg]] | ||
+ | |- | ||
+ | | 0x68 || 0x3F4 || 0x8 || InputText UTF-16 string | ||
+ | |- | ||
+ | | 0x45C || 0x1 || 0x20 || utf8Mode | ||
+ | |- | ||
+ | | 0x45D || 0x1 || || Unknown | ||
+ | |- | ||
+ | | 0x45E || 0x1 || 0x8000 || [5.0.0+] enableBackspace | ||
+ | |- | ||
+ | | 0x45F || 0x3 || || Unknown | ||
+ | |- | ||
+ | | 0x462 || 0x1 || 0x200 || keytopAsFloating | ||
+ | |- | ||
+ | | 0x463 || 0x1 || 0x100 || footerScalable | ||
+ | |- | ||
+ | | 0x464 || 0x1 || 0x100 || alphaEnabledInInputMode | ||
+ | |- | ||
+ | | 0x465 || 0x1 || 0x100 || inputModeFadeType | ||
+ | |- | ||
+ | | 0x466 || 0x1 || 0x200 || disableTouch | ||
+ | |- | ||
+ | | 0x467 || 0x1 || 0x800 || disableHardwareKeyboard ([1.0.0-9.2.0] disableUSBKeyboard) | ||
+ | |- | ||
+ | | 0x468 || 0x5 || || Unknown | ||
+ | |- | ||
+ | | 0x46D || 0x2 || || | ||
+ | |- | ||
+ | | 0x46F || 0x1 || || | ||
+ | |- | ||
+ | | 0x470 || 0x4 || 0x200 || float keytopScale0 | ||
+ | |- | ||
+ | | 0x474 || 0x4 || 0x200 || float keytopScale1 | ||
+ | |- | ||
+ | | 0x478 || 0x4 || 0x200 || float keytopTranslate0 | ||
+ | |- | ||
+ | | 0x47C || 0x4 || 0x200 || float keytopTranslate1 | ||
+ | |- | ||
+ | | 0x480 || 0x4 || 0x100 || float keytopBgAlpha | ||
+ | |- | ||
+ | | 0x484 || 0x4 || 0x100 || float footerBgAlpha | ||
+ | |- | ||
+ | | 0x488 || 0x4 || 0x200 || float balloonScale | ||
+ | |- | ||
+ | | 0x48C || 0x4 || || float, unknown | ||
+ | |- | ||
+ | | 0x490 || 0xC || || | ||
+ | |- | ||
+ | | 0x49C || 0x1 || Enable=0x2000, disable=0x4000. || [5.0.0+] SeGroup (sound effect) | ||
+ | |- | ||
+ | | 0x49D || 0x1 || || [6.0.0+] u8 triggerFlag, for [[#AppearArg]]. Enables using the trigger field when set, this is only set when trigger is non-zero. | ||
+ | |- | ||
+ | | 0x49E || 0x2 || || [6.0.0+] u8 trigger, for [[#AppearArg]]. Official sw currently only uses value 0. | ||
+ | |- | ||
+ | | 0x49F || 0x1 || || Padding | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | This is 0x4A0-bytes. | ||
+ | |||
+ | All floats except for keytopTranslate0/keytopTranslate1 are initialized to value 1.0f. | ||
+ | |||
+ | The applet-specific IStorage for data input is the [[#InitializeArg]] within this struct. | ||
+ | |||
+ | disableHardwareKeyboard: On [10.0.0+] there's sdknso user-facing funcs for both Hardware/USB, which call the same Impl func that has Hardware in the name. | ||
+ | |||
+ | == Runtime == | ||
+ | Once the applet is running, official sw can call a func which does the following: | ||
+ | * Checks whether the applet exited via an event, then handles exit if so and returns. | ||
+ | * Otherwise: | ||
+ | ** If the Flags field in the [[#CalcArg]] state is non-zero, sends a Calc [[#Request]] then clears the Flags field. | ||
+ | ** Enters a loop which pops each applet Interactive output IStorage, reads 2 u32s from the [[#Reply]] storage and processes the reply. | ||
+ | ** The u32 from offset 0x0 from the last processed storage is then returned as the retval. | ||
+ | |||
+ | == Request == | ||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! RequestCommand || Data size || Description | ||
+ | |- | ||
+ | | 0x4 || 0x0 || Finalize | ||
+ | |- | ||
+ | | 0x6 || Varies || SetUserWordInfo | ||
+ | |- | ||
+ | | 0x7 || 0x70 || SetCustomizeDic | ||
+ | |- | ||
+ | | 0xA || 0x4A0 || Calc (data is [[#CalcArg]]) | ||
+ | |- | ||
+ | | 0xB || 0xD0 || SetCustomizedDictionaries (data is [[#CustomizedDictionarySet]] with an additional 2-bytes of padding | ||
+ | |- | ||
+ | | 0xC || 0x0 || UnsetCustomizedDictionaries | ||
+ | |- | ||
+ | | 0xD || 0x1 || [8.0.0+] Takes an input u8 bool which controls whether ChangedString*V2 or ChangedString* replies should be used. | ||
+ | |- | ||
+ | | 0xE || 0x1 || [8.0.0+] Takes an input u8 bool which controls whether MovedCursor*V2 or MovedCursor* replies should be used. | ||
+ | |} | ||
+ | |||
+ | Requests are sent via an applet Interactive input IStorage: the u32 at offset 0x0 is the RequestCommand, and the rest of the storage is the request-specific data. While swkbd supports other requests, official sw only uses requests 0x4, 0x7, and 0xA. | ||
+ | |||
+ | == Reply == | ||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! ReplyType || Data size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x1 || FinishedInitialize (reply data is ignored by the user-process) | ||
+ | |- | ||
+ | | 0x1 (default) || 0x0 || Official sw has no handling for this besides just closing the storage. | ||
+ | |- | ||
+ | | 0x2 || 0x3FC || ChangedString | ||
+ | |- | ||
+ | | 0x3 || 0x3F4 || MovedCursor | ||
+ | |- | ||
+ | | 0x4 || 0x3F4 || MovedTab | ||
+ | |- | ||
+ | | 0x5 || 0x3F0 || DecidedEnter | ||
+ | |- | ||
+ | | 0x6 || 0x0 || DecidedCancel | ||
+ | |- | ||
+ | | 0x7 || 0x7E4 || ChangedStringUtf8 | ||
+ | |- | ||
+ | | 0x8 || 0x7DC || MovedCursorUtf8 | ||
+ | |- | ||
+ | | 0x9 || 0x7D8 || DecidedEnterUtf8 | ||
+ | |- | ||
+ | | 0xA || 0x0 || UnsetCustomizeDic (official sw clears a flag related to CustomizeDic, then runs the same handling code as 0x1/default) | ||
+ | |- | ||
+ | | 0xB || 0x0 || ReleasedUserWordInfo | ||
+ | |- | ||
+ | | 0xC || 0x0 || [6.0.0+] UnsetCustomizedDictionaries (official sw handles this the same as UnsetCustomizeDic) | ||
+ | |- | ||
+ | | 0xD || 0x3FC + 0x1 || [8.0.0+] ChangedStringV2 | ||
+ | |- | ||
+ | | 0xE || 0x3F4 + 0x1 || [8.0.0+] MovedCursorV2 | ||
+ | |- | ||
+ | | 0xF || 0x7E4 + 0x1 || [8.0.0+] ChangedStringUtf8V2 | ||
+ | |- | ||
+ | | 0x10 || 0x7DC + 0x1 || [8.0.0+] MovedCursorUtf8V2 | ||
+ | |} | ||
+ | |||
+ | See [[#Runtime]]. In the storage, the first u32 is the State, while the second u32 is the ReplyType. The rest is the reply-specific data. | ||
+ | |||
+ | The replies with name "*Utf8" contain an UTF-8 string in the reply data, while the other replies contain an UTF-16 string. These are identical besides the string encoding. | ||
+ | |||
+ | Reply data format: | ||
+ | * ChangedString*: +0 = string. Last 0x10-bytes: 4 u32s, where the first one is the length of the string in characters, without NUL-terminator. The last u32 is cursorPos. The other 2 fields are s32s. | ||
+ | * MovedCursor*: +0 = string. Last 0x8-bytes: 2 u32s, where the first one is the stringLen, and the second one is cursorPos. | ||
+ | * DecidedEnter*: +0 = string. The last u32 is the stringLen. | ||
+ | * *V2: See above. Last byte: u8 bool, passed to the callback as <code>flag==0</code>. | ||
+ | |||
+ | = CustomizedDictionarySet = | ||
+ | This is "nn::swkbd::CustomizedDictionarySet". This struct is 0xCE-bytes. | ||
+ | |||
+ | This was added with [6.0.0+]. | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x8 || Dictionaries (0x1000-byte aligned buffer address) | ||
+ | |- | ||
+ | | 0x8 || 0x4 || DictionariesSize (0x1000-byte aligned buffer size) | ||
+ | |- | ||
+ | | 0xC || 0xC0 (0x18*4) || DicInfoList (array of 0x18 entries, where each entry is a [[#DictionaryInfo]]). | ||
+ | |- | ||
+ | | 0xCC || 0x2 || Count | ||
+ | |} | ||
+ | |||
+ | = DictionaryInfo = | ||
+ | This is "nn::swkbd::DictionaryInfo". | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x4 || Offset | ||
+ | |- | ||
+ | | 0x4 || 0x2 || Size | ||
+ | |- | ||
+ | | 0x6 || 0x2 || [[#DictionaryLang]] | ||
+ | |} | ||
+ | |||
+ | = DictionaryLang = | ||
+ | This is "nn::swkbd::DictionaryLang". | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
− | ! | + | ! Value || Description |
+ | |- | ||
+ | | 0 || Japanese | ||
+ | |- | ||
+ | | 1 || AmericanEnglish | ||
+ | |- | ||
+ | | 2 || CanadianFrench | ||
+ | |- | ||
+ | | 3 || LatinAmericanSpanish | ||
|- | |- | ||
− | + | | 4 || Reserved1 | |
|- | |- | ||
− | | | + | | 5 || BritishEnglish |
|- | |- | ||
− | | | + | | 6 || French |
|- | |- | ||
− | | | + | | 7 || German |
|- | |- | ||
− | | | + | | 8 || Spanish |
|- | |- | ||
− | | | + | | 9 || Italian |
|- | |- | ||
− | | | + | | 10 || Dutch |
|- | |- | ||
− | | | + | | 11 || Portuguese |
|- | |- | ||
− | | | + | | 12 || Russian |
|- | |- | ||
− | | | + | | 13 || Reserved2 |
|- | |- | ||
− | | | + | | 14 || SimplifiedChinesePinyin |
|- | |- | ||
− | | | + | | 15 || TraditionalChineseCangjie |
|- | |- | ||
− | | | + | | 16 || TraditionalChineseSimplifiedCangjie |
|- | |- | ||
− | | | + | | 17 || TraditionalChineseZhuyin |
|- | |- | ||
− | | | + | | 18 || Korean |
+ | |} | ||
+ | |||
+ | = KeyboardMode = | ||
+ | This is "nn::swkbd::KeyboardMode". | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
|- | |- | ||
− | | | + | ! Value || Description |
|- | |- | ||
− | | | + | | 0 || Full |
|- | |- | ||
− | | | + | | 1 || Numeric |
|- | |- | ||
− | | | + | | 2 || ASCII |
|- | |- | ||
− | | | + | | 3 || FullLatin |
|- | |- | ||
− | | | + | | 4 || Alphabet |
|- | |- | ||
− | | | + | | 5 || SimplifiedChinese |
|- | |- | ||
− | | | + | | 6 || TraditionalChinese |
|- | |- | ||
− | | | + | | 7 || Korean |
|- | |- | ||
− | + | | 8 || LanguageSet2 | |
|- | |- | ||
− | | | + | | 9 || LanguageSet2Latin |
|} | |} | ||
− | This | + | = State = |
+ | This is "nn::swkbd::State". | ||
− | + | {| class="wikitable" border="1" | |
+ | |- | ||
+ | ! Value || Description | ||
+ | |- | ||
+ | | 0 || None | ||
+ | |- | ||
+ | | 1 || Disappear | ||
+ | |- | ||
+ | | 2 || InAppear | ||
+ | |- | ||
+ | | 3 || Appear | ||
+ | |- | ||
+ | | 4 || InDisappear | ||
+ | |- | ||
+ | | 5 || SelectKeyTop | ||
+ | |- | ||
+ | | 6 || Miniaturized | ||
+ | |} | ||
+ | |||
+ | = CloseResult = | ||
+ | This is "nn::swkbd::CloseResult". | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Value || Description | ||
+ | |- | ||
+ | | 0 || Enter | ||
+ | |- | ||
+ | | 1 || Cancel | ||
+ | |} | ||
− | + | = PasswordMode = | |
+ | This is "nn::swkbd::PasswordMode". | ||
− | + | {| class="wikitable" border="1" | |
+ | |- | ||
+ | ! Value || Description | ||
+ | |- | ||
+ | | 0 || Show | ||
+ | |- | ||
+ | | 1 || Hide | ||
+ | |} | ||
− | == | + | = InitialCursorPos = |
+ | This is "nn::swkbd::InitialCursorPos". | ||
− | + | {| class="wikitable" border="1" | |
+ | |- | ||
+ | ! Value || Description | ||
+ | |- | ||
+ | | 0 || First | ||
+ | |- | ||
+ | | 1 || Last | ||
+ | |} | ||
− | + | = InputFormMode = | |
− | + | This is "nn::swkbd::InputFormMode". | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | == | + | {| class="wikitable" border="1" |
+ | |- | ||
+ | ! Value || Description | ||
+ | |- | ||
+ | | 0 || OneLine | ||
+ | |- | ||
+ | | 1 || MultiLine | ||
+ | |- | ||
+ | | 2 || Separate | ||
+ | |} | ||
− | This is | + | = TextCheckResult = |
+ | This is "nn::swkbd::TextCheckResult". | ||
− | + | {| class="wikitable" border="1" | |
+ | |- | ||
+ | ! Value || Description | ||
+ | |- | ||
+ | | 0 || Success | ||
+ | |- | ||
+ | | 1 || ShowFailureDialog | ||
+ | |- | ||
+ | | 2 || ShowConfirmDialog | ||
+ | |} | ||
− | + | = Preset = | |
+ | This is "nn::swkbd::Preset". | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
− | ! | + | ! Value || Description |
|- | |- | ||
− | | | + | | 0 || Default |
|- | |- | ||
− | | | + | | 1 || Password |
+ | |- | ||
+ | | 2 || UserName | ||
+ | |- | ||
+ | | 3 || DownloadCode | ||
|} | |} | ||
− | == | + | = MiniaturizationMode = |
+ | This is "nn::swkbd::MiniaturizationMode". | ||
− | + | {| class="wikitable" border="1" | |
+ | |- | ||
+ | ! Value || Description | ||
+ | |- | ||
+ | | 0 || None | ||
+ | |- | ||
+ | | 1 || Auto | ||
+ | |- | ||
+ | | 2 || Forced | ||
+ | |} | ||
+ | |||
+ | = InvalidCharFlag = | ||
+ | This is "nn::swkbd::InvalidCharFlag". | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
− | ! | + | ! Bits || Description |
|- | |- | ||
− | | | + | | 1 || Space |
|- | |- | ||
− | | | + | | 2 || AtMark |
+ | |- | ||
+ | | 3 || Percent | ||
+ | |- | ||
+ | | 4 || Slash | ||
+ | |- | ||
+ | | 5 || BackSlash | ||
+ | |- | ||
+ | | 6 || Numeric | ||
+ | |- | ||
+ | | 7 || OutsideOfDownloadCode | ||
|- | |- | ||
+ | | 8 || OutsideOfMiiNickName | ||
|} | |} | ||
− | + | = InvalidButtonFlag = | |
+ | This is "nn::swkbd::InvalidButtonFlag". | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
− | ! | + | ! Bits || Description |
+ | |- | ||
+ | | 1 || AnalogStickL | ||
|- | |- | ||
− | | | + | | 2 || AnalogStickR |
|- | |- | ||
− | | | + | | 3 || ZL |
|- | |- | ||
+ | | 4 || ZR | ||
|} | |} | ||
− | == | + | = UserWord = |
+ | This is "nn::swkbd::UserWord". | ||
+ | |||
+ | {| class="wikitable" border="1" | ||
+ | |- | ||
+ | ! Offset || Size || Description | ||
+ | |- | ||
+ | | 0x0 || 0x32 || Reading | ||
+ | |- | ||
+ | | 0x32 || 0x32 || Word | ||
+ | |} | ||
− | + | = Rect = | |
+ | This is "nn::swkbd::Rect". This is a 0x8-byte struct. | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
− | ! Offset || Size || | + | ! Offset || Size || Description |
+ | |- | ||
+ | | 0x0 || 0x2 || (s16) Left | ||
|- | |- | ||
− | | | + | | 0x2 || 0x2 || (s16) Top |
|- | |- | ||
− | | 0x4 || | + | | 0x4 || 0x2 || (s16) Width |
|- | |- | ||
+ | | 0x6 || 0x2 || (s16) Height | ||
|} | |} | ||
[[Category:Library Applets]] | [[Category:Library Applets]] |
Latest revision as of 17:45, 9 May 2021
The software keyboard (swkbd) expects to be passed three IStorages. See also AM_services#Library_Applets.
The below is for normal swkbd usage, see the #InlineKeyboard section for InlineKeyboard.
With version 0x6000B+ after pushing all other storage: when #CustomizedDictionarySet was setup where buffer addr/size is set and total_entries is non-zero, Applet_Manager_services#CreateHandleStorage will be used to create TransferMemory storage which is then pushed.
Library Applet Versions
System Version | Value |
---|---|
0x0..0x1 | |
0x2 | |
0x3..4 | |
[1.0.0+] | 0x5 |
[2.0.0+] | 0x10006 |
[3.0.0+] | 0x30007 |
[4.0.0+] | 0x40008 |
[5.0.0+] | 0x50009 |
[6.0.0+] | 0x6000B |
[8.0.0+] | 0x8000D |
KeyboardConfig
The second IStorage passed to this applet should contain the configuration for the keyboard.
Offset | Size | Description |
---|---|---|
0x0 | 0x4 | #KeyboardMode |
0x4 | 0x12 | OkText |
0x16 | 0x2 | LeftOptionalSymbolKey |
0x18 | 0x2 | RightOptionalSymbolKey |
0x1A | 0x1 | IsPredictionEnabled |
0x1C | 0x4 | #InvalidCharFlag |
0x20 | 0x4 | #InitialCursorPos |
0x24 | 0x82 | HeaderText |
0xA6 | 0x102 | SubText |
0x1A8 | 0x202 | GuideText |
0x3AC | 0x4 | TextMaxLength |
0x3B0 | 0x4 | TextMinLength |
0x3B4 | 0x4 | #PasswordMode |
0x3B8 | 0x4 | #InputFormMode |
0x3BC | 0x1 | IsUseNewLine |
0x3BD | 0x1 | IsUseUtf8 |
0x3BE | 0x1 | IsUseBlurBackground |
0x3C0 | 0x4 | InitialStringOffset |
0x3C4 | 0x4 | InitialStringLength |
0x3C8 | 0x4 | UserDictionaryOffset |
0x3CC | 0x4 | UserDictionaryNum |
0x3D0 | 0x1 | IsUseTextCheck |
0x3D1 | 0x7 | Reserved |
0x3D8 | 0x8 | #TextCheckCallback |
0x3E0 | 0x20 | SeparateTextPos |
Version 0x6000B+:
Offset | Size | Description |
---|---|---|
0x3D4 | 0x20 | SeparateTextPos |
0x3F4 | 0xC0 | CustomizedDicInfoList |
0x4B4 | 0x1 | CustomizedDicCount |
0x4B5 | 0x1 | [8.0.0+] IsCancelButtonDisabled |
0x4B6 | 0xD | Reserved |
0x4C3 | 0x1 | [8.0.0+] Trigger |
0x4C4 | 0x4 | Reserved |
Struct sizes:
- Initial version: 0x3E0-bytes.
- Version 0x30007+: 0x400-bytes.
- Version 0x6000B+: 0x4C8-bytes.
Each entry in the user dictionary is 100 bytes long.
TextMaxLength: When the input is too long, swkbd will stop accepting more input until text is deleted via the B button (Backspace).
WorkBuffer
This is the third IStorage passed to this applet. It is a transfer memory storage. The transfer memory should have size 0x1000 (0xd000 in certain cases) and permissions 0.
The layout of the work buffer doesn't seem to matter as long as the offsets in the #KeyboardConfig are adjusted, but official code lays it out like this.
Prior to version 0x5, offset 0x0 size 0x14 is a header (size 0x10 for version <=0x2).
Offset | Size | Description |
---|---|---|
0x14 | Unknown | UTF-16 initial string |
0x7E8 | Unknown | User dictionary |
TextCheckCallback
If text checking is enabled in #KeyboardConfig, text will be checked when the submit button is pressed. First, swkbd sends the text via PushInteractiveOutData. Normally size 0x7D4 is allocated for the string.
Offset | Size | Description |
---|---|---|
0x0 | 0x8 | Buffer size |
0x8 | Variable | UTF-16 text |
The application then has an opportunity to validate or reject the text. It creates a new IStorage, writes the response to it, and sends it via PushInteractiveInData. This storage is 0x7D8-bytes.
Offset | Size | Description |
---|---|---|
0x0 | 0x4 | #TextCheckResult |
0x4 | Variable | UTF-16 error message (shown in a dialog box) |
Output
When either the submit button is pressed and input has been validated, or the user cancels the text entry, swkbd will push its response and exit. The response IStorage has the following format. This storage is 0x7D8-bytes.
Offset | Size | Description |
---|---|---|
0x0 | 0x4 | Result code (0 = OK, 1 = Cancel) |
0x4 | Variable | UTF-16 text |
InlineKeyboard
This doesn't run in the foreground and has completely different input/output IStorages. This is essentially an asynchronous version of the regular swkbd. Whether it displays on the screen is controlled by the user-process. The user-process can also get the gfx data via Display_services. InlineKeyboard was added with 2.0.0, however it wasn't added to sdk-nso until the version corresponding to sysver 3.x (even though 2.0.0 system titles use it).
InitializeArg
Offset | Size | Description |
---|---|---|
0x0 | 0x4 | Unknown, normally 0. |
0x4 | 0x1 | Controls the LibraryAppletMode when launching the applet. Non-zero indicates LibraryAppletMode=0x1, otherwise LibraryAppletMode=0x3. |
0x5 | 0x1 | Set to 0x1 with [5.0.0+] in a separate init func by official sw, originally set to value 0. |
0x6 | 0x2 | Padding |
AppearArg
Offset | Size | Description |
---|---|---|
0x0 | 0x4 | #KeyboardMode |
0x4 | 0x12 | OkText |
0x16 | 2 | LeftOptionalSymbolKey |
0x18 | 2 | RightOptionalSymbolKey |
0x1A | 0x1 | IsPredictionEnabled |
0x1B | 0x1 | IsCancelButtonDisabled |
0x1C | 0x4 | #InvalidCharFlag |
0x20 | 0x4 | TextMaxLength |
0x24 | 0x4 | TextMinLength |
0x28 | 0x1 | IsUseNewLine |
0x29 | 0x1 | [10.0.0+] #MiniaturizationMode |
0x2A | 0x1 | Reserved |
0x2B | 0x1 | Reserved |
0x2C | 0x4 | [4.0.0+] #InvalidButtonFlag |
0x30 | 0x1 | IsUseSaveData |
0x31 | 0x7 | Reserved |
0x38 | 0x10 | Uid |
0x48 | 0x8 | StartSamplingNumber |
0x50 | 0x20 | Reserved |
The above struct is cleared to 0 during initialization, besides the fields specified otherwise.
[6.0.0+] Flags bitmask 0x10000 is set when #CalcArg trigger is set.
CalcArg
Offset | Size | Flags bitmask | Description |
---|---|---|---|
0x0 | 0x4 | Set to 0x30000. | |
0x4 | 0x2 | Size of this struct. | |
0x6 | 0x1 | Unknown, set to value 0x1. | |
0x7 | 0x1 | Unknown, set to value 0x1. | |
0x8 | 0x8 | Flags | |
0x10 | 0x8 | 0x1 | #InitializeArg |
0x18 | 0x4 | 0x2 | float volume |
0x1C | 0x4 | 0x10 | s32 cursorPos |
0x20 | 0x48 | #AppearArg | |
0x68 | 0x3F4 | 0x8 | InputText UTF-16 string |
0x45C | 0x1 | 0x20 | utf8Mode |
0x45D | 0x1 | Unknown | |
0x45E | 0x1 | 0x8000 | [5.0.0+] enableBackspace |
0x45F | 0x3 | Unknown | |
0x462 | 0x1 | 0x200 | keytopAsFloating |
0x463 | 0x1 | 0x100 | footerScalable |
0x464 | 0x1 | 0x100 | alphaEnabledInInputMode |
0x465 | 0x1 | 0x100 | inputModeFadeType |
0x466 | 0x1 | 0x200 | disableTouch |
0x467 | 0x1 | 0x800 | disableHardwareKeyboard ([1.0.0-9.2.0] disableUSBKeyboard) |
0x468 | 0x5 | Unknown | |
0x46D | 0x2 | ||
0x46F | 0x1 | ||
0x470 | 0x4 | 0x200 | float keytopScale0 |
0x474 | 0x4 | 0x200 | float keytopScale1 |
0x478 | 0x4 | 0x200 | float keytopTranslate0 |
0x47C | 0x4 | 0x200 | float keytopTranslate1 |
0x480 | 0x4 | 0x100 | float keytopBgAlpha |
0x484 | 0x4 | 0x100 | float footerBgAlpha |
0x488 | 0x4 | 0x200 | float balloonScale |
0x48C | 0x4 | float, unknown | |
0x490 | 0xC | ||
0x49C | 0x1 | Enable=0x2000, disable=0x4000. | [5.0.0+] SeGroup (sound effect) |
0x49D | 0x1 | [6.0.0+] u8 triggerFlag, for #AppearArg. Enables using the trigger field when set, this is only set when trigger is non-zero. | |
0x49E | 0x2 | [6.0.0+] u8 trigger, for #AppearArg. Official sw currently only uses value 0. | |
0x49F | 0x1 | Padding |
This is 0x4A0-bytes.
All floats except for keytopTranslate0/keytopTranslate1 are initialized to value 1.0f.
The applet-specific IStorage for data input is the #InitializeArg within this struct.
disableHardwareKeyboard: On [10.0.0+] there's sdknso user-facing funcs for both Hardware/USB, which call the same Impl func that has Hardware in the name.
Runtime
Once the applet is running, official sw can call a func which does the following:
- Checks whether the applet exited via an event, then handles exit if so and returns.
- Otherwise:
- If the Flags field in the #CalcArg state is non-zero, sends a Calc #Request then clears the Flags field.
- Enters a loop which pops each applet Interactive output IStorage, reads 2 u32s from the #Reply storage and processes the reply.
- The u32 from offset 0x0 from the last processed storage is then returned as the retval.
Request
RequestCommand | Data size | Description |
---|---|---|
0x4 | 0x0 | Finalize |
0x6 | Varies | SetUserWordInfo |
0x7 | 0x70 | SetCustomizeDic |
0xA | 0x4A0 | Calc (data is #CalcArg) |
0xB | 0xD0 | SetCustomizedDictionaries (data is #CustomizedDictionarySet with an additional 2-bytes of padding |
0xC | 0x0 | UnsetCustomizedDictionaries |
0xD | 0x1 | [8.0.0+] Takes an input u8 bool which controls whether ChangedString*V2 or ChangedString* replies should be used. |
0xE | 0x1 | [8.0.0+] Takes an input u8 bool which controls whether MovedCursor*V2 or MovedCursor* replies should be used. |
Requests are sent via an applet Interactive input IStorage: the u32 at offset 0x0 is the RequestCommand, and the rest of the storage is the request-specific data. While swkbd supports other requests, official sw only uses requests 0x4, 0x7, and 0xA.
Reply
ReplyType | Data size | Description |
---|---|---|
0x0 | 0x1 | FinishedInitialize (reply data is ignored by the user-process) |
0x1 (default) | 0x0 | Official sw has no handling for this besides just closing the storage. |
0x2 | 0x3FC | ChangedString |
0x3 | 0x3F4 | MovedCursor |
0x4 | 0x3F4 | MovedTab |
0x5 | 0x3F0 | DecidedEnter |
0x6 | 0x0 | DecidedCancel |
0x7 | 0x7E4 | ChangedStringUtf8 |
0x8 | 0x7DC | MovedCursorUtf8 |
0x9 | 0x7D8 | DecidedEnterUtf8 |
0xA | 0x0 | UnsetCustomizeDic (official sw clears a flag related to CustomizeDic, then runs the same handling code as 0x1/default) |
0xB | 0x0 | ReleasedUserWordInfo |
0xC | 0x0 | [6.0.0+] UnsetCustomizedDictionaries (official sw handles this the same as UnsetCustomizeDic) |
0xD | 0x3FC + 0x1 | [8.0.0+] ChangedStringV2 |
0xE | 0x3F4 + 0x1 | [8.0.0+] MovedCursorV2 |
0xF | 0x7E4 + 0x1 | [8.0.0+] ChangedStringUtf8V2 |
0x10 | 0x7DC + 0x1 | [8.0.0+] MovedCursorUtf8V2 |
See #Runtime. In the storage, the first u32 is the State, while the second u32 is the ReplyType. The rest is the reply-specific data.
The replies with name "*Utf8" contain an UTF-8 string in the reply data, while the other replies contain an UTF-16 string. These are identical besides the string encoding.
Reply data format:
- ChangedString*: +0 = string. Last 0x10-bytes: 4 u32s, where the first one is the length of the string in characters, without NUL-terminator. The last u32 is cursorPos. The other 2 fields are s32s.
- MovedCursor*: +0 = string. Last 0x8-bytes: 2 u32s, where the first one is the stringLen, and the second one is cursorPos.
- DecidedEnter*: +0 = string. The last u32 is the stringLen.
- *V2: See above. Last byte: u8 bool, passed to the callback as
flag==0
.
CustomizedDictionarySet
This is "nn::swkbd::CustomizedDictionarySet". This struct is 0xCE-bytes.
This was added with [6.0.0+].
Offset | Size | Description |
---|---|---|
0x0 | 0x8 | Dictionaries (0x1000-byte aligned buffer address) |
0x8 | 0x4 | DictionariesSize (0x1000-byte aligned buffer size) |
0xC | 0xC0 (0x18*4) | DicInfoList (array of 0x18 entries, where each entry is a #DictionaryInfo). |
0xCC | 0x2 | Count |
DictionaryInfo
This is "nn::swkbd::DictionaryInfo".
Offset | Size | Description |
---|---|---|
0x0 | 0x4 | Offset |
0x4 | 0x2 | Size |
0x6 | 0x2 | #DictionaryLang |
DictionaryLang
This is "nn::swkbd::DictionaryLang".
Value | Description |
---|---|
0 | Japanese |
1 | AmericanEnglish |
2 | CanadianFrench |
3 | LatinAmericanSpanish |
4 | Reserved1 |
5 | BritishEnglish |
6 | French |
7 | German |
8 | Spanish |
9 | Italian |
10 | Dutch |
11 | Portuguese |
12 | Russian |
13 | Reserved2 |
14 | SimplifiedChinesePinyin |
15 | TraditionalChineseCangjie |
16 | TraditionalChineseSimplifiedCangjie |
17 | TraditionalChineseZhuyin |
18 | Korean |
KeyboardMode
This is "nn::swkbd::KeyboardMode".
Value | Description |
---|---|
0 | Full |
1 | Numeric |
2 | ASCII |
3 | FullLatin |
4 | Alphabet |
5 | SimplifiedChinese |
6 | TraditionalChinese |
7 | Korean |
8 | LanguageSet2 |
9 | LanguageSet2Latin |
State
This is "nn::swkbd::State".
Value | Description |
---|---|
0 | None |
1 | Disappear |
2 | InAppear |
3 | Appear |
4 | InDisappear |
5 | SelectKeyTop |
6 | Miniaturized |
CloseResult
This is "nn::swkbd::CloseResult".
Value | Description |
---|---|
0 | Enter |
1 | Cancel |
PasswordMode
This is "nn::swkbd::PasswordMode".
Value | Description |
---|---|
0 | Show |
1 | Hide |
InitialCursorPos
This is "nn::swkbd::InitialCursorPos".
Value | Description |
---|---|
0 | First |
1 | Last |
InputFormMode
This is "nn::swkbd::InputFormMode".
Value | Description |
---|---|
0 | OneLine |
1 | MultiLine |
2 | Separate |
TextCheckResult
This is "nn::swkbd::TextCheckResult".
Value | Description |
---|---|
0 | Success |
1 | ShowFailureDialog |
2 | ShowConfirmDialog |
Preset
This is "nn::swkbd::Preset".
Value | Description |
---|---|
0 | Default |
1 | Password |
2 | UserName |
3 | DownloadCode |
MiniaturizationMode
This is "nn::swkbd::MiniaturizationMode".
Value | Description |
---|---|
0 | None |
1 | Auto |
2 | Forced |
InvalidCharFlag
This is "nn::swkbd::InvalidCharFlag".
Bits | Description |
---|---|
1 | Space |
2 | AtMark |
3 | Percent |
4 | Slash |
5 | BackSlash |
6 | Numeric |
7 | OutsideOfDownloadCode |
8 | OutsideOfMiiNickName |
InvalidButtonFlag
This is "nn::swkbd::InvalidButtonFlag".
Bits | Description |
---|---|
1 | AnalogStickL |
2 | AnalogStickR |
3 | ZL |
4 | ZR |
UserWord
This is "nn::swkbd::UserWord".
Offset | Size | Description |
---|---|---|
0x0 | 0x32 | Reading |
0x32 | 0x32 | Word |
Rect
This is "nn::swkbd::Rect". This is a 0x8-byte struct.
Offset | Size | Description |
---|---|---|
0x0 | 0x2 | (s16) Left |
0x2 | 0x2 | (s16) Top |
0x4 | 0x2 | (s16) Width |
0x6 | 0x2 | (s16) Height |